diff options
| -rw-r--r-- | .clang-format | 49 | ||||
| -rw-r--r-- | .gitignore | 44 | ||||
| -rw-r--r-- | CMakeLists.txt | 232 | ||||
| -rw-r--r-- | README | 28 | ||||
| -rw-r--r-- | clang-tidy.conf | 8 | ||||
| -rw-r--r-- | contrib/catch2/.bazelrc (renamed from .bazelrc) | 0 | ||||
| -rw-r--r-- | contrib/catch2/.clang-format | 45 | ||||
| -rw-r--r-- | contrib/catch2/.clang-tidy (renamed from .clang-tidy) | 0 | ||||
| -rw-r--r-- | contrib/catch2/.conan/build.py (renamed from .conan/build.py) | 0 | ||||
| -rw-r--r-- | contrib/catch2/.conan/test_package/CMakeLists.txt (renamed from .conan/test_package/CMakeLists.txt) | 0 | ||||
| -rw-r--r-- | contrib/catch2/.conan/test_package/conanfile.py (renamed from .conan/test_package/conanfile.py) | 0 | ||||
| -rw-r--r-- | contrib/catch2/.conan/test_package/test_package.cpp (renamed from .conan/test_package/test_package.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/.gitattributes (renamed from .gitattributes) | 0 | ||||
| -rw-r--r-- | contrib/catch2/.github/FUNDING.yml (renamed from .github/FUNDING.yml) | 0 | ||||
| -rw-r--r-- | contrib/catch2/.github/ISSUE_TEMPLATE/bug_report.md (renamed from .github/ISSUE_TEMPLATE/bug_report.md) | 0 | ||||
| -rw-r--r-- | contrib/catch2/.github/ISSUE_TEMPLATE/feature_request.md (renamed from .github/ISSUE_TEMPLATE/feature_request.md) | 0 | ||||
| -rw-r--r-- | contrib/catch2/.github/pull_request_template.md (renamed from .github/pull_request_template.md) | 0 | ||||
| -rw-r--r-- | contrib/catch2/.github/workflows/linux-bazel-builds.yml (renamed from .github/workflows/linux-bazel-builds.yml) | 0 | ||||
| -rw-r--r-- | contrib/catch2/.github/workflows/linux-meson-builds.yml (renamed from .github/workflows/linux-meson-builds.yml) | 0 | ||||
| -rw-r--r-- | contrib/catch2/.github/workflows/linux-other-builds.yml (renamed from .github/workflows/linux-other-builds.yml) | 0 | ||||
| -rw-r--r-- | contrib/catch2/.github/workflows/linux-simple-builds.yml (renamed from .github/workflows/linux-simple-builds.yml) | 0 | ||||
| -rw-r--r-- | contrib/catch2/.github/workflows/mac-builds-m1.yml (renamed from .github/workflows/mac-builds-m1.yml) | 0 | ||||
| -rw-r--r-- | contrib/catch2/.github/workflows/mac-builds.yml (renamed from .github/workflows/mac-builds.yml) | 0 | ||||
| -rw-r--r-- | contrib/catch2/.github/workflows/package-manager-builds.yaml (renamed from .github/workflows/package-manager-builds.yaml) | 0 | ||||
| -rw-r--r-- | contrib/catch2/.github/workflows/validate-header-guards.yml (renamed from .github/workflows/validate-header-guards.yml) | 0 | ||||
| -rw-r--r-- | contrib/catch2/.github/workflows/windows-simple-builds.yml (renamed from .github/workflows/windows-simple-builds.yml) | 0 | ||||
| -rw-r--r-- | contrib/catch2/.gitignore | 40 | ||||
| -rw-r--r-- | contrib/catch2/BUILD.bazel (renamed from BUILD.bazel) | 0 | ||||
| -rw-r--r-- | contrib/catch2/CMake/Catch2Config.cmake.in (renamed from CMake/Catch2Config.cmake.in) | 0 | ||||
| -rw-r--r-- | contrib/catch2/CMake/CatchConfigOptions.cmake (renamed from CMake/CatchConfigOptions.cmake) | 0 | ||||
| -rw-r--r-- | contrib/catch2/CMake/CatchMiscFunctions.cmake (renamed from CMake/CatchMiscFunctions.cmake) | 0 | ||||
| -rw-r--r-- | contrib/catch2/CMake/FindGcov.cmake (renamed from CMake/FindGcov.cmake) | 0 | ||||
| -rw-r--r-- | contrib/catch2/CMake/FindLcov.cmake (renamed from CMake/FindLcov.cmake) | 0 | ||||
| -rw-r--r-- | contrib/catch2/CMake/Findcodecov.cmake (renamed from CMake/Findcodecov.cmake) | 0 | ||||
| -rw-r--r-- | contrib/catch2/CMake/catch2-with-main.pc.in (renamed from CMake/catch2-with-main.pc.in) | 0 | ||||
| -rw-r--r-- | contrib/catch2/CMake/catch2.pc.in (renamed from CMake/catch2.pc.in) | 0 | ||||
| -rwxr-xr-x | contrib/catch2/CMake/llvm-cov-wrapper (renamed from CMake/llvm-cov-wrapper) | 0 | ||||
| -rw-r--r-- | contrib/catch2/CMakeLists.txt | 198 | ||||
| -rw-r--r-- | contrib/catch2/CMakePresets.json (renamed from CMakePresets.json) | 0 | ||||
| -rw-r--r-- | contrib/catch2/CODE_OF_CONDUCT.md (renamed from CODE_OF_CONDUCT.md) | 0 | ||||
| -rw-r--r-- | contrib/catch2/Doxyfile (renamed from Doxyfile) | 0 | ||||
| -rw-r--r-- | contrib/catch2/LICENSE.txt (renamed from LICENSE.txt) | 0 | ||||
| -rw-r--r-- | contrib/catch2/MODULE.bazel (renamed from MODULE.bazel) | 0 | ||||
| -rw-r--r-- | contrib/catch2/README.md (renamed from README.md) | 0 | ||||
| -rw-r--r-- | contrib/catch2/SECURITY.md (renamed from SECURITY.md) | 0 | ||||
| -rw-r--r-- | contrib/catch2/appveyor.yml (renamed from appveyor.yml) | 0 | ||||
| -rw-r--r-- | contrib/catch2/codecov.yml (renamed from codecov.yml) | 0 | ||||
| -rwxr-xr-x | contrib/catch2/conanfile.py (renamed from conanfile.py) | 0 | ||||
| -rw-r--r-- | contrib/catch2/data/artwork/catch2-c-logo.png (renamed from data/artwork/catch2-c-logo.png) | bin | 10636 -> 10636 bytes | |||
| -rw-r--r-- | contrib/catch2/data/artwork/catch2-hand-logo.png (renamed from data/artwork/catch2-hand-logo.png) | bin | 33761 -> 33761 bytes | |||
| -rw-r--r-- | contrib/catch2/data/artwork/catch2-logo-small-with-background.png (renamed from data/artwork/catch2-logo-small-with-background.png) | bin | 25330 -> 25330 bytes | |||
| -rw-r--r-- | contrib/catch2/data/artwork/catch2-logo-small.png (renamed from data/artwork/catch2-logo-small.png) | bin | 20939 -> 20939 bytes | |||
| -rw-r--r-- | contrib/catch2/docs/Readme.md (renamed from docs/Readme.md) | 0 | ||||
| -rw-r--r-- | contrib/catch2/docs/assertions.md (renamed from docs/assertions.md) | 0 | ||||
| -rw-r--r-- | contrib/catch2/docs/benchmarks.md (renamed from docs/benchmarks.md) | 0 | ||||
| -rw-r--r-- | contrib/catch2/docs/ci-and-misc.md (renamed from docs/ci-and-misc.md) | 0 | ||||
| -rw-r--r-- | contrib/catch2/docs/cmake-integration.md (renamed from docs/cmake-integration.md) | 0 | ||||
| -rw-r--r-- | contrib/catch2/docs/command-line.md (renamed from docs/command-line.md) | 0 | ||||
| -rw-r--r-- | contrib/catch2/docs/commercial-users.md (renamed from docs/commercial-users.md) | 0 | ||||
| -rw-r--r-- | contrib/catch2/docs/comparing-floating-point-numbers.md (renamed from docs/comparing-floating-point-numbers.md) | 0 | ||||
| -rw-r--r-- | contrib/catch2/docs/configuration.md (renamed from docs/configuration.md) | 0 | ||||
| -rw-r--r-- | contrib/catch2/docs/contributing.md (renamed from docs/contributing.md) | 0 | ||||
| -rw-r--r-- | contrib/catch2/docs/deprecations.md (renamed from docs/deprecations.md) | 0 | ||||
| -rw-r--r-- | contrib/catch2/docs/event-listeners.md (renamed from docs/event-listeners.md) | 0 | ||||
| -rw-r--r-- | contrib/catch2/docs/faq.md (renamed from docs/faq.md) | 0 | ||||
| -rw-r--r-- | contrib/catch2/docs/generators.md (renamed from docs/generators.md) | 0 | ||||
| -rw-r--r-- | contrib/catch2/docs/limitations.md (renamed from docs/limitations.md) | 0 | ||||
| -rw-r--r-- | contrib/catch2/docs/list-of-examples.md (renamed from docs/list-of-examples.md) | 0 | ||||
| -rw-r--r-- | contrib/catch2/docs/logging.md (renamed from docs/logging.md) | 0 | ||||
| -rw-r--r-- | contrib/catch2/docs/matchers.md (renamed from docs/matchers.md) | 0 | ||||
| -rw-r--r-- | contrib/catch2/docs/migrate-v2-to-v3.md (renamed from docs/migrate-v2-to-v3.md) | 0 | ||||
| -rw-r--r-- | contrib/catch2/docs/opensource-users.md (renamed from docs/opensource-users.md) | 0 | ||||
| -rw-r--r-- | contrib/catch2/docs/other-macros.md (renamed from docs/other-macros.md) | 0 | ||||
| -rw-r--r-- | contrib/catch2/docs/own-main.md (renamed from docs/own-main.md) | 0 | ||||
| -rw-r--r-- | contrib/catch2/docs/release-notes.md (renamed from docs/release-notes.md) | 0 | ||||
| -rw-r--r-- | contrib/catch2/docs/release-process.md (renamed from docs/release-process.md) | 0 | ||||
| -rw-r--r-- | contrib/catch2/docs/reporter-events.md (renamed from docs/reporter-events.md) | 0 | ||||
| -rw-r--r-- | contrib/catch2/docs/reporters.md (renamed from docs/reporters.md) | 0 | ||||
| -rw-r--r-- | contrib/catch2/docs/skipping-passing-failing.md (renamed from docs/skipping-passing-failing.md) | 0 | ||||
| -rw-r--r-- | contrib/catch2/docs/test-cases-and-sections.md (renamed from docs/test-cases-and-sections.md) | 0 | ||||
| -rw-r--r-- | contrib/catch2/docs/test-fixtures.md (renamed from docs/test-fixtures.md) | 0 | ||||
| -rw-r--r-- | contrib/catch2/docs/tostring.md (renamed from docs/tostring.md) | 0 | ||||
| -rw-r--r-- | contrib/catch2/docs/tutorial.md (renamed from docs/tutorial.md) | 0 | ||||
| -rw-r--r-- | contrib/catch2/docs/usage-tips.md (renamed from docs/usage-tips.md) | 0 | ||||
| -rw-r--r-- | contrib/catch2/docs/why-catch.md (renamed from docs/why-catch.md) | 0 | ||||
| -rw-r--r-- | contrib/catch2/examples/010-TestCase.cpp (renamed from examples/010-TestCase.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/examples/020-TestCase-1.cpp (renamed from examples/020-TestCase-1.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/examples/020-TestCase-2.cpp (renamed from examples/020-TestCase-2.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/examples/030-Asn-Require-Check.cpp (renamed from examples/030-Asn-Require-Check.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/examples/100-Fix-Section.cpp (renamed from examples/100-Fix-Section.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/examples/110-Fix-ClassFixture.cpp (renamed from examples/110-Fix-ClassFixture.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/examples/111-Fix-PersistentFixture.cpp (renamed from examples/111-Fix-PersistentFixture.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/examples/120-Bdd-ScenarioGivenWhenThen.cpp (renamed from examples/120-Bdd-ScenarioGivenWhenThen.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/examples/210-Evt-EventListeners.cpp (renamed from examples/210-Evt-EventListeners.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/examples/231-Cfg-OutputStreams.cpp (renamed from examples/231-Cfg-OutputStreams.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/examples/232-Cfg-CustomMain.cpp (renamed from examples/232-Cfg-CustomMain.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/examples/300-Gen-OwnGenerator.cpp (renamed from examples/300-Gen-OwnGenerator.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/examples/301-Gen-MapTypeConversion.cpp (renamed from examples/301-Gen-MapTypeConversion.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/examples/302-Gen-Table.cpp (renamed from examples/302-Gen-Table.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/examples/310-Gen-VariablesInGenerators.cpp (renamed from examples/310-Gen-VariablesInGenerators.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/examples/311-Gen-CustomCapture.cpp (renamed from examples/311-Gen-CustomCapture.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/examples/CMakeLists.txt (renamed from examples/CMakeLists.txt) | 0 | ||||
| -rw-r--r-- | contrib/catch2/extras/Catch.cmake (renamed from extras/Catch.cmake) | 0 | ||||
| -rw-r--r-- | contrib/catch2/extras/CatchAddTests.cmake (renamed from extras/CatchAddTests.cmake) | 0 | ||||
| -rw-r--r-- | contrib/catch2/extras/CatchShardTests.cmake (renamed from extras/CatchShardTests.cmake) | 0 | ||||
| -rw-r--r-- | contrib/catch2/extras/CatchShardTestsImpl.cmake (renamed from extras/CatchShardTestsImpl.cmake) | 0 | ||||
| -rw-r--r-- | contrib/catch2/extras/ParseAndAddCatchTests.cmake (renamed from extras/ParseAndAddCatchTests.cmake) | 0 | ||||
| -rw-r--r-- | contrib/catch2/extras/catch_amalgamated.cpp (renamed from extras/catch_amalgamated.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/extras/catch_amalgamated.hpp (renamed from extras/catch_amalgamated.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/extras/gdbinit (renamed from extras/gdbinit) | 0 | ||||
| -rw-r--r-- | contrib/catch2/extras/lldbinit (renamed from extras/lldbinit) | 0 | ||||
| -rw-r--r-- | contrib/catch2/fuzzing/CMakeLists.txt (renamed from fuzzing/CMakeLists.txt) | 0 | ||||
| -rw-r--r-- | contrib/catch2/fuzzing/NullOStream.cpp (renamed from fuzzing/NullOStream.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/fuzzing/NullOStream.h (renamed from fuzzing/NullOStream.h) | 0 | ||||
| -rwxr-xr-x | contrib/catch2/fuzzing/build_fuzzers.sh (renamed from fuzzing/build_fuzzers.sh) | 0 | ||||
| -rw-r--r-- | contrib/catch2/fuzzing/fuzz_TestSpecParser.cpp (renamed from fuzzing/fuzz_TestSpecParser.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/fuzzing/fuzz_XmlWriter.cpp (renamed from fuzzing/fuzz_XmlWriter.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/fuzzing/fuzz_textflow.cpp (renamed from fuzzing/fuzz_textflow.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/mdsnippets.json (renamed from mdsnippets.json) | 0 | ||||
| -rw-r--r-- | contrib/catch2/meson.build (renamed from meson.build) | 0 | ||||
| -rw-r--r-- | contrib/catch2/meson_options.txt (renamed from meson_options.txt) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/CMakeLists.txt (renamed from src/CMakeLists.txt) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/benchmark/catch_benchmark.hpp (renamed from src/catch2/benchmark/catch_benchmark.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/benchmark/catch_benchmark_all.hpp (renamed from src/catch2/benchmark/catch_benchmark_all.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/benchmark/catch_chronometer.cpp (renamed from src/catch2/benchmark/catch_chronometer.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/benchmark/catch_chronometer.hpp (renamed from src/catch2/benchmark/catch_chronometer.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/benchmark/catch_clock.hpp (renamed from src/catch2/benchmark/catch_clock.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/benchmark/catch_constructor.hpp (renamed from src/catch2/benchmark/catch_constructor.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/benchmark/catch_environment.hpp (renamed from src/catch2/benchmark/catch_environment.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/benchmark/catch_estimate.hpp (renamed from src/catch2/benchmark/catch_estimate.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/benchmark/catch_execution_plan.hpp (renamed from src/catch2/benchmark/catch_execution_plan.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/benchmark/catch_optimizer.hpp (renamed from src/catch2/benchmark/catch_optimizer.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/benchmark/catch_outlier_classification.hpp (renamed from src/catch2/benchmark/catch_outlier_classification.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/benchmark/catch_sample_analysis.hpp (renamed from src/catch2/benchmark/catch_sample_analysis.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/benchmark/detail/catch_analyse.cpp (renamed from src/catch2/benchmark/detail/catch_analyse.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/benchmark/detail/catch_analyse.hpp (renamed from src/catch2/benchmark/detail/catch_analyse.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/benchmark/detail/catch_benchmark_function.cpp (renamed from src/catch2/benchmark/detail/catch_benchmark_function.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/benchmark/detail/catch_benchmark_function.hpp (renamed from src/catch2/benchmark/detail/catch_benchmark_function.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/benchmark/detail/catch_benchmark_stats.hpp (renamed from src/catch2/benchmark/detail/catch_benchmark_stats.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/benchmark/detail/catch_benchmark_stats_fwd.hpp (renamed from src/catch2/benchmark/detail/catch_benchmark_stats_fwd.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/benchmark/detail/catch_complete_invoke.hpp (renamed from src/catch2/benchmark/detail/catch_complete_invoke.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/benchmark/detail/catch_estimate_clock.hpp (renamed from src/catch2/benchmark/detail/catch_estimate_clock.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/benchmark/detail/catch_measure.hpp (renamed from src/catch2/benchmark/detail/catch_measure.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/benchmark/detail/catch_repeat.hpp (renamed from src/catch2/benchmark/detail/catch_repeat.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/benchmark/detail/catch_run_for_at_least.cpp (renamed from src/catch2/benchmark/detail/catch_run_for_at_least.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/benchmark/detail/catch_run_for_at_least.hpp (renamed from src/catch2/benchmark/detail/catch_run_for_at_least.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/benchmark/detail/catch_stats.cpp (renamed from src/catch2/benchmark/detail/catch_stats.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/benchmark/detail/catch_stats.hpp (renamed from src/catch2/benchmark/detail/catch_stats.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/benchmark/detail/catch_timing.hpp (renamed from src/catch2/benchmark/detail/catch_timing.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/catch_all.hpp (renamed from src/catch2/catch_all.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/catch_approx.cpp (renamed from src/catch2/catch_approx.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/catch_approx.hpp (renamed from src/catch2/catch_approx.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/catch_assertion_info.hpp (renamed from src/catch2/catch_assertion_info.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/catch_assertion_result.cpp (renamed from src/catch2/catch_assertion_result.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/catch_assertion_result.hpp (renamed from src/catch2/catch_assertion_result.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/catch_case_sensitive.hpp (renamed from src/catch2/catch_case_sensitive.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/catch_config.cpp (renamed from src/catch2/catch_config.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/catch_config.hpp (renamed from src/catch2/catch_config.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/catch_get_random_seed.cpp (renamed from src/catch2/catch_get_random_seed.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/catch_get_random_seed.hpp (renamed from src/catch2/catch_get_random_seed.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/catch_message.cpp (renamed from src/catch2/catch_message.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/catch_message.hpp (renamed from src/catch2/catch_message.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/catch_registry_hub.cpp (renamed from src/catch2/catch_registry_hub.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/catch_section_info.hpp (renamed from src/catch2/catch_section_info.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/catch_session.cpp (renamed from src/catch2/catch_session.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/catch_session.hpp (renamed from src/catch2/catch_session.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/catch_tag_alias.hpp (renamed from src/catch2/catch_tag_alias.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/catch_tag_alias_autoregistrar.cpp (renamed from src/catch2/catch_tag_alias_autoregistrar.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/catch_tag_alias_autoregistrar.hpp (renamed from src/catch2/catch_tag_alias_autoregistrar.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/catch_template_test_macros.hpp (renamed from src/catch2/catch_template_test_macros.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/catch_test_case_info.cpp (renamed from src/catch2/catch_test_case_info.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/catch_test_case_info.hpp (renamed from src/catch2/catch_test_case_info.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/catch_test_macros.hpp (renamed from src/catch2/catch_test_macros.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/catch_test_spec.cpp (renamed from src/catch2/catch_test_spec.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/catch_test_spec.hpp (renamed from src/catch2/catch_test_spec.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/catch_timer.cpp (renamed from src/catch2/catch_timer.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/catch_timer.hpp (renamed from src/catch2/catch_timer.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/catch_tostring.cpp (renamed from src/catch2/catch_tostring.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/catch_tostring.hpp (renamed from src/catch2/catch_tostring.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/catch_totals.cpp (renamed from src/catch2/catch_totals.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/catch_totals.hpp (renamed from src/catch2/catch_totals.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/catch_translate_exception.cpp (renamed from src/catch2/catch_translate_exception.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/catch_translate_exception.hpp (renamed from src/catch2/catch_translate_exception.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/catch_user_config.hpp.in (renamed from src/catch2/catch_user_config.hpp.in) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/catch_version.cpp (renamed from src/catch2/catch_version.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/catch_version.hpp (renamed from src/catch2/catch_version.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/catch_version_macros.hpp (renamed from src/catch2/catch_version_macros.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/generators/catch_generator_exception.cpp (renamed from src/catch2/generators/catch_generator_exception.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/generators/catch_generator_exception.hpp (renamed from src/catch2/generators/catch_generator_exception.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/generators/catch_generators.cpp (renamed from src/catch2/generators/catch_generators.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/generators/catch_generators.hpp (renamed from src/catch2/generators/catch_generators.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/generators/catch_generators_adapters.hpp (renamed from src/catch2/generators/catch_generators_adapters.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/generators/catch_generators_all.hpp (renamed from src/catch2/generators/catch_generators_all.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/generators/catch_generators_random.cpp (renamed from src/catch2/generators/catch_generators_random.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/generators/catch_generators_random.hpp (renamed from src/catch2/generators/catch_generators_random.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/generators/catch_generators_range.hpp (renamed from src/catch2/generators/catch_generators_range.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/interfaces/catch_interfaces_all.hpp (renamed from src/catch2/interfaces/catch_interfaces_all.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/interfaces/catch_interfaces_capture.cpp (renamed from src/catch2/interfaces/catch_interfaces_capture.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/interfaces/catch_interfaces_capture.hpp (renamed from src/catch2/interfaces/catch_interfaces_capture.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/interfaces/catch_interfaces_config.cpp (renamed from src/catch2/interfaces/catch_interfaces_config.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/interfaces/catch_interfaces_config.hpp (renamed from src/catch2/interfaces/catch_interfaces_config.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/interfaces/catch_interfaces_enum_values_registry.hpp (renamed from src/catch2/interfaces/catch_interfaces_enum_values_registry.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/interfaces/catch_interfaces_exception.cpp (renamed from src/catch2/interfaces/catch_interfaces_exception.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/interfaces/catch_interfaces_exception.hpp (renamed from src/catch2/interfaces/catch_interfaces_exception.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/interfaces/catch_interfaces_generatortracker.cpp (renamed from src/catch2/interfaces/catch_interfaces_generatortracker.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/interfaces/catch_interfaces_generatortracker.hpp (renamed from src/catch2/interfaces/catch_interfaces_generatortracker.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/interfaces/catch_interfaces_registry_hub.cpp (renamed from src/catch2/interfaces/catch_interfaces_registry_hub.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/interfaces/catch_interfaces_registry_hub.hpp (renamed from src/catch2/interfaces/catch_interfaces_registry_hub.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/interfaces/catch_interfaces_reporter.cpp (renamed from src/catch2/interfaces/catch_interfaces_reporter.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/interfaces/catch_interfaces_reporter.hpp (renamed from src/catch2/interfaces/catch_interfaces_reporter.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/interfaces/catch_interfaces_reporter_factory.cpp (renamed from src/catch2/interfaces/catch_interfaces_reporter_factory.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/interfaces/catch_interfaces_reporter_factory.hpp (renamed from src/catch2/interfaces/catch_interfaces_reporter_factory.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/interfaces/catch_interfaces_tag_alias_registry.hpp (renamed from src/catch2/interfaces/catch_interfaces_tag_alias_registry.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/interfaces/catch_interfaces_test_invoker.hpp (renamed from src/catch2/interfaces/catch_interfaces_test_invoker.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/interfaces/catch_interfaces_testcase.cpp (renamed from src/catch2/interfaces/catch_interfaces_testcase.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/interfaces/catch_interfaces_testcase.hpp (renamed from src/catch2/interfaces/catch_interfaces_testcase.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_assertion_handler.cpp (renamed from src/catch2/internal/catch_assertion_handler.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_assertion_handler.hpp (renamed from src/catch2/internal/catch_assertion_handler.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_case_insensitive_comparisons.cpp (renamed from src/catch2/internal/catch_case_insensitive_comparisons.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_case_insensitive_comparisons.hpp (renamed from src/catch2/internal/catch_case_insensitive_comparisons.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_clara.cpp (renamed from src/catch2/internal/catch_clara.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_clara.hpp (renamed from src/catch2/internal/catch_clara.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_commandline.cpp (renamed from src/catch2/internal/catch_commandline.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_commandline.hpp (renamed from src/catch2/internal/catch_commandline.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_compare_traits.hpp (renamed from src/catch2/internal/catch_compare_traits.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_compiler_capabilities.hpp (renamed from src/catch2/internal/catch_compiler_capabilities.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_config_android_logwrite.hpp (renamed from src/catch2/internal/catch_config_android_logwrite.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_config_counter.hpp (renamed from src/catch2/internal/catch_config_counter.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_config_prefix_messages.hpp (renamed from src/catch2/internal/catch_config_prefix_messages.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_config_static_analysis_support.hpp (renamed from src/catch2/internal/catch_config_static_analysis_support.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_config_uncaught_exceptions.hpp (renamed from src/catch2/internal/catch_config_uncaught_exceptions.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_config_wchar.hpp (renamed from src/catch2/internal/catch_config_wchar.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_console_colour.cpp (renamed from src/catch2/internal/catch_console_colour.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_console_colour.hpp (renamed from src/catch2/internal/catch_console_colour.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_console_width.hpp (renamed from src/catch2/internal/catch_console_width.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_container_nonmembers.hpp (renamed from src/catch2/internal/catch_container_nonmembers.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_context.cpp (renamed from src/catch2/internal/catch_context.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_context.hpp (renamed from src/catch2/internal/catch_context.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_debug_console.cpp (renamed from src/catch2/internal/catch_debug_console.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_debug_console.hpp (renamed from src/catch2/internal/catch_debug_console.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_debugger.cpp (renamed from src/catch2/internal/catch_debugger.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_debugger.hpp (renamed from src/catch2/internal/catch_debugger.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_decomposer.cpp (renamed from src/catch2/internal/catch_decomposer.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_decomposer.hpp (renamed from src/catch2/internal/catch_decomposer.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_enforce.cpp (renamed from src/catch2/internal/catch_enforce.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_enforce.hpp (renamed from src/catch2/internal/catch_enforce.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_enum_values_registry.cpp (renamed from src/catch2/internal/catch_enum_values_registry.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_enum_values_registry.hpp (renamed from src/catch2/internal/catch_enum_values_registry.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_errno_guard.cpp (renamed from src/catch2/internal/catch_errno_guard.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_errno_guard.hpp (renamed from src/catch2/internal/catch_errno_guard.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_exception_translator_registry.cpp (renamed from src/catch2/internal/catch_exception_translator_registry.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_exception_translator_registry.hpp (renamed from src/catch2/internal/catch_exception_translator_registry.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_fatal_condition_handler.cpp (renamed from src/catch2/internal/catch_fatal_condition_handler.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_fatal_condition_handler.hpp (renamed from src/catch2/internal/catch_fatal_condition_handler.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_floating_point_helpers.cpp (renamed from src/catch2/internal/catch_floating_point_helpers.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_floating_point_helpers.hpp (renamed from src/catch2/internal/catch_floating_point_helpers.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_getenv.cpp (renamed from src/catch2/internal/catch_getenv.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_getenv.hpp (renamed from src/catch2/internal/catch_getenv.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_is_permutation.hpp (renamed from src/catch2/internal/catch_is_permutation.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_istream.cpp (renamed from src/catch2/internal/catch_istream.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_istream.hpp (renamed from src/catch2/internal/catch_istream.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_jsonwriter.cpp (renamed from src/catch2/internal/catch_jsonwriter.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_jsonwriter.hpp (renamed from src/catch2/internal/catch_jsonwriter.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_lazy_expr.cpp (renamed from src/catch2/internal/catch_lazy_expr.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_lazy_expr.hpp (renamed from src/catch2/internal/catch_lazy_expr.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_leak_detector.cpp (renamed from src/catch2/internal/catch_leak_detector.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_leak_detector.hpp (renamed from src/catch2/internal/catch_leak_detector.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_list.cpp (renamed from src/catch2/internal/catch_list.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_list.hpp (renamed from src/catch2/internal/catch_list.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_logical_traits.hpp (renamed from src/catch2/internal/catch_logical_traits.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_main.cpp (renamed from src/catch2/internal/catch_main.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_message_info.cpp (renamed from src/catch2/internal/catch_message_info.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_message_info.hpp (renamed from src/catch2/internal/catch_message_info.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_meta.hpp (renamed from src/catch2/internal/catch_meta.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_move_and_forward.hpp (renamed from src/catch2/internal/catch_move_and_forward.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_noncopyable.hpp (renamed from src/catch2/internal/catch_noncopyable.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_optional.hpp (renamed from src/catch2/internal/catch_optional.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_output_redirect.cpp (renamed from src/catch2/internal/catch_output_redirect.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_output_redirect.hpp (renamed from src/catch2/internal/catch_output_redirect.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_parse_numbers.cpp (renamed from src/catch2/internal/catch_parse_numbers.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_parse_numbers.hpp (renamed from src/catch2/internal/catch_parse_numbers.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_platform.hpp (renamed from src/catch2/internal/catch_platform.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_polyfills.cpp (renamed from src/catch2/internal/catch_polyfills.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_polyfills.hpp (renamed from src/catch2/internal/catch_polyfills.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_preprocessor.hpp (renamed from src/catch2/internal/catch_preprocessor.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_preprocessor_internal_stringify.hpp (renamed from src/catch2/internal/catch_preprocessor_internal_stringify.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_preprocessor_remove_parens.hpp (renamed from src/catch2/internal/catch_preprocessor_remove_parens.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_random_floating_point_helpers.hpp (renamed from src/catch2/internal/catch_random_floating_point_helpers.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_random_integer_helpers.hpp (renamed from src/catch2/internal/catch_random_integer_helpers.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_random_number_generator.cpp (renamed from src/catch2/internal/catch_random_number_generator.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_random_number_generator.hpp (renamed from src/catch2/internal/catch_random_number_generator.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_random_seed_generation.cpp (renamed from src/catch2/internal/catch_random_seed_generation.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_random_seed_generation.hpp (renamed from src/catch2/internal/catch_random_seed_generation.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_reporter_registry.cpp (renamed from src/catch2/internal/catch_reporter_registry.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_reporter_registry.hpp (renamed from src/catch2/internal/catch_reporter_registry.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_reporter_spec_parser.cpp (renamed from src/catch2/internal/catch_reporter_spec_parser.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_reporter_spec_parser.hpp (renamed from src/catch2/internal/catch_reporter_spec_parser.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_result_type.hpp (renamed from src/catch2/internal/catch_result_type.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_reusable_string_stream.cpp (renamed from src/catch2/internal/catch_reusable_string_stream.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_reusable_string_stream.hpp (renamed from src/catch2/internal/catch_reusable_string_stream.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_run_context.cpp (renamed from src/catch2/internal/catch_run_context.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_run_context.hpp (renamed from src/catch2/internal/catch_run_context.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_section.cpp (renamed from src/catch2/internal/catch_section.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_section.hpp (renamed from src/catch2/internal/catch_section.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_sharding.hpp (renamed from src/catch2/internal/catch_sharding.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_singletons.cpp (renamed from src/catch2/internal/catch_singletons.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_singletons.hpp (renamed from src/catch2/internal/catch_singletons.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_source_line_info.cpp (renamed from src/catch2/internal/catch_source_line_info.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_source_line_info.hpp (renamed from src/catch2/internal/catch_source_line_info.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_startup_exception_registry.cpp (renamed from src/catch2/internal/catch_startup_exception_registry.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_startup_exception_registry.hpp (renamed from src/catch2/internal/catch_startup_exception_registry.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_stdstreams.cpp (renamed from src/catch2/internal/catch_stdstreams.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_stdstreams.hpp (renamed from src/catch2/internal/catch_stdstreams.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_stream_end_stop.hpp (renamed from src/catch2/internal/catch_stream_end_stop.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_string_manip.cpp (renamed from src/catch2/internal/catch_string_manip.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_string_manip.hpp (renamed from src/catch2/internal/catch_string_manip.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_stringref.cpp (renamed from src/catch2/internal/catch_stringref.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_stringref.hpp (renamed from src/catch2/internal/catch_stringref.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_tag_alias_registry.cpp (renamed from src/catch2/internal/catch_tag_alias_registry.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_tag_alias_registry.hpp (renamed from src/catch2/internal/catch_tag_alias_registry.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_template_test_registry.hpp (renamed from src/catch2/internal/catch_template_test_registry.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_test_case_info_hasher.cpp (renamed from src/catch2/internal/catch_test_case_info_hasher.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_test_case_info_hasher.hpp (renamed from src/catch2/internal/catch_test_case_info_hasher.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_test_case_registry_impl.cpp (renamed from src/catch2/internal/catch_test_case_registry_impl.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_test_case_registry_impl.hpp (renamed from src/catch2/internal/catch_test_case_registry_impl.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_test_case_tracker.cpp (renamed from src/catch2/internal/catch_test_case_tracker.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_test_case_tracker.hpp (renamed from src/catch2/internal/catch_test_case_tracker.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_test_failure_exception.cpp (renamed from src/catch2/internal/catch_test_failure_exception.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_test_failure_exception.hpp (renamed from src/catch2/internal/catch_test_failure_exception.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_test_macro_impl.hpp (renamed from src/catch2/internal/catch_test_macro_impl.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_test_registry.cpp (renamed from src/catch2/internal/catch_test_registry.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_test_registry.hpp (renamed from src/catch2/internal/catch_test_registry.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_test_run_info.hpp (renamed from src/catch2/internal/catch_test_run_info.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_test_spec_parser.cpp (renamed from src/catch2/internal/catch_test_spec_parser.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_test_spec_parser.hpp (renamed from src/catch2/internal/catch_test_spec_parser.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_textflow.cpp (renamed from src/catch2/internal/catch_textflow.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_textflow.hpp (renamed from src/catch2/internal/catch_textflow.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_to_string.hpp (renamed from src/catch2/internal/catch_to_string.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_uncaught_exceptions.cpp (renamed from src/catch2/internal/catch_uncaught_exceptions.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_uncaught_exceptions.hpp (renamed from src/catch2/internal/catch_uncaught_exceptions.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_uniform_floating_point_distribution.hpp (renamed from src/catch2/internal/catch_uniform_floating_point_distribution.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_uniform_integer_distribution.hpp (renamed from src/catch2/internal/catch_uniform_integer_distribution.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_unique_name.hpp (renamed from src/catch2/internal/catch_unique_name.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_unique_ptr.hpp (renamed from src/catch2/internal/catch_unique_ptr.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_void_type.hpp (renamed from src/catch2/internal/catch_void_type.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_wildcard_pattern.cpp (renamed from src/catch2/internal/catch_wildcard_pattern.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_wildcard_pattern.hpp (renamed from src/catch2/internal/catch_wildcard_pattern.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_windows_h_proxy.hpp (renamed from src/catch2/internal/catch_windows_h_proxy.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_xmlwriter.cpp (renamed from src/catch2/internal/catch_xmlwriter.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/internal/catch_xmlwriter.hpp (renamed from src/catch2/internal/catch_xmlwriter.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/matchers/catch_matchers.cpp (renamed from src/catch2/matchers/catch_matchers.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/matchers/catch_matchers.hpp (renamed from src/catch2/matchers/catch_matchers.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/matchers/catch_matchers_all.hpp (renamed from src/catch2/matchers/catch_matchers_all.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/matchers/catch_matchers_container_properties.cpp (renamed from src/catch2/matchers/catch_matchers_container_properties.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/matchers/catch_matchers_container_properties.hpp (renamed from src/catch2/matchers/catch_matchers_container_properties.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/matchers/catch_matchers_contains.hpp (renamed from src/catch2/matchers/catch_matchers_contains.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/matchers/catch_matchers_exception.cpp (renamed from src/catch2/matchers/catch_matchers_exception.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/matchers/catch_matchers_exception.hpp (renamed from src/catch2/matchers/catch_matchers_exception.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/matchers/catch_matchers_floating_point.cpp (renamed from src/catch2/matchers/catch_matchers_floating_point.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/matchers/catch_matchers_floating_point.hpp (renamed from src/catch2/matchers/catch_matchers_floating_point.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/matchers/catch_matchers_predicate.cpp (renamed from src/catch2/matchers/catch_matchers_predicate.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/matchers/catch_matchers_predicate.hpp (renamed from src/catch2/matchers/catch_matchers_predicate.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/matchers/catch_matchers_quantifiers.cpp (renamed from src/catch2/matchers/catch_matchers_quantifiers.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/matchers/catch_matchers_quantifiers.hpp (renamed from src/catch2/matchers/catch_matchers_quantifiers.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/matchers/catch_matchers_range_equals.hpp (renamed from src/catch2/matchers/catch_matchers_range_equals.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/matchers/catch_matchers_string.cpp (renamed from src/catch2/matchers/catch_matchers_string.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/matchers/catch_matchers_string.hpp (renamed from src/catch2/matchers/catch_matchers_string.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/matchers/catch_matchers_templated.cpp (renamed from src/catch2/matchers/catch_matchers_templated.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/matchers/catch_matchers_templated.hpp (renamed from src/catch2/matchers/catch_matchers_templated.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/matchers/catch_matchers_vector.hpp (renamed from src/catch2/matchers/catch_matchers_vector.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/matchers/internal/catch_matchers_impl.cpp (renamed from src/catch2/matchers/internal/catch_matchers_impl.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/matchers/internal/catch_matchers_impl.hpp (renamed from src/catch2/matchers/internal/catch_matchers_impl.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/meson.build (renamed from src/catch2/meson.build) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/reporters/catch_reporter_automake.cpp (renamed from src/catch2/reporters/catch_reporter_automake.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/reporters/catch_reporter_automake.hpp (renamed from src/catch2/reporters/catch_reporter_automake.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/reporters/catch_reporter_common_base.cpp (renamed from src/catch2/reporters/catch_reporter_common_base.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/reporters/catch_reporter_common_base.hpp (renamed from src/catch2/reporters/catch_reporter_common_base.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/reporters/catch_reporter_compact.cpp (renamed from src/catch2/reporters/catch_reporter_compact.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/reporters/catch_reporter_compact.hpp (renamed from src/catch2/reporters/catch_reporter_compact.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/reporters/catch_reporter_console.cpp (renamed from src/catch2/reporters/catch_reporter_console.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/reporters/catch_reporter_console.hpp (renamed from src/catch2/reporters/catch_reporter_console.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/reporters/catch_reporter_cumulative_base.cpp (renamed from src/catch2/reporters/catch_reporter_cumulative_base.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/reporters/catch_reporter_cumulative_base.hpp (renamed from src/catch2/reporters/catch_reporter_cumulative_base.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/reporters/catch_reporter_event_listener.cpp (renamed from src/catch2/reporters/catch_reporter_event_listener.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/reporters/catch_reporter_event_listener.hpp (renamed from src/catch2/reporters/catch_reporter_event_listener.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/reporters/catch_reporter_helpers.cpp (renamed from src/catch2/reporters/catch_reporter_helpers.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/reporters/catch_reporter_helpers.hpp (renamed from src/catch2/reporters/catch_reporter_helpers.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/reporters/catch_reporter_json.cpp (renamed from src/catch2/reporters/catch_reporter_json.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/reporters/catch_reporter_json.hpp (renamed from src/catch2/reporters/catch_reporter_json.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/reporters/catch_reporter_junit.cpp (renamed from src/catch2/reporters/catch_reporter_junit.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/reporters/catch_reporter_junit.hpp (renamed from src/catch2/reporters/catch_reporter_junit.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/reporters/catch_reporter_multi.cpp (renamed from src/catch2/reporters/catch_reporter_multi.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/reporters/catch_reporter_multi.hpp (renamed from src/catch2/reporters/catch_reporter_multi.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/reporters/catch_reporter_registrars.cpp (renamed from src/catch2/reporters/catch_reporter_registrars.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/reporters/catch_reporter_registrars.hpp (renamed from src/catch2/reporters/catch_reporter_registrars.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/reporters/catch_reporter_sonarqube.cpp (renamed from src/catch2/reporters/catch_reporter_sonarqube.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/reporters/catch_reporter_sonarqube.hpp (renamed from src/catch2/reporters/catch_reporter_sonarqube.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/reporters/catch_reporter_streaming_base.cpp (renamed from src/catch2/reporters/catch_reporter_streaming_base.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/reporters/catch_reporter_streaming_base.hpp (renamed from src/catch2/reporters/catch_reporter_streaming_base.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/reporters/catch_reporter_tap.cpp (renamed from src/catch2/reporters/catch_reporter_tap.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/reporters/catch_reporter_tap.hpp (renamed from src/catch2/reporters/catch_reporter_tap.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/reporters/catch_reporter_teamcity.cpp (renamed from src/catch2/reporters/catch_reporter_teamcity.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/reporters/catch_reporter_teamcity.hpp (renamed from src/catch2/reporters/catch_reporter_teamcity.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/reporters/catch_reporter_xml.cpp (renamed from src/catch2/reporters/catch_reporter_xml.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/reporters/catch_reporter_xml.hpp (renamed from src/catch2/reporters/catch_reporter_xml.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/src/catch2/reporters/catch_reporters_all.hpp (renamed from src/catch2/reporters/catch_reporters_all.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/BUILD.bazel (renamed from tests/BUILD.bazel) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/CMakeLists.txt (renamed from tests/CMakeLists.txt) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/ExtraTests/CMakeLists.txt (renamed from tests/ExtraTests/CMakeLists.txt) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/ExtraTests/ToDo.txt (renamed from tests/ExtraTests/ToDo.txt) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/ExtraTests/X01-PrefixedMacros.cpp (renamed from tests/ExtraTests/X01-PrefixedMacros.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/ExtraTests/X02-DisabledMacros.cpp (renamed from tests/ExtraTests/X02-DisabledMacros.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/ExtraTests/X03-DisabledExceptions-DefaultHandler.cpp (renamed from tests/ExtraTests/X03-DisabledExceptions-DefaultHandler.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/ExtraTests/X04-DisabledExceptions-CustomHandler.cpp (renamed from tests/ExtraTests/X04-DisabledExceptions-CustomHandler.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/ExtraTests/X05-DeferredStaticChecks.cpp (renamed from tests/ExtraTests/X05-DeferredStaticChecks.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/ExtraTests/X10-FallbackStringifier.cpp (renamed from tests/ExtraTests/X10-FallbackStringifier.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/ExtraTests/X11-DisableStringification.cpp (renamed from tests/ExtraTests/X11-DisableStringification.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/ExtraTests/X12-CustomDebugBreakMacro.cpp (renamed from tests/ExtraTests/X12-CustomDebugBreakMacro.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/ExtraTests/X20-AssertionStartingEventGoesBeforeAssertionIsEvaluated.cpp (renamed from tests/ExtraTests/X20-AssertionStartingEventGoesBeforeAssertionIsEvaluated.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/ExtraTests/X21-PartialTestCaseEvents.cpp (renamed from tests/ExtraTests/X21-PartialTestCaseEvents.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/ExtraTests/X22-BenchmarksInCumulativeReporter.cpp (renamed from tests/ExtraTests/X22-BenchmarksInCumulativeReporter.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/ExtraTests/X23-CasingInReporterNames.cpp (renamed from tests/ExtraTests/X23-CasingInReporterNames.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/ExtraTests/X24-ListenerStdoutCaptureInMultireporter.cpp (renamed from tests/ExtraTests/X24-ListenerStdoutCaptureInMultireporter.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/ExtraTests/X25-ListenerCanAskForCapturedStdout.cpp (renamed from tests/ExtraTests/X25-ListenerCanAskForCapturedStdout.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/ExtraTests/X26-ReporterPreferencesForPassingAssertionsIsRespected.cpp (renamed from tests/ExtraTests/X26-ReporterPreferencesForPassingAssertionsIsRespected.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/ExtraTests/X27-CapturedStdoutInTestCaseEvents.cpp (renamed from tests/ExtraTests/X27-CapturedStdoutInTestCaseEvents.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/ExtraTests/X28-ListenersGetEventsBeforeReporters.cpp (renamed from tests/ExtraTests/X28-ListenersGetEventsBeforeReporters.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/ExtraTests/X29-CustomArgumentsForReporters.cpp (renamed from tests/ExtraTests/X29-CustomArgumentsForReporters.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/ExtraTests/X30-BazelReporter.cpp (renamed from tests/ExtraTests/X30-BazelReporter.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/ExtraTests/X31-DuplicatedTestCases.cpp (renamed from tests/ExtraTests/X31-DuplicatedTestCases.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/ExtraTests/X32-DuplicatedTestCasesDifferentTags.cpp (renamed from tests/ExtraTests/X32-DuplicatedTestCasesDifferentTags.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/ExtraTests/X33-DuplicatedTestCaseMethods.cpp (renamed from tests/ExtraTests/X33-DuplicatedTestCaseMethods.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/ExtraTests/X34-DuplicatedTestCaseMethodsDifferentFixtures.cpp (renamed from tests/ExtraTests/X34-DuplicatedTestCaseMethodsDifferentFixtures.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/ExtraTests/X35-DuplicatedReporterNames.cpp (renamed from tests/ExtraTests/X35-DuplicatedReporterNames.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/ExtraTests/X36-ReportingCrashWithJunitReporter.cpp (renamed from tests/ExtraTests/X36-ReportingCrashWithJunitReporter.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/ExtraTests/X90-WindowsHeaderInclusion.cpp (renamed from tests/ExtraTests/X90-WindowsHeaderInclusion.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/ExtraTests/X91-AmalgamatedCatch.cpp (renamed from tests/ExtraTests/X91-AmalgamatedCatch.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/ExtraTests/X92-NoTests.cpp (renamed from tests/ExtraTests/X92-NoTests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/ExtraTests/X93-AllSkipped.cpp (renamed from tests/ExtraTests/X93-AllSkipped.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/Baselines/automake.std.approved.txt (renamed from tests/SelfTest/Baselines/automake.std.approved.txt) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/Baselines/automake.sw.approved.txt (renamed from tests/SelfTest/Baselines/automake.sw.approved.txt) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/Baselines/automake.sw.multi.approved.txt (renamed from tests/SelfTest/Baselines/automake.sw.multi.approved.txt) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/Baselines/compact.sw.approved.txt (renamed from tests/SelfTest/Baselines/compact.sw.approved.txt) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/Baselines/compact.sw.multi.approved.txt (renamed from tests/SelfTest/Baselines/compact.sw.multi.approved.txt) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/Baselines/console.std.approved.txt (renamed from tests/SelfTest/Baselines/console.std.approved.txt) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/Baselines/console.sw.approved.txt (renamed from tests/SelfTest/Baselines/console.sw.approved.txt) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/Baselines/console.sw.multi.approved.txt (renamed from tests/SelfTest/Baselines/console.sw.multi.approved.txt) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/Baselines/console.swa4.approved.txt (renamed from tests/SelfTest/Baselines/console.swa4.approved.txt) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/Baselines/default.sw.multi.approved.txt (renamed from tests/SelfTest/Baselines/default.sw.multi.approved.txt) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/Baselines/junit.sw.approved.txt (renamed from tests/SelfTest/Baselines/junit.sw.approved.txt) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/Baselines/junit.sw.multi.approved.txt (renamed from tests/SelfTest/Baselines/junit.sw.multi.approved.txt) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/Baselines/sonarqube.sw.approved.txt (renamed from tests/SelfTest/Baselines/sonarqube.sw.approved.txt) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/Baselines/sonarqube.sw.multi.approved.txt (renamed from tests/SelfTest/Baselines/sonarqube.sw.multi.approved.txt) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/Baselines/tap.sw.approved.txt (renamed from tests/SelfTest/Baselines/tap.sw.approved.txt) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/Baselines/tap.sw.multi.approved.txt (renamed from tests/SelfTest/Baselines/tap.sw.multi.approved.txt) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/Baselines/teamcity.sw.approved.txt (renamed from tests/SelfTest/Baselines/teamcity.sw.approved.txt) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/Baselines/teamcity.sw.multi.approved.txt (renamed from tests/SelfTest/Baselines/teamcity.sw.multi.approved.txt) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/Baselines/xml.sw.approved.txt (renamed from tests/SelfTest/Baselines/xml.sw.approved.txt) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/Baselines/xml.sw.multi.approved.txt (renamed from tests/SelfTest/Baselines/xml.sw.multi.approved.txt) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/IntrospectiveTests/Algorithms.tests.cpp (renamed from tests/SelfTest/IntrospectiveTests/Algorithms.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/IntrospectiveTests/AssertionHandler.tests.cpp (renamed from tests/SelfTest/IntrospectiveTests/AssertionHandler.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/IntrospectiveTests/Clara.tests.cpp (renamed from tests/SelfTest/IntrospectiveTests/Clara.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/IntrospectiveTests/CmdLine.tests.cpp (renamed from tests/SelfTest/IntrospectiveTests/CmdLine.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/IntrospectiveTests/CmdLineHelpers.tests.cpp (renamed from tests/SelfTest/IntrospectiveTests/CmdLineHelpers.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/IntrospectiveTests/ColourImpl.tests.cpp (renamed from tests/SelfTest/IntrospectiveTests/ColourImpl.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/IntrospectiveTests/Details.tests.cpp (renamed from tests/SelfTest/IntrospectiveTests/Details.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/IntrospectiveTests/FloatingPoint.tests.cpp (renamed from tests/SelfTest/IntrospectiveTests/FloatingPoint.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/IntrospectiveTests/GeneratorsImpl.tests.cpp (renamed from tests/SelfTest/IntrospectiveTests/GeneratorsImpl.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/IntrospectiveTests/Integer.tests.cpp (renamed from tests/SelfTest/IntrospectiveTests/Integer.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/IntrospectiveTests/InternalBenchmark.tests.cpp (renamed from tests/SelfTest/IntrospectiveTests/InternalBenchmark.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/IntrospectiveTests/Json.tests.cpp (renamed from tests/SelfTest/IntrospectiveTests/Json.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/IntrospectiveTests/Parse.tests.cpp (renamed from tests/SelfTest/IntrospectiveTests/Parse.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/IntrospectiveTests/PartTracker.tests.cpp (renamed from tests/SelfTest/IntrospectiveTests/PartTracker.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/IntrospectiveTests/RandomNumberGeneration.tests.cpp (renamed from tests/SelfTest/IntrospectiveTests/RandomNumberGeneration.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/IntrospectiveTests/Reporters.tests.cpp (renamed from tests/SelfTest/IntrospectiveTests/Reporters.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/IntrospectiveTests/Sharding.tests.cpp (renamed from tests/SelfTest/IntrospectiveTests/Sharding.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/IntrospectiveTests/Stream.tests.cpp (renamed from tests/SelfTest/IntrospectiveTests/Stream.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/IntrospectiveTests/String.tests.cpp (renamed from tests/SelfTest/IntrospectiveTests/String.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/IntrospectiveTests/StringManip.tests.cpp (renamed from tests/SelfTest/IntrospectiveTests/StringManip.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/IntrospectiveTests/Tag.tests.cpp (renamed from tests/SelfTest/IntrospectiveTests/Tag.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/IntrospectiveTests/TestCaseInfoHasher.tests.cpp (renamed from tests/SelfTest/IntrospectiveTests/TestCaseInfoHasher.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/IntrospectiveTests/TestSpec.tests.cpp (renamed from tests/SelfTest/IntrospectiveTests/TestSpec.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/IntrospectiveTests/TestSpecParser.tests.cpp (renamed from tests/SelfTest/IntrospectiveTests/TestSpecParser.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/IntrospectiveTests/TextFlow.tests.cpp (renamed from tests/SelfTest/IntrospectiveTests/TextFlow.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/IntrospectiveTests/ToString.tests.cpp (renamed from tests/SelfTest/IntrospectiveTests/ToString.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/IntrospectiveTests/Traits.tests.cpp (renamed from tests/SelfTest/IntrospectiveTests/Traits.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/IntrospectiveTests/UniquePtr.tests.cpp (renamed from tests/SelfTest/IntrospectiveTests/UniquePtr.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/IntrospectiveTests/Xml.tests.cpp (renamed from tests/SelfTest/IntrospectiveTests/Xml.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/Misc/invalid-test-names.input (renamed from tests/SelfTest/Misc/invalid-test-names.input) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/Misc/plain-old-tests.input (renamed from tests/SelfTest/Misc/plain-old-tests.input) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/Misc/special-characters-in-file.input (renamed from tests/SelfTest/Misc/special-characters-in-file.input) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/TestRegistrations.cpp (renamed from tests/SelfTest/TestRegistrations.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/TimingTests/Sleep.tests.cpp (renamed from tests/SelfTest/TimingTests/Sleep.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/UsageTests/Approx.tests.cpp (renamed from tests/SelfTest/UsageTests/Approx.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/UsageTests/BDD.tests.cpp (renamed from tests/SelfTest/UsageTests/BDD.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/UsageTests/Benchmark.tests.cpp (renamed from tests/SelfTest/UsageTests/Benchmark.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/UsageTests/Class.tests.cpp (renamed from tests/SelfTest/UsageTests/Class.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/UsageTests/Compilation.tests.cpp (renamed from tests/SelfTest/UsageTests/Compilation.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/UsageTests/Condition.tests.cpp (renamed from tests/SelfTest/UsageTests/Condition.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/UsageTests/Decomposition.tests.cpp (renamed from tests/SelfTest/UsageTests/Decomposition.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/UsageTests/EnumToString.tests.cpp (renamed from tests/SelfTest/UsageTests/EnumToString.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/UsageTests/Exception.tests.cpp (renamed from tests/SelfTest/UsageTests/Exception.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/UsageTests/Generators.tests.cpp (renamed from tests/SelfTest/UsageTests/Generators.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/UsageTests/Matchers.tests.cpp (renamed from tests/SelfTest/UsageTests/Matchers.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/UsageTests/MatchersRanges.tests.cpp (renamed from tests/SelfTest/UsageTests/MatchersRanges.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/UsageTests/Message.tests.cpp (renamed from tests/SelfTest/UsageTests/Message.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/UsageTests/Misc.tests.cpp (renamed from tests/SelfTest/UsageTests/Misc.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/UsageTests/Skip.tests.cpp (renamed from tests/SelfTest/UsageTests/Skip.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/UsageTests/ToStringByte.tests.cpp (renamed from tests/SelfTest/UsageTests/ToStringByte.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/UsageTests/ToStringChrono.tests.cpp (renamed from tests/SelfTest/UsageTests/ToStringChrono.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/UsageTests/ToStringGeneral.tests.cpp (renamed from tests/SelfTest/UsageTests/ToStringGeneral.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/UsageTests/ToStringOptional.tests.cpp (renamed from tests/SelfTest/UsageTests/ToStringOptional.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/UsageTests/ToStringPair.tests.cpp (renamed from tests/SelfTest/UsageTests/ToStringPair.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/UsageTests/ToStringTuple.tests.cpp (renamed from tests/SelfTest/UsageTests/ToStringTuple.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/UsageTests/ToStringVariant.tests.cpp (renamed from tests/SelfTest/UsageTests/ToStringVariant.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/UsageTests/ToStringVector.tests.cpp (renamed from tests/SelfTest/UsageTests/ToStringVector.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/UsageTests/ToStringWhich.tests.cpp (renamed from tests/SelfTest/UsageTests/ToStringWhich.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/UsageTests/Tricky.tests.cpp (renamed from tests/SelfTest/UsageTests/Tricky.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/UsageTests/VariadicMacros.tests.cpp (renamed from tests/SelfTest/UsageTests/VariadicMacros.tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/helpers/parse_test_spec.cpp (renamed from tests/SelfTest/helpers/parse_test_spec.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/helpers/parse_test_spec.hpp (renamed from tests/SelfTest/helpers/parse_test_spec.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/helpers/range_test_helpers.hpp (renamed from tests/SelfTest/helpers/range_test_helpers.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/SelfTest/helpers/type_with_lit_0_comparisons.hpp (renamed from tests/SelfTest/helpers/type_with_lit_0_comparisons.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/TestScripts/ConfigureTestsCommon.py (renamed from tests/TestScripts/ConfigureTestsCommon.py) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/TestScripts/DiscoverTests/CMakeLists.txt (renamed from tests/TestScripts/DiscoverTests/CMakeLists.txt) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/TestScripts/DiscoverTests/VerifyRegistration.py (renamed from tests/TestScripts/DiscoverTests/VerifyRegistration.py) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/TestScripts/DiscoverTests/register-tests.cpp (renamed from tests/TestScripts/DiscoverTests/register-tests.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/TestScripts/testBazelReporter.py (renamed from tests/TestScripts/testBazelReporter.py) | 0 | ||||
| -rwxr-xr-x | contrib/catch2/tests/TestScripts/testBazelSharding.py (renamed from tests/TestScripts/testBazelSharding.py) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/TestScripts/testConfigureDefaultReporter.py (renamed from tests/TestScripts/testConfigureDefaultReporter.py) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/TestScripts/testConfigureDisable.py (renamed from tests/TestScripts/testConfigureDisable.py) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/TestScripts/testConfigureDisableStringification.py (renamed from tests/TestScripts/testConfigureDisableStringification.py) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/TestScripts/testConfigureExperimentalRedirect.py (renamed from tests/TestScripts/testConfigureExperimentalRedirect.py) | 0 | ||||
| -rwxr-xr-x | contrib/catch2/tests/TestScripts/testPartialTestCaseEvent.py (renamed from tests/TestScripts/testPartialTestCaseEvent.py) | 0 | ||||
| -rwxr-xr-x | contrib/catch2/tests/TestScripts/testRandomOrder.py (renamed from tests/TestScripts/testRandomOrder.py) | 0 | ||||
| -rwxr-xr-x | contrib/catch2/tests/TestScripts/testSharding.py (renamed from tests/TestScripts/testSharding.py) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tests/meson.build (renamed from tests/meson.build) | 0 | ||||
| -rw-r--r-- | contrib/catch2/third_party/clara.hpp (renamed from third_party/clara.hpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tools/misc/CMakeLists.txt (renamed from tools/misc/CMakeLists.txt) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tools/misc/appveyorBuildConfigurationScript.bat (renamed from tools/misc/appveyorBuildConfigurationScript.bat) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tools/misc/appveyorMergeCoverageScript.py (renamed from tools/misc/appveyorMergeCoverageScript.py) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tools/misc/appveyorTestRunScript.bat (renamed from tools/misc/appveyorTestRunScript.bat) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tools/misc/coverage-helper.cpp (renamed from tools/misc/coverage-helper.cpp) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tools/misc/installOpenCppCoverage.ps1 (renamed from tools/misc/installOpenCppCoverage.ps1) | 0 | ||||
| -rwxr-xr-x | contrib/catch2/tools/scripts/approvalTests.py (renamed from tools/scripts/approvalTests.py) | 0 | ||||
| -rwxr-xr-x | contrib/catch2/tools/scripts/approve.py (renamed from tools/scripts/approve.py) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tools/scripts/buildAndTest.cmd (renamed from tools/scripts/buildAndTest.cmd) | 0 | ||||
| -rwxr-xr-x | contrib/catch2/tools/scripts/buildAndTest.sh (renamed from tools/scripts/buildAndTest.sh) | 0 | ||||
| -rwxr-xr-x | contrib/catch2/tools/scripts/checkConvenienceHeaders.py (renamed from tools/scripts/checkConvenienceHeaders.py) | 0 | ||||
| -rwxr-xr-x | contrib/catch2/tools/scripts/checkDuplicateFilenames.py (renamed from tools/scripts/checkDuplicateFilenames.py) | 0 | ||||
| -rwxr-xr-x | contrib/catch2/tools/scripts/checkLicense.py (renamed from tools/scripts/checkLicense.py) | 0 | ||||
| -rwxr-xr-x | contrib/catch2/tools/scripts/developBuild.py (renamed from tools/scripts/developBuild.py) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tools/scripts/extractFeaturesFromReleaseNotes.py (renamed from tools/scripts/extractFeaturesFromReleaseNotes.py) | 0 | ||||
| -rwxr-xr-x | contrib/catch2/tools/scripts/fixWhitespace.py (renamed from tools/scripts/fixWhitespace.py) | 0 | ||||
| -rwxr-xr-x | contrib/catch2/tools/scripts/generateAmalgamatedFiles.py (renamed from tools/scripts/generateAmalgamatedFiles.py) | 0 | ||||
| -rwxr-xr-x | contrib/catch2/tools/scripts/majorRelease.py (renamed from tools/scripts/majorRelease.py) | 0 | ||||
| -rwxr-xr-x | contrib/catch2/tools/scripts/minorRelease.py (renamed from tools/scripts/minorRelease.py) | 0 | ||||
| -rwxr-xr-x | contrib/catch2/tools/scripts/patchRelease.py (renamed from tools/scripts/patchRelease.py) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tools/scripts/releaseCommon.py (renamed from tools/scripts/releaseCommon.py) | 0 | ||||
| -rw-r--r-- | contrib/catch2/tools/scripts/scriptCommon.py (renamed from tools/scripts/scriptCommon.py) | 0 | ||||
| -rwxr-xr-x | contrib/catch2/tools/scripts/updateDocumentSnippets.py (renamed from tools/scripts/updateDocumentSnippets.py) | 0 | ||||
| -rwxr-xr-x | contrib/catch2/tools/scripts/updateDocumentToC.py (renamed from tools/scripts/updateDocumentToC.py) | 0 | ||||
| -rw-r--r-- | nihil.cli/CMakeLists.txt | 40 | ||||
| -rw-r--r-- | nihil.cli/command.cc | 52 | ||||
| -rw-r--r-- | nihil.cli/command.ccm | 48 | ||||
| -rw-r--r-- | nihil.cli/command_node.cc | 49 | ||||
| -rw-r--r-- | nihil.cli/command_node.ccm | 35 | ||||
| -rw-r--r-- | nihil.cli/command_tree.cc | 179 | ||||
| -rw-r--r-- | nihil.cli/command_tree.ccm | 107 | ||||
| -rw-r--r-- | nihil.cli/dispatch_command.cc | 82 | ||||
| -rw-r--r-- | nihil.cli/dispatch_command.ccm | 31 | ||||
| -rw-r--r-- | nihil.cli/nihil.cli.ccm | 14 | ||||
| -rw-r--r-- | nihil.cli/registry.cc | 57 | ||||
| -rw-r--r-- | nihil.cli/registry.ccm | 28 | ||||
| -rw-r--r-- | nihil.cli/test.cc | 104 | ||||
| -rw-r--r-- | nihil.cli/usage_error.ccm | 22 | ||||
| -rw-r--r-- | nihil.config/CMakeLists.txt | 31 | ||||
| -rw-r--r-- | nihil.config/nihil.config.ccm | 13 | ||||
| -rw-r--r-- | nihil.config/option.cc | 97 | ||||
| -rw-r--r-- | nihil.config/option.ccm | 105 | ||||
| -rw-r--r-- | nihil.config/read.cc | 50 | ||||
| -rw-r--r-- | nihil.config/read.ccm | 22 | ||||
| -rw-r--r-- | nihil.config/store.cc | 95 | ||||
| -rw-r--r-- | nihil.config/store.ccm | 75 | ||||
| -rw-r--r-- | nihil.config/string.cc | 62 | ||||
| -rw-r--r-- | nihil.config/string.ccm | 56 | ||||
| -rw-r--r-- | nihil.config/tests/CMakeLists.txt | 15 | ||||
| -rw-r--r-- | nihil.config/tests/string.cc | 36 | ||||
| -rw-r--r-- | nihil.config/write.cc | 41 | ||||
| -rw-r--r-- | nihil.config/write.ccm | 22 | ||||
| -rw-r--r-- | nihil.core/CMakeLists.txt | 12 | ||||
| -rw-r--r-- | nihil.core/errc.cc | 51 | ||||
| -rw-r--r-- | nihil.core/errc.ccm | 44 | ||||
| -rw-r--r-- | nihil.core/nihil.core.ccm | 9 | ||||
| -rw-r--r-- | nihil.core/nihil.hh | 26 | ||||
| -rw-r--r-- | nihil.error/CMakeLists.txt | 29 | ||||
| -rw-r--r-- | nihil.error/error.cc | 160 | ||||
| -rw-r--r-- | nihil.error/error.ccm | 199 | ||||
| -rw-r--r-- | nihil.error/test.cc | 169 | ||||
| -rw-r--r-- | nihil.flagset/CMakeLists.txt | 23 | ||||
| -rw-r--r-- | nihil.flagset/flagset.ccm | 228 | ||||
| -rw-r--r-- | nihil.flagset/test.cc | 149 | ||||
| -rw-r--r-- | nihil.generator/CMakeLists.txt | 29 | ||||
| -rw-r--r-- | nihil.generator/elements_of.ccm | 69 | ||||
| -rw-r--r-- | nihil.generator/generator.ccm | 507 | ||||
| -rw-r--r-- | nihil.generator/manual_lifetime.ccm | 117 | ||||
| -rw-r--r-- | nihil.generator/nihil.generator.ccm | 32 | ||||
| -rw-r--r-- | nihil.generator/promise_base_alloc.ccm | 94 | ||||
| -rw-r--r-- | nihil.generator/test.cc | 56 | ||||
| -rw-r--r-- | nihil.generator/util.ccm | 37 | ||||
| -rw-r--r-- | nihil.guard/CMakeLists.txt | 23 | ||||
| -rw-r--r-- | nihil.guard/guard.ccm | 53 | ||||
| -rw-r--r-- | nihil.guard/test.cc | 20 | ||||
| -rw-r--r-- | nihil.match/CMakeLists.txt | 23 | ||||
| -rw-r--r-- | nihil.match/match.ccm | 23 | ||||
| -rw-r--r-- | nihil.match/test.cc | 34 | ||||
| -rw-r--r-- | nihil.monad/CMakeLists.txt | 24 | ||||
| -rw-r--r-- | nihil.monad/monad.ccm | 289 | ||||
| -rw-r--r-- | nihil.monad/test.cc | 69 | ||||
| -rw-r--r-- | nihil.posix/CMakeLists.txt | 61 | ||||
| -rw-r--r-- | nihil.posix/argv.cc | 65 | ||||
| -rw-r--r-- | nihil.posix/argv.ccm | 78 | ||||
| -rw-r--r-- | nihil.posix/ensure_dir.cc | 30 | ||||
| -rw-r--r-- | nihil.posix/ensure_dir.ccm | 23 | ||||
| -rw-r--r-- | nihil.posix/exec.cc | 71 | ||||
| -rw-r--r-- | nihil.posix/exec.ccm | 105 | ||||
| -rw-r--r-- | nihil.posix/executor.ccm | 27 | ||||
| -rw-r--r-- | nihil.posix/execv.cc | 43 | ||||
| -rw-r--r-- | nihil.posix/execv.ccm | 47 | ||||
| -rw-r--r-- | nihil.posix/fd.cc | 220 | ||||
| -rw-r--r-- | nihil.posix/fd.ccm | 157 | ||||
| -rw-r--r-- | nihil.posix/fexecv.ccm | 53 | ||||
| -rw-r--r-- | nihil.posix/find_in_path.cc | 52 | ||||
| -rw-r--r-- | nihil.posix/find_in_path.ccm | 24 | ||||
| -rw-r--r-- | nihil.posix/getenv.cc | 54 | ||||
| -rw-r--r-- | nihil.posix/getenv.ccm | 23 | ||||
| -rw-r--r-- | nihil.posix/open.cc | 31 | ||||
| -rw-r--r-- | nihil.posix/open.ccm | 24 | ||||
| -rw-r--r-- | nihil.posix/open_in_path.cc | 51 | ||||
| -rw-r--r-- | nihil.posix/open_in_path.ccm | 23 | ||||
| -rw-r--r-- | nihil.posix/posix.ccm | 35 | ||||
| -rw-r--r-- | nihil.posix/process.cc | 102 | ||||
| -rw-r--r-- | nihil.posix/process.ccm | 91 | ||||
| -rw-r--r-- | nihil.posix/read_file.ccm | 49 | ||||
| -rw-r--r-- | nihil.posix/rename.cc | 34 | ||||
| -rw-r--r-- | nihil.posix/rename.ccm | 23 | ||||
| -rw-r--r-- | nihil.posix/spawn.ccm | 246 | ||||
| -rw-r--r-- | nihil.posix/tempfile.cc | 128 | ||||
| -rw-r--r-- | nihil.posix/tempfile.ccm | 87 | ||||
| -rw-r--r-- | nihil.posix/test.fd.cc | 199 | ||||
| -rw-r--r-- | nihil.posix/test.getenv.cc | 50 | ||||
| -rw-r--r-- | nihil.posix/test.spawn.cc | 117 | ||||
| -rw-r--r-- | nihil.posix/test.tempfile.cc | 90 | ||||
| -rw-r--r-- | nihil.posix/write_file.ccm | 82 | ||||
| -rw-r--r-- | nihil.ucl/CMakeLists.txt | 46 | ||||
| -rw-r--r-- | nihil.ucl/array.ccm | 468 | ||||
| -rw-r--r-- | nihil.ucl/boolean.cc | 106 | ||||
| -rw-r--r-- | nihil.ucl/boolean.ccm | 91 | ||||
| -rw-r--r-- | nihil.ucl/emit.cc | 21 | ||||
| -rw-r--r-- | nihil.ucl/emit.ccm | 209 | ||||
| -rw-r--r-- | nihil.ucl/errc.cc | 49 | ||||
| -rw-r--r-- | nihil.ucl/errc.ccm | 33 | ||||
| -rw-r--r-- | nihil.ucl/integer.cc | 102 | ||||
| -rw-r--r-- | nihil.ucl/integer.ccm | 115 | ||||
| -rw-r--r-- | nihil.ucl/map.ccm | 293 | ||||
| -rw-r--r-- | nihil.ucl/nihil.ucl.ccm | 21 | ||||
| -rw-r--r-- | nihil.ucl/object.cc | 114 | ||||
| -rw-r--r-- | nihil.ucl/object.ccm | 88 | ||||
| -rw-r--r-- | nihil.ucl/object_cast.ccm | 89 | ||||
| -rw-r--r-- | nihil.ucl/parser.cc | 102 | ||||
| -rw-r--r-- | nihil.ucl/parser.ccm | 160 | ||||
| -rw-r--r-- | nihil.ucl/real.cc | 104 | ||||
| -rw-r--r-- | nihil.ucl/real.ccm | 112 | ||||
| -rw-r--r-- | nihil.ucl/string.cc | 187 | ||||
| -rw-r--r-- | nihil.ucl/string.ccm | 229 | ||||
| -rw-r--r-- | nihil.ucl/tests/CMakeLists.txt | 22 | ||||
| -rw-r--r-- | nihil.ucl/tests/array.cc | 478 | ||||
| -rw-r--r-- | nihil.ucl/tests/boolean.cc | 224 | ||||
| -rw-r--r-- | nihil.ucl/tests/emit.cc | 93 | ||||
| -rw-r--r-- | nihil.ucl/tests/integer.cc | 247 | ||||
| -rw-r--r-- | nihil.ucl/tests/map.cc | 192 | ||||
| -rw-r--r-- | nihil.ucl/tests/object.cc | 44 | ||||
| -rw-r--r-- | nihil.ucl/tests/parse.cc | 55 | ||||
| -rw-r--r-- | nihil.ucl/tests/real.cc | 248 | ||||
| -rw-r--r-- | nihil.ucl/tests/string.cc | 415 | ||||
| -rw-r--r-- | nihil.ucl/type.cc | 62 | ||||
| -rw-r--r-- | nihil.ucl/type.ccm | 58 | ||||
| -rw-r--r-- | nihil.util/CMakeLists.txt | 38 | ||||
| -rw-r--r-- | nihil.util/capture_stream.ccm | 62 | ||||
| -rw-r--r-- | nihil.util/ctype.ccm | 87 | ||||
| -rw-r--r-- | nihil.util/next_word.ccm | 49 | ||||
| -rw-r--r-- | nihil.util/nihil.util.ccm | 14 | ||||
| -rw-r--r-- | nihil.util/parse_size.ccm | 107 | ||||
| -rw-r--r-- | nihil.util/skipws.ccm | 40 | ||||
| -rw-r--r-- | nihil.util/tabulate.ccm | 312 | ||||
| -rw-r--r-- | nihil.util/test_capture_stream.cc | 44 | ||||
| -rw-r--r-- | nihil.util/test_ctype.cc | 373 | ||||
| -rw-r--r-- | nihil.util/test_next_word.cc | 65 | ||||
| -rw-r--r-- | nihil.util/test_parse_size.cc | 169 | ||||
| -rw-r--r-- | nihil.util/test_skipws.cc | 45 | ||||
| -rw-r--r-- | nihil.util/test_tabulate.cc | 75 | ||||
| -rw-r--r-- | nihil.uuid/CMakeLists.txt | 23 | ||||
| -rw-r--r-- | nihil.uuid/test.cc | 1001 | ||||
| -rw-r--r-- | nihil.uuid/uuid.ccm | 842 |
704 files changed, 15362 insertions, 266 deletions
diff --git a/.clang-format b/.clang-format index 9efb854..c2dc3e0 100644 --- a/.clang-format +++ b/.clang-format @@ -1,45 +1,6 @@ ---- -Language: Cpp -Standard: c++14 - -# Note that we cannot use IncludeIsMainRegex functionality, because it -# does not support includes in angle brackets (<>) -SortIncludes: true -IncludeBlocks: Regroup -IncludeCategories: - - Regex: <catch2/.*\.hpp> - Priority: 1 - - Regex: <.*/.*\.hpp> - Priority: 2 - - Regex: <.*> - Priority: 3 - -AllowShortBlocksOnASingleLine: Always -AllowShortEnumsOnASingleLine: false -AllowShortFunctionsOnASingleLine: All -AllowShortIfStatementsOnASingleLine: WithoutElse -AllowShortLambdasOnASingleLine: Inline - -AccessModifierOffset: "-4" -AlignEscapedNewlines: Left -AllowAllConstructorInitializersOnNextLine: "true" -BinPackArguments: "false" -BinPackParameters: "false" -BreakConstructorInitializers: AfterColon -ConstructorInitializerAllOnOneLineOrOnePerLine: "true" -DerivePointerAlignment: "false" -FixNamespaceComments: "true" -IndentCaseLabels: "false" +TabWidth: 8 +IndentWidth: 8 +PPIndentWidth: -1 +UseTab: AlignWithSpaces IndentPPDirectives: AfterHash -IndentWidth: "4" -NamespaceIndentation: All -PointerAlignment: Left -SpaceBeforeCtorInitializerColon: "false" -SpaceInEmptyParentheses: "false" -SpacesInParentheses: "true" -TabWidth: "4" -UseTab: Never -AlwaysBreakTemplateDeclarations: Yes -SpaceAfterTemplateKeyword: true -SortUsingDeclarations: true -ReflowComments: true +ColumnLimit: 100
\ No newline at end of file @@ -1,40 +1,4 @@ -*.build -!meson.build -*.pbxuser -*.mode1v3 -*.ncb -*.suo -Debug -Release -*.user -*.xcuserstate -.DS_Store -xcuserdata -CatchSelfTest.xcscheme -Breakpoints.xcbkptlist -UpgradeLog.XML -Resources/DWARF -projects/Generated -*.pyc -DerivedData -*.xccheckout -Build -.idea -.vs -.vscode -cmake-build-* -benchmark-dir -.conan/test_package/build -**/CMakeUserPresets.json -bazel-* -MODULE.bazel.lock -build-fuzzers -debug-build -.vscode -msvc-sln* -# Currently we use Doxygen for dep graphs and the full docs are only slowly -# being filled in, so we definitely do not want git to deal with the docs. -docs/doxygen -*.cache -compile_commands.json -**/*.unapproved.txt +/build* +/dist +*.sw? +/.idea diff --git a/CMakeLists.txt b/CMakeLists.txt index 0d8226b..8e8b131 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,198 +1,66 @@ -cmake_minimum_required(VERSION 3.16) +# This source code is released into the public domain. -# detect if Catch is being bundled, -# disable testsuite in that case -if(NOT DEFINED PROJECT_NAME) - set(NOT_SUBPROJECT ON) -else() - set(NOT_SUBPROJECT OFF) -endif() - -set_property(GLOBAL PROPERTY USE_FOLDERS ON) - -option(CATCH_INSTALL_DOCS "Install documentation alongside library" ON) -option(CATCH_INSTALL_EXTRAS "Install extras (CMake scripts, debugger helpers) alongside library" ON) -option(CATCH_DEVELOPMENT_BUILD "Build tests, enable warnings, enable Werror, etc" OFF) -option(CATCH_ENABLE_REPRODUCIBLE_BUILD "Add compiler flags for improving build reproducibility" ON) - -include(CMakeDependentOption) -cmake_dependent_option(CATCH_BUILD_TESTING "Build the SelfTest project" ON "CATCH_DEVELOPMENT_BUILD" OFF) -cmake_dependent_option(CATCH_BUILD_EXAMPLES "Build code examples" OFF "CATCH_DEVELOPMENT_BUILD" OFF) -cmake_dependent_option(CATCH_BUILD_EXTRA_TESTS "Build extra tests" OFF "CATCH_DEVELOPMENT_BUILD" OFF) -cmake_dependent_option(CATCH_BUILD_FUZZERS "Build fuzzers" OFF "CATCH_DEVELOPMENT_BUILD" OFF) -cmake_dependent_option(CATCH_ENABLE_COVERAGE "Generate coverage for codecov.io" OFF "CATCH_DEVELOPMENT_BUILD" OFF) -cmake_dependent_option(CATCH_ENABLE_WERROR "Enables Werror during build" ON "CATCH_DEVELOPMENT_BUILD" OFF) -cmake_dependent_option(CATCH_BUILD_SURROGATES "Enable generating and building surrogate TUs for the main headers" OFF "CATCH_DEVELOPMENT_BUILD" OFF) -cmake_dependent_option(CATCH_ENABLE_CONFIGURE_TESTS "Enable CMake configuration tests. WARNING: VERY EXPENSIVE" OFF "CATCH_DEVELOPMENT_BUILD" OFF) -cmake_dependent_option(CATCH_ENABLE_CMAKE_HELPER_TESTS "Enable CMake helper tests. WARNING: VERY EXPENSIVE" OFF "CATCH_DEVELOPMENT_BUILD" OFF) - - -# Catch2's build breaks if done in-tree. You probably should not build -# things in tree anyway, but we can allow projects that include Catch2 -# as a subproject to build in-tree as long as it is not in our tree. -if (CMAKE_BINARY_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) - message(FATAL_ERROR "Building in-source is not supported! Create a build dir and remove ${CMAKE_SOURCE_DIR}/CMakeCache.txt") -endif() - -project(Catch2 - VERSION 3.8.1 # CML version placeholder, don't delete - LANGUAGES CXX - HOMEPAGE_URL "https://github.com/catchorg/Catch2" - DESCRIPTION "A modern, C++-native, unit test framework." -) - -# Provide path for scripts. We first add path to the scripts we don't use, -# but projects including us might, and set the path up to parent scope. -# Then we also add path that we use to configure the project, but is of -# no use to top level projects. -list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/extras") -if (NOT NOT_SUBPROJECT) - set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" PARENT_SCOPE) -endif() -list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/CMake") - -include(GNUInstallDirs) -include(CMakePackageConfigHelpers) -include(CatchConfigOptions) -if(CATCH_DEVELOPMENT_BUILD) - include(CTest) -endif() +cmake_minimum_required(VERSION 3.28) -# This variable is used in some subdirectories, so we need it here, rather -# than later in the install block -set(CATCH_CMAKE_CONFIG_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Catch2") +project(nihil) -# We have some Windows builds that test `wmain` entry point, -# and we need this change to be present in all binaries that -# are built during these tests, so this is required here, before -# the subdirectories are added. -if(CATCH_TEST_USE_WMAIN) - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /ENTRY:wmainCRTStartup") -endif() +option(NIHIL_CONFIG "Build the nihil.config library" ON) +option(NIHIL_UCL "Build the nihil.ucl library" ON) +option(NIHIL_TESTS "Build nihil's unit tests" ON) +option(NIHIL_TIDY "Run clang-tidy during build" ON) +set(CMAKE_CXX_STANDARD 26) -# Basic paths -set(CATCH_DIR ${CMAKE_CURRENT_SOURCE_DIR}) -set(SOURCES_DIR ${CATCH_DIR}/src/catch2) -set(SELF_TEST_DIR ${CATCH_DIR}/tests/SelfTest) +find_package(PkgConfig REQUIRED) -# We need to bring-in the variables defined there to this scope -add_subdirectory(src) +# clang-tidy support +find_program(CLANG_TIDY clang-tidy) -# Build tests only if requested -if (BUILD_TESTING AND CATCH_BUILD_TESTING AND NOT_SUBPROJECT) - find_package(PythonInterp 3 REQUIRED) - if (NOT PYTHONINTERP_FOUND) - message(FATAL_ERROR "Python not found, but required for tests") - endif() - set(CMAKE_FOLDER "tests") - add_subdirectory(tests) -endif() +if(NOT (CLANG_TIDY STREQUAL "")) + set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -if(CATCH_BUILD_EXAMPLES) - set(CMAKE_FOLDER "Examples") - add_subdirectory(examples) -endif() + file(GLOB_RECURSE NIHIL_SOURCES "*.cc" "*.ccm") + list(FILTER NIHIL_SOURCES EXCLUDE REGEX ${CMAKE_CURRENT_BINARY_DIR}) -if(CATCH_BUILD_EXTRA_TESTS) - set(CMAKE_FOLDER "tests/ExtraTests") - add_subdirectory(tests/ExtraTests) + add_custom_target(tidy COMMAND + ${CLANG_TIDY} -config-file=${CMAKE_CURRENT_SOURCE_DIR}/clang-tidy.conf + -p=${CMAKE_CURRENT_BINARY_DIR} + ${NIHIL_SOURCES}) + + if(NIHIL_TIDY) + set(CMAKE_CXX_CLANG_TIDY + ${CLANG_TIDY}; + -config-file=${CMAKE_CURRENT_SOURCE_DIR}/clang-tidy.conf; + ) + endif() endif() -if(CATCH_BUILD_FUZZERS) - set(CMAKE_FOLDER "fuzzing") - add_subdirectory(fuzzing) +add_compile_options(-W) +add_compile_options(-Wall) +add_compile_options(-Wextra) +add_compile_options(-Werror) +add_compile_options(-Wpedantic) + +add_subdirectory(nihil.cli) +add_subdirectory(nihil.core) +add_subdirectory(nihil.error) +add_subdirectory(nihil.flagset) +add_subdirectory(nihil.generator) +add_subdirectory(nihil.guard) +add_subdirectory(nihil.match) +add_subdirectory(nihil.monad) +add_subdirectory(nihil.posix) +add_subdirectory(nihil.util) +add_subdirectory(nihil.uuid) + +if(NIHIL_UCL) + add_subdirectory(nihil.ucl) endif() -if (CATCH_DEVELOPMENT_BUILD) - add_warnings_to_targets("${CATCH_WARNING_TARGETS}") +if(NIHIL_CONFIG) + add_subdirectory(nihil.config) endif() -# Only perform the installation steps when Catch is not being used as -# a subproject via `add_subdirectory`, or the destinations will break, -# see https://github.com/catchorg/Catch2/issues/1373 -if (NOT_SUBPROJECT) - configure_package_config_file( - ${CMAKE_CURRENT_LIST_DIR}/CMake/Catch2Config.cmake.in - ${CMAKE_CURRENT_BINARY_DIR}/Catch2Config.cmake - INSTALL_DESTINATION - ${CATCH_CMAKE_CONFIG_DESTINATION} - ) - - write_basic_package_version_file( - "${CMAKE_CURRENT_BINARY_DIR}/Catch2ConfigVersion.cmake" - COMPATIBILITY - SameMajorVersion - ) - - install( - FILES - "${CMAKE_CURRENT_BINARY_DIR}/Catch2Config.cmake" - "${CMAKE_CURRENT_BINARY_DIR}/Catch2ConfigVersion.cmake" - DESTINATION - ${CATCH_CMAKE_CONFIG_DESTINATION} - ) - - # Install documentation - if(CATCH_INSTALL_DOCS) - install( - DIRECTORY - docs/ - DESTINATION - "${CMAKE_INSTALL_DOCDIR}" - PATTERN "doxygen" EXCLUDE - ) - endif() - - if(CATCH_INSTALL_EXTRAS) - # Install CMake scripts - install( - FILES - "extras/ParseAndAddCatchTests.cmake" - "extras/Catch.cmake" - "extras/CatchAddTests.cmake" - "extras/CatchShardTests.cmake" - "extras/CatchShardTestsImpl.cmake" - DESTINATION - ${CATCH_CMAKE_CONFIG_DESTINATION} - ) - - # Install debugger helpers - install( - FILES - "extras/gdbinit" - "extras/lldbinit" - DESTINATION - ${CMAKE_INSTALL_DATAROOTDIR}/Catch2 - ) - endif() - - ## Provide some pkg-config integration - set(PKGCONFIG_INSTALL_DIR - "${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig" - CACHE PATH "Path where catch2.pc is installed" - ) - configure_file( - ${CMAKE_CURRENT_SOURCE_DIR}/CMake/catch2.pc.in - ${CMAKE_CURRENT_BINARY_DIR}/catch2.pc - @ONLY - ) - configure_file( - ${CMAKE_CURRENT_SOURCE_DIR}/CMake/catch2-with-main.pc.in - ${CMAKE_CURRENT_BINARY_DIR}/catch2-with-main.pc - @ONLY - ) - install( - FILES - "${CMAKE_CURRENT_BINARY_DIR}/catch2.pc" - "${CMAKE_CURRENT_BINARY_DIR}/catch2-with-main.pc" - DESTINATION - ${PKGCONFIG_INSTALL_DIR} - ) - - set(CPACK_PACKAGE_CONTACT "https://github.com/catchorg/Catch2/") - - - include( CPack ) - +if(NIHIL_TESTS) + enable_testing() endif() @@ -0,0 +1,28 @@ +nihil: C++ utility library +========================== + +nihil is a C++ library which provides various utilities that might be useful +in a C++ program. many of the utilities are specific to FreeBSD. + +i wrote this primarily for my own programs, but you're welcome to use it too. + +license +------- + +all of nihil is in the public domain, with the exception of: + +- nihil/generator.ccm (BSL) +- nihil/monad.ccm (MIT) +- nihil/uuid.ccm (MIT) + +requirements +------------ + ++ FreeBSD ++ a modern C++ compiler. nihil is tested using LLVM 19.x with -std=c++26. + +usage +----- + +nihil is intended to be consumed as a CMake subdirectory. better install +options might be added later. diff --git a/clang-tidy.conf b/clang-tidy.conf new file mode 100644 index 0000000..92a59bc --- /dev/null +++ b/clang-tidy.conf @@ -0,0 +1,8 @@ +--- +WarningsAsErrors: '*' +Checks: > + -*, + bugprone-*, + -bugprone-reserved-identifier, + -bugprone-easily-swappable-parameters, + modernize-*, diff --git a/.bazelrc b/contrib/catch2/.bazelrc index 6084470..6084470 100644 --- a/.bazelrc +++ b/contrib/catch2/.bazelrc diff --git a/contrib/catch2/.clang-format b/contrib/catch2/.clang-format new file mode 100644 index 0000000..9efb854 --- /dev/null +++ b/contrib/catch2/.clang-format @@ -0,0 +1,45 @@ +--- +Language: Cpp +Standard: c++14 + +# Note that we cannot use IncludeIsMainRegex functionality, because it +# does not support includes in angle brackets (<>) +SortIncludes: true +IncludeBlocks: Regroup +IncludeCategories: + - Regex: <catch2/.*\.hpp> + Priority: 1 + - Regex: <.*/.*\.hpp> + Priority: 2 + - Regex: <.*> + Priority: 3 + +AllowShortBlocksOnASingleLine: Always +AllowShortEnumsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: WithoutElse +AllowShortLambdasOnASingleLine: Inline + +AccessModifierOffset: "-4" +AlignEscapedNewlines: Left +AllowAllConstructorInitializersOnNextLine: "true" +BinPackArguments: "false" +BinPackParameters: "false" +BreakConstructorInitializers: AfterColon +ConstructorInitializerAllOnOneLineOrOnePerLine: "true" +DerivePointerAlignment: "false" +FixNamespaceComments: "true" +IndentCaseLabels: "false" +IndentPPDirectives: AfterHash +IndentWidth: "4" +NamespaceIndentation: All +PointerAlignment: Left +SpaceBeforeCtorInitializerColon: "false" +SpaceInEmptyParentheses: "false" +SpacesInParentheses: "true" +TabWidth: "4" +UseTab: Never +AlwaysBreakTemplateDeclarations: Yes +SpaceAfterTemplateKeyword: true +SortUsingDeclarations: true +ReflowComments: true diff --git a/.clang-tidy b/contrib/catch2/.clang-tidy index 3488457..3488457 100644 --- a/.clang-tidy +++ b/contrib/catch2/.clang-tidy diff --git a/.conan/build.py b/contrib/catch2/.conan/build.py index e163d5f..e163d5f 100644 --- a/.conan/build.py +++ b/contrib/catch2/.conan/build.py diff --git a/.conan/test_package/CMakeLists.txt b/contrib/catch2/.conan/test_package/CMakeLists.txt index f067457..f067457 100644 --- a/.conan/test_package/CMakeLists.txt +++ b/contrib/catch2/.conan/test_package/CMakeLists.txt diff --git a/.conan/test_package/conanfile.py b/contrib/catch2/.conan/test_package/conanfile.py index dc03876..dc03876 100644 --- a/.conan/test_package/conanfile.py +++ b/contrib/catch2/.conan/test_package/conanfile.py diff --git a/.conan/test_package/test_package.cpp b/contrib/catch2/.conan/test_package/test_package.cpp index 3c08090..3c08090 100644 --- a/.conan/test_package/test_package.cpp +++ b/contrib/catch2/.conan/test_package/test_package.cpp diff --git a/.gitattributes b/contrib/catch2/.gitattributes index 23f98ff..23f98ff 100644 --- a/.gitattributes +++ b/contrib/catch2/.gitattributes diff --git a/.github/FUNDING.yml b/contrib/catch2/.github/FUNDING.yml index 9122aa8..9122aa8 100644 --- a/.github/FUNDING.yml +++ b/contrib/catch2/.github/FUNDING.yml diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/contrib/catch2/.github/ISSUE_TEMPLATE/bug_report.md index dbeff11..dbeff11 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/contrib/catch2/.github/ISSUE_TEMPLATE/bug_report.md diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/contrib/catch2/.github/ISSUE_TEMPLATE/feature_request.md index be9b9ee..be9b9ee 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/contrib/catch2/.github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/pull_request_template.md b/contrib/catch2/.github/pull_request_template.md index ea2b7bb..ea2b7bb 100644 --- a/.github/pull_request_template.md +++ b/contrib/catch2/.github/pull_request_template.md diff --git a/.github/workflows/linux-bazel-builds.yml b/contrib/catch2/.github/workflows/linux-bazel-builds.yml index dc826ac..dc826ac 100644 --- a/.github/workflows/linux-bazel-builds.yml +++ b/contrib/catch2/.github/workflows/linux-bazel-builds.yml diff --git a/.github/workflows/linux-meson-builds.yml b/contrib/catch2/.github/workflows/linux-meson-builds.yml index 4a6cfd5..4a6cfd5 100644 --- a/.github/workflows/linux-meson-builds.yml +++ b/contrib/catch2/.github/workflows/linux-meson-builds.yml diff --git a/.github/workflows/linux-other-builds.yml b/contrib/catch2/.github/workflows/linux-other-builds.yml index 6993c81..6993c81 100644 --- a/.github/workflows/linux-other-builds.yml +++ b/contrib/catch2/.github/workflows/linux-other-builds.yml diff --git a/.github/workflows/linux-simple-builds.yml b/contrib/catch2/.github/workflows/linux-simple-builds.yml index 4cca316..4cca316 100644 --- a/.github/workflows/linux-simple-builds.yml +++ b/contrib/catch2/.github/workflows/linux-simple-builds.yml diff --git a/.github/workflows/mac-builds-m1.yml b/contrib/catch2/.github/workflows/mac-builds-m1.yml index 45629cd..45629cd 100644 --- a/.github/workflows/mac-builds-m1.yml +++ b/contrib/catch2/.github/workflows/mac-builds-m1.yml diff --git a/.github/workflows/mac-builds.yml b/contrib/catch2/.github/workflows/mac-builds.yml index fb41f5d..fb41f5d 100644 --- a/.github/workflows/mac-builds.yml +++ b/contrib/catch2/.github/workflows/mac-builds.yml diff --git a/.github/workflows/package-manager-builds.yaml b/contrib/catch2/.github/workflows/package-manager-builds.yaml index 6d90d14..6d90d14 100644 --- a/.github/workflows/package-manager-builds.yaml +++ b/contrib/catch2/.github/workflows/package-manager-builds.yaml diff --git a/.github/workflows/validate-header-guards.yml b/contrib/catch2/.github/workflows/validate-header-guards.yml index fa9d157..fa9d157 100644 --- a/.github/workflows/validate-header-guards.yml +++ b/contrib/catch2/.github/workflows/validate-header-guards.yml diff --git a/.github/workflows/windows-simple-builds.yml b/contrib/catch2/.github/workflows/windows-simple-builds.yml index 5fb7b8f..5fb7b8f 100644 --- a/.github/workflows/windows-simple-builds.yml +++ b/contrib/catch2/.github/workflows/windows-simple-builds.yml diff --git a/contrib/catch2/.gitignore b/contrib/catch2/.gitignore new file mode 100644 index 0000000..dbf9f40 --- /dev/null +++ b/contrib/catch2/.gitignore @@ -0,0 +1,40 @@ +*.build +!meson.build +*.pbxuser +*.mode1v3 +*.ncb +*.suo +Debug +Release +*.user +*.xcuserstate +.DS_Store +xcuserdata +CatchSelfTest.xcscheme +Breakpoints.xcbkptlist +UpgradeLog.XML +Resources/DWARF +projects/Generated +*.pyc +DerivedData +*.xccheckout +Build +.idea +.vs +.vscode +cmake-build-* +benchmark-dir +.conan/test_package/build +**/CMakeUserPresets.json +bazel-* +MODULE.bazel.lock +build-fuzzers +debug-build +.vscode +msvc-sln* +# Currently we use Doxygen for dep graphs and the full docs are only slowly +# being filled in, so we definitely do not want git to deal with the docs. +docs/doxygen +*.cache +compile_commands.json +**/*.unapproved.txt diff --git a/BUILD.bazel b/contrib/catch2/BUILD.bazel index d96c7fa..d96c7fa 100644 --- a/BUILD.bazel +++ b/contrib/catch2/BUILD.bazel diff --git a/CMake/Catch2Config.cmake.in b/contrib/catch2/CMake/Catch2Config.cmake.in index c485219..c485219 100644 --- a/CMake/Catch2Config.cmake.in +++ b/contrib/catch2/CMake/Catch2Config.cmake.in diff --git a/CMake/CatchConfigOptions.cmake b/contrib/catch2/CMake/CatchConfigOptions.cmake index a2f2870..a2f2870 100644 --- a/CMake/CatchConfigOptions.cmake +++ b/contrib/catch2/CMake/CatchConfigOptions.cmake diff --git a/CMake/CatchMiscFunctions.cmake b/contrib/catch2/CMake/CatchMiscFunctions.cmake index 05bc83c..05bc83c 100644 --- a/CMake/CatchMiscFunctions.cmake +++ b/contrib/catch2/CMake/CatchMiscFunctions.cmake diff --git a/CMake/FindGcov.cmake b/contrib/catch2/CMake/FindGcov.cmake index 4141711..4141711 100644 --- a/CMake/FindGcov.cmake +++ b/contrib/catch2/CMake/FindGcov.cmake diff --git a/CMake/FindLcov.cmake b/contrib/catch2/CMake/FindLcov.cmake index beb925a..beb925a 100644 --- a/CMake/FindLcov.cmake +++ b/contrib/catch2/CMake/FindLcov.cmake diff --git a/CMake/Findcodecov.cmake b/contrib/catch2/CMake/Findcodecov.cmake index 2c0f2fe..2c0f2fe 100644 --- a/CMake/Findcodecov.cmake +++ b/contrib/catch2/CMake/Findcodecov.cmake diff --git a/CMake/catch2-with-main.pc.in b/contrib/catch2/CMake/catch2-with-main.pc.in index 69a790b..69a790b 100644 --- a/CMake/catch2-with-main.pc.in +++ b/contrib/catch2/CMake/catch2-with-main.pc.in diff --git a/CMake/catch2.pc.in b/contrib/catch2/CMake/catch2.pc.in index bd1c95a..bd1c95a 100644 --- a/CMake/catch2.pc.in +++ b/contrib/catch2/CMake/catch2.pc.in diff --git a/CMake/llvm-cov-wrapper b/contrib/catch2/CMake/llvm-cov-wrapper index 2ac3310..2ac3310 100755 --- a/CMake/llvm-cov-wrapper +++ b/contrib/catch2/CMake/llvm-cov-wrapper diff --git a/contrib/catch2/CMakeLists.txt b/contrib/catch2/CMakeLists.txt new file mode 100644 index 0000000..0d8226b --- /dev/null +++ b/contrib/catch2/CMakeLists.txt @@ -0,0 +1,198 @@ +cmake_minimum_required(VERSION 3.16) + +# detect if Catch is being bundled, +# disable testsuite in that case +if(NOT DEFINED PROJECT_NAME) + set(NOT_SUBPROJECT ON) +else() + set(NOT_SUBPROJECT OFF) +endif() + +set_property(GLOBAL PROPERTY USE_FOLDERS ON) + +option(CATCH_INSTALL_DOCS "Install documentation alongside library" ON) +option(CATCH_INSTALL_EXTRAS "Install extras (CMake scripts, debugger helpers) alongside library" ON) +option(CATCH_DEVELOPMENT_BUILD "Build tests, enable warnings, enable Werror, etc" OFF) +option(CATCH_ENABLE_REPRODUCIBLE_BUILD "Add compiler flags for improving build reproducibility" ON) + +include(CMakeDependentOption) +cmake_dependent_option(CATCH_BUILD_TESTING "Build the SelfTest project" ON "CATCH_DEVELOPMENT_BUILD" OFF) +cmake_dependent_option(CATCH_BUILD_EXAMPLES "Build code examples" OFF "CATCH_DEVELOPMENT_BUILD" OFF) +cmake_dependent_option(CATCH_BUILD_EXTRA_TESTS "Build extra tests" OFF "CATCH_DEVELOPMENT_BUILD" OFF) +cmake_dependent_option(CATCH_BUILD_FUZZERS "Build fuzzers" OFF "CATCH_DEVELOPMENT_BUILD" OFF) +cmake_dependent_option(CATCH_ENABLE_COVERAGE "Generate coverage for codecov.io" OFF "CATCH_DEVELOPMENT_BUILD" OFF) +cmake_dependent_option(CATCH_ENABLE_WERROR "Enables Werror during build" ON "CATCH_DEVELOPMENT_BUILD" OFF) +cmake_dependent_option(CATCH_BUILD_SURROGATES "Enable generating and building surrogate TUs for the main headers" OFF "CATCH_DEVELOPMENT_BUILD" OFF) +cmake_dependent_option(CATCH_ENABLE_CONFIGURE_TESTS "Enable CMake configuration tests. WARNING: VERY EXPENSIVE" OFF "CATCH_DEVELOPMENT_BUILD" OFF) +cmake_dependent_option(CATCH_ENABLE_CMAKE_HELPER_TESTS "Enable CMake helper tests. WARNING: VERY EXPENSIVE" OFF "CATCH_DEVELOPMENT_BUILD" OFF) + + +# Catch2's build breaks if done in-tree. You probably should not build +# things in tree anyway, but we can allow projects that include Catch2 +# as a subproject to build in-tree as long as it is not in our tree. +if (CMAKE_BINARY_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) + message(FATAL_ERROR "Building in-source is not supported! Create a build dir and remove ${CMAKE_SOURCE_DIR}/CMakeCache.txt") +endif() + +project(Catch2 + VERSION 3.8.1 # CML version placeholder, don't delete + LANGUAGES CXX + HOMEPAGE_URL "https://github.com/catchorg/Catch2" + DESCRIPTION "A modern, C++-native, unit test framework." +) + +# Provide path for scripts. We first add path to the scripts we don't use, +# but projects including us might, and set the path up to parent scope. +# Then we also add path that we use to configure the project, but is of +# no use to top level projects. +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/extras") +if (NOT NOT_SUBPROJECT) + set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" PARENT_SCOPE) +endif() +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/CMake") + +include(GNUInstallDirs) +include(CMakePackageConfigHelpers) +include(CatchConfigOptions) +if(CATCH_DEVELOPMENT_BUILD) + include(CTest) +endif() + +# This variable is used in some subdirectories, so we need it here, rather +# than later in the install block +set(CATCH_CMAKE_CONFIG_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Catch2") + +# We have some Windows builds that test `wmain` entry point, +# and we need this change to be present in all binaries that +# are built during these tests, so this is required here, before +# the subdirectories are added. +if(CATCH_TEST_USE_WMAIN) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /ENTRY:wmainCRTStartup") +endif() + + +# Basic paths +set(CATCH_DIR ${CMAKE_CURRENT_SOURCE_DIR}) +set(SOURCES_DIR ${CATCH_DIR}/src/catch2) +set(SELF_TEST_DIR ${CATCH_DIR}/tests/SelfTest) + +# We need to bring-in the variables defined there to this scope +add_subdirectory(src) + +# Build tests only if requested +if (BUILD_TESTING AND CATCH_BUILD_TESTING AND NOT_SUBPROJECT) + find_package(PythonInterp 3 REQUIRED) + if (NOT PYTHONINTERP_FOUND) + message(FATAL_ERROR "Python not found, but required for tests") + endif() + set(CMAKE_FOLDER "tests") + add_subdirectory(tests) +endif() + +if(CATCH_BUILD_EXAMPLES) + set(CMAKE_FOLDER "Examples") + add_subdirectory(examples) +endif() + +if(CATCH_BUILD_EXTRA_TESTS) + set(CMAKE_FOLDER "tests/ExtraTests") + add_subdirectory(tests/ExtraTests) +endif() + +if(CATCH_BUILD_FUZZERS) + set(CMAKE_FOLDER "fuzzing") + add_subdirectory(fuzzing) +endif() + +if (CATCH_DEVELOPMENT_BUILD) + add_warnings_to_targets("${CATCH_WARNING_TARGETS}") +endif() + +# Only perform the installation steps when Catch is not being used as +# a subproject via `add_subdirectory`, or the destinations will break, +# see https://github.com/catchorg/Catch2/issues/1373 +if (NOT_SUBPROJECT) + configure_package_config_file( + ${CMAKE_CURRENT_LIST_DIR}/CMake/Catch2Config.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/Catch2Config.cmake + INSTALL_DESTINATION + ${CATCH_CMAKE_CONFIG_DESTINATION} + ) + + write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/Catch2ConfigVersion.cmake" + COMPATIBILITY + SameMajorVersion + ) + + install( + FILES + "${CMAKE_CURRENT_BINARY_DIR}/Catch2Config.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/Catch2ConfigVersion.cmake" + DESTINATION + ${CATCH_CMAKE_CONFIG_DESTINATION} + ) + + # Install documentation + if(CATCH_INSTALL_DOCS) + install( + DIRECTORY + docs/ + DESTINATION + "${CMAKE_INSTALL_DOCDIR}" + PATTERN "doxygen" EXCLUDE + ) + endif() + + if(CATCH_INSTALL_EXTRAS) + # Install CMake scripts + install( + FILES + "extras/ParseAndAddCatchTests.cmake" + "extras/Catch.cmake" + "extras/CatchAddTests.cmake" + "extras/CatchShardTests.cmake" + "extras/CatchShardTestsImpl.cmake" + DESTINATION + ${CATCH_CMAKE_CONFIG_DESTINATION} + ) + + # Install debugger helpers + install( + FILES + "extras/gdbinit" + "extras/lldbinit" + DESTINATION + ${CMAKE_INSTALL_DATAROOTDIR}/Catch2 + ) + endif() + + ## Provide some pkg-config integration + set(PKGCONFIG_INSTALL_DIR + "${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig" + CACHE PATH "Path where catch2.pc is installed" + ) + configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/CMake/catch2.pc.in + ${CMAKE_CURRENT_BINARY_DIR}/catch2.pc + @ONLY + ) + configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/CMake/catch2-with-main.pc.in + ${CMAKE_CURRENT_BINARY_DIR}/catch2-with-main.pc + @ONLY + ) + install( + FILES + "${CMAKE_CURRENT_BINARY_DIR}/catch2.pc" + "${CMAKE_CURRENT_BINARY_DIR}/catch2-with-main.pc" + DESTINATION + ${PKGCONFIG_INSTALL_DIR} + ) + + set(CPACK_PACKAGE_CONTACT "https://github.com/catchorg/Catch2/") + + + include( CPack ) + +endif() diff --git a/CMakePresets.json b/contrib/catch2/CMakePresets.json index 8854128..8854128 100644 --- a/CMakePresets.json +++ b/contrib/catch2/CMakePresets.json diff --git a/CODE_OF_CONDUCT.md b/contrib/catch2/CODE_OF_CONDUCT.md index be1a688..be1a688 100644 --- a/CODE_OF_CONDUCT.md +++ b/contrib/catch2/CODE_OF_CONDUCT.md diff --git a/Doxyfile b/contrib/catch2/Doxyfile index 914e598..914e598 100644 --- a/Doxyfile +++ b/contrib/catch2/Doxyfile diff --git a/LICENSE.txt b/contrib/catch2/LICENSE.txt index 36b7cd9..36b7cd9 100644 --- a/LICENSE.txt +++ b/contrib/catch2/LICENSE.txt diff --git a/MODULE.bazel b/contrib/catch2/MODULE.bazel index 628d77a..628d77a 100644 --- a/MODULE.bazel +++ b/contrib/catch2/MODULE.bazel diff --git a/README.md b/contrib/catch2/README.md index 3ea54a5..3ea54a5 100644 --- a/README.md +++ b/contrib/catch2/README.md diff --git a/SECURITY.md b/contrib/catch2/SECURITY.md index b66bf73..b66bf73 100644 --- a/SECURITY.md +++ b/contrib/catch2/SECURITY.md diff --git a/appveyor.yml b/contrib/catch2/appveyor.yml index ba4556e..ba4556e 100644 --- a/appveyor.yml +++ b/contrib/catch2/appveyor.yml diff --git a/codecov.yml b/contrib/catch2/codecov.yml index 357cb5e..357cb5e 100644 --- a/codecov.yml +++ b/contrib/catch2/codecov.yml diff --git a/conanfile.py b/contrib/catch2/conanfile.py index 7a3ac7c..7a3ac7c 100755 --- a/conanfile.py +++ b/contrib/catch2/conanfile.py diff --git a/data/artwork/catch2-c-logo.png b/contrib/catch2/data/artwork/catch2-c-logo.png Binary files differindex b1066b8..b1066b8 100644 --- a/data/artwork/catch2-c-logo.png +++ b/contrib/catch2/data/artwork/catch2-c-logo.png diff --git a/data/artwork/catch2-hand-logo.png b/contrib/catch2/data/artwork/catch2-hand-logo.png Binary files differindex ab857ea..ab857ea 100644 --- a/data/artwork/catch2-hand-logo.png +++ b/contrib/catch2/data/artwork/catch2-hand-logo.png diff --git a/data/artwork/catch2-logo-small-with-background.png b/contrib/catch2/data/artwork/catch2-logo-small-with-background.png Binary files differindex 963eaf4..963eaf4 100644 --- a/data/artwork/catch2-logo-small-with-background.png +++ b/contrib/catch2/data/artwork/catch2-logo-small-with-background.png diff --git a/data/artwork/catch2-logo-small.png b/contrib/catch2/data/artwork/catch2-logo-small.png Binary files differindex 742e81e..742e81e 100644 --- a/data/artwork/catch2-logo-small.png +++ b/contrib/catch2/data/artwork/catch2-logo-small.png diff --git a/docs/Readme.md b/contrib/catch2/docs/Readme.md index d84b4bf..d84b4bf 100644 --- a/docs/Readme.md +++ b/contrib/catch2/docs/Readme.md diff --git a/docs/assertions.md b/contrib/catch2/docs/assertions.md index f3dcdd4..f3dcdd4 100644 --- a/docs/assertions.md +++ b/contrib/catch2/docs/assertions.md diff --git a/docs/benchmarks.md b/contrib/catch2/docs/benchmarks.md index 9edbb93..9edbb93 100644 --- a/docs/benchmarks.md +++ b/contrib/catch2/docs/benchmarks.md diff --git a/docs/ci-and-misc.md b/contrib/catch2/docs/ci-and-misc.md index 49bbd98..49bbd98 100644 --- a/docs/ci-and-misc.md +++ b/contrib/catch2/docs/ci-and-misc.md diff --git a/docs/cmake-integration.md b/contrib/catch2/docs/cmake-integration.md index cfb9853..cfb9853 100644 --- a/docs/cmake-integration.md +++ b/contrib/catch2/docs/cmake-integration.md diff --git a/docs/command-line.md b/contrib/catch2/docs/command-line.md index 7e69bf1..7e69bf1 100644 --- a/docs/command-line.md +++ b/contrib/catch2/docs/command-line.md diff --git a/docs/commercial-users.md b/contrib/catch2/docs/commercial-users.md index 020eaef..020eaef 100644 --- a/docs/commercial-users.md +++ b/contrib/catch2/docs/commercial-users.md diff --git a/docs/comparing-floating-point-numbers.md b/contrib/catch2/docs/comparing-floating-point-numbers.md index ab5ba6d..ab5ba6d 100644 --- a/docs/comparing-floating-point-numbers.md +++ b/contrib/catch2/docs/comparing-floating-point-numbers.md diff --git a/docs/configuration.md b/contrib/catch2/docs/configuration.md index f268ba5..f268ba5 100644 --- a/docs/configuration.md +++ b/contrib/catch2/docs/configuration.md diff --git a/docs/contributing.md b/contrib/catch2/docs/contributing.md index d21323d..d21323d 100644 --- a/docs/contributing.md +++ b/contrib/catch2/docs/contributing.md diff --git a/docs/deprecations.md b/contrib/catch2/docs/deprecations.md index 0b5bee1..0b5bee1 100644 --- a/docs/deprecations.md +++ b/contrib/catch2/docs/deprecations.md diff --git a/docs/event-listeners.md b/contrib/catch2/docs/event-listeners.md index 71db3e1..71db3e1 100644 --- a/docs/event-listeners.md +++ b/contrib/catch2/docs/event-listeners.md diff --git a/docs/faq.md b/contrib/catch2/docs/faq.md index 80923d2..80923d2 100644 --- a/docs/faq.md +++ b/contrib/catch2/docs/faq.md diff --git a/docs/generators.md b/contrib/catch2/docs/generators.md index eb1a255..eb1a255 100644 --- a/docs/generators.md +++ b/contrib/catch2/docs/generators.md diff --git a/docs/limitations.md b/contrib/catch2/docs/limitations.md index f5f60ba..f5f60ba 100644 --- a/docs/limitations.md +++ b/contrib/catch2/docs/limitations.md diff --git a/docs/list-of-examples.md b/contrib/catch2/docs/list-of-examples.md index 40d3f71..40d3f71 100644 --- a/docs/list-of-examples.md +++ b/contrib/catch2/docs/list-of-examples.md diff --git a/docs/logging.md b/contrib/catch2/docs/logging.md index 7970938..7970938 100644 --- a/docs/logging.md +++ b/contrib/catch2/docs/logging.md diff --git a/docs/matchers.md b/contrib/catch2/docs/matchers.md index 4b9445a..4b9445a 100644 --- a/docs/matchers.md +++ b/contrib/catch2/docs/matchers.md diff --git a/docs/migrate-v2-to-v3.md b/contrib/catch2/docs/migrate-v2-to-v3.md index 84ed769..84ed769 100644 --- a/docs/migrate-v2-to-v3.md +++ b/contrib/catch2/docs/migrate-v2-to-v3.md diff --git a/docs/opensource-users.md b/contrib/catch2/docs/opensource-users.md index a02d0b9..a02d0b9 100644 --- a/docs/opensource-users.md +++ b/contrib/catch2/docs/opensource-users.md diff --git a/docs/other-macros.md b/contrib/catch2/docs/other-macros.md index 79990a6..79990a6 100644 --- a/docs/other-macros.md +++ b/contrib/catch2/docs/other-macros.md diff --git a/docs/own-main.md b/contrib/catch2/docs/own-main.md index 26dfa86..26dfa86 100644 --- a/docs/own-main.md +++ b/contrib/catch2/docs/own-main.md diff --git a/docs/release-notes.md b/contrib/catch2/docs/release-notes.md index a393a52..a393a52 100644 --- a/docs/release-notes.md +++ b/contrib/catch2/docs/release-notes.md diff --git a/docs/release-process.md b/contrib/catch2/docs/release-process.md index 12d23b7..12d23b7 100644 --- a/docs/release-process.md +++ b/contrib/catch2/docs/release-process.md diff --git a/docs/reporter-events.md b/contrib/catch2/docs/reporter-events.md index 015f67b..015f67b 100644 --- a/docs/reporter-events.md +++ b/contrib/catch2/docs/reporter-events.md diff --git a/docs/reporters.md b/contrib/catch2/docs/reporters.md index 20ef5e5..20ef5e5 100644 --- a/docs/reporters.md +++ b/contrib/catch2/docs/reporters.md diff --git a/docs/skipping-passing-failing.md b/contrib/catch2/docs/skipping-passing-failing.md index 52bb18f..52bb18f 100644 --- a/docs/skipping-passing-failing.md +++ b/contrib/catch2/docs/skipping-passing-failing.md diff --git a/docs/test-cases-and-sections.md b/contrib/catch2/docs/test-cases-and-sections.md index 14db55a..14db55a 100644 --- a/docs/test-cases-and-sections.md +++ b/contrib/catch2/docs/test-cases-and-sections.md diff --git a/docs/test-fixtures.md b/contrib/catch2/docs/test-fixtures.md index 653b50e..653b50e 100644 --- a/docs/test-fixtures.md +++ b/contrib/catch2/docs/test-fixtures.md diff --git a/docs/tostring.md b/contrib/catch2/docs/tostring.md index 513c1b4..513c1b4 100644 --- a/docs/tostring.md +++ b/contrib/catch2/docs/tostring.md diff --git a/docs/tutorial.md b/contrib/catch2/docs/tutorial.md index fb5a5b3..fb5a5b3 100644 --- a/docs/tutorial.md +++ b/contrib/catch2/docs/tutorial.md diff --git a/docs/usage-tips.md b/contrib/catch2/docs/usage-tips.md index 6be01ee..6be01ee 100644 --- a/docs/usage-tips.md +++ b/contrib/catch2/docs/usage-tips.md diff --git a/docs/why-catch.md b/contrib/catch2/docs/why-catch.md index b736749..b736749 100644 --- a/docs/why-catch.md +++ b/contrib/catch2/docs/why-catch.md diff --git a/examples/010-TestCase.cpp b/contrib/catch2/examples/010-TestCase.cpp index 9e5cd8c..9e5cd8c 100644 --- a/examples/010-TestCase.cpp +++ b/contrib/catch2/examples/010-TestCase.cpp diff --git a/examples/020-TestCase-1.cpp b/contrib/catch2/examples/020-TestCase-1.cpp index a9d87db..a9d87db 100644 --- a/examples/020-TestCase-1.cpp +++ b/contrib/catch2/examples/020-TestCase-1.cpp diff --git a/examples/020-TestCase-2.cpp b/contrib/catch2/examples/020-TestCase-2.cpp index 72dd0ff..72dd0ff 100644 --- a/examples/020-TestCase-2.cpp +++ b/contrib/catch2/examples/020-TestCase-2.cpp diff --git a/examples/030-Asn-Require-Check.cpp b/contrib/catch2/examples/030-Asn-Require-Check.cpp index 62cd3cf..62cd3cf 100644 --- a/examples/030-Asn-Require-Check.cpp +++ b/contrib/catch2/examples/030-Asn-Require-Check.cpp diff --git a/examples/100-Fix-Section.cpp b/contrib/catch2/examples/100-Fix-Section.cpp index 7c8d8aa..7c8d8aa 100644 --- a/examples/100-Fix-Section.cpp +++ b/contrib/catch2/examples/100-Fix-Section.cpp diff --git a/examples/110-Fix-ClassFixture.cpp b/contrib/catch2/examples/110-Fix-ClassFixture.cpp index 614c379..614c379 100644 --- a/examples/110-Fix-ClassFixture.cpp +++ b/contrib/catch2/examples/110-Fix-ClassFixture.cpp diff --git a/examples/111-Fix-PersistentFixture.cpp b/contrib/catch2/examples/111-Fix-PersistentFixture.cpp index 2bef90f..2bef90f 100644 --- a/examples/111-Fix-PersistentFixture.cpp +++ b/contrib/catch2/examples/111-Fix-PersistentFixture.cpp diff --git a/examples/120-Bdd-ScenarioGivenWhenThen.cpp b/contrib/catch2/examples/120-Bdd-ScenarioGivenWhenThen.cpp index 345d53c..345d53c 100644 --- a/examples/120-Bdd-ScenarioGivenWhenThen.cpp +++ b/contrib/catch2/examples/120-Bdd-ScenarioGivenWhenThen.cpp diff --git a/examples/210-Evt-EventListeners.cpp b/contrib/catch2/examples/210-Evt-EventListeners.cpp index d05dfaa..d05dfaa 100644 --- a/examples/210-Evt-EventListeners.cpp +++ b/contrib/catch2/examples/210-Evt-EventListeners.cpp diff --git a/examples/231-Cfg-OutputStreams.cpp b/contrib/catch2/examples/231-Cfg-OutputStreams.cpp index 5aee38b..5aee38b 100644 --- a/examples/231-Cfg-OutputStreams.cpp +++ b/contrib/catch2/examples/231-Cfg-OutputStreams.cpp diff --git a/examples/232-Cfg-CustomMain.cpp b/contrib/catch2/examples/232-Cfg-CustomMain.cpp index 2704099..2704099 100644 --- a/examples/232-Cfg-CustomMain.cpp +++ b/contrib/catch2/examples/232-Cfg-CustomMain.cpp diff --git a/examples/300-Gen-OwnGenerator.cpp b/contrib/catch2/examples/300-Gen-OwnGenerator.cpp index 9cb02e3..9cb02e3 100644 --- a/examples/300-Gen-OwnGenerator.cpp +++ b/contrib/catch2/examples/300-Gen-OwnGenerator.cpp diff --git a/examples/301-Gen-MapTypeConversion.cpp b/contrib/catch2/examples/301-Gen-MapTypeConversion.cpp index 0a28448..0a28448 100644 --- a/examples/301-Gen-MapTypeConversion.cpp +++ b/contrib/catch2/examples/301-Gen-MapTypeConversion.cpp diff --git a/examples/302-Gen-Table.cpp b/contrib/catch2/examples/302-Gen-Table.cpp index 3cdb143..3cdb143 100644 --- a/examples/302-Gen-Table.cpp +++ b/contrib/catch2/examples/302-Gen-Table.cpp diff --git a/examples/310-Gen-VariablesInGenerators.cpp b/contrib/catch2/examples/310-Gen-VariablesInGenerators.cpp index 5d24d45..5d24d45 100644 --- a/examples/310-Gen-VariablesInGenerators.cpp +++ b/contrib/catch2/examples/310-Gen-VariablesInGenerators.cpp diff --git a/examples/311-Gen-CustomCapture.cpp b/contrib/catch2/examples/311-Gen-CustomCapture.cpp index ee31038..ee31038 100644 --- a/examples/311-Gen-CustomCapture.cpp +++ b/contrib/catch2/examples/311-Gen-CustomCapture.cpp diff --git a/examples/CMakeLists.txt b/contrib/catch2/examples/CMakeLists.txt index d45ddfc..d45ddfc 100644 --- a/examples/CMakeLists.txt +++ b/contrib/catch2/examples/CMakeLists.txt diff --git a/extras/Catch.cmake b/contrib/catch2/extras/Catch.cmake index 3d93fe2..3d93fe2 100644 --- a/extras/Catch.cmake +++ b/contrib/catch2/extras/Catch.cmake diff --git a/extras/CatchAddTests.cmake b/contrib/catch2/extras/CatchAddTests.cmake index 2534213..2534213 100644 --- a/extras/CatchAddTests.cmake +++ b/contrib/catch2/extras/CatchAddTests.cmake diff --git a/extras/CatchShardTests.cmake b/contrib/catch2/extras/CatchShardTests.cmake index 68228f5..68228f5 100644 --- a/extras/CatchShardTests.cmake +++ b/contrib/catch2/extras/CatchShardTests.cmake diff --git a/extras/CatchShardTestsImpl.cmake b/contrib/catch2/extras/CatchShardTestsImpl.cmake index bb2fc3e..bb2fc3e 100644 --- a/extras/CatchShardTestsImpl.cmake +++ b/contrib/catch2/extras/CatchShardTestsImpl.cmake diff --git a/extras/ParseAndAddCatchTests.cmake b/contrib/catch2/extras/ParseAndAddCatchTests.cmake index 31fc193..31fc193 100644 --- a/extras/ParseAndAddCatchTests.cmake +++ b/contrib/catch2/extras/ParseAndAddCatchTests.cmake diff --git a/extras/catch_amalgamated.cpp b/contrib/catch2/extras/catch_amalgamated.cpp index b979eb2..b979eb2 100644 --- a/extras/catch_amalgamated.cpp +++ b/contrib/catch2/extras/catch_amalgamated.cpp diff --git a/extras/catch_amalgamated.hpp b/contrib/catch2/extras/catch_amalgamated.hpp index 7703e95..7703e95 100644 --- a/extras/catch_amalgamated.hpp +++ b/contrib/catch2/extras/catch_amalgamated.hpp diff --git a/extras/gdbinit b/contrib/catch2/extras/gdbinit index fb3608a..fb3608a 100644 --- a/extras/gdbinit +++ b/contrib/catch2/extras/gdbinit diff --git a/extras/lldbinit b/contrib/catch2/extras/lldbinit index 4f13634..4f13634 100644 --- a/extras/lldbinit +++ b/contrib/catch2/extras/lldbinit diff --git a/fuzzing/CMakeLists.txt b/contrib/catch2/fuzzing/CMakeLists.txt index daba61d..daba61d 100644 --- a/fuzzing/CMakeLists.txt +++ b/contrib/catch2/fuzzing/CMakeLists.txt diff --git a/fuzzing/NullOStream.cpp b/contrib/catch2/fuzzing/NullOStream.cpp index e3a181e..e3a181e 100644 --- a/fuzzing/NullOStream.cpp +++ b/contrib/catch2/fuzzing/NullOStream.cpp diff --git a/fuzzing/NullOStream.h b/contrib/catch2/fuzzing/NullOStream.h index abbec09..abbec09 100644 --- a/fuzzing/NullOStream.h +++ b/contrib/catch2/fuzzing/NullOStream.h diff --git a/fuzzing/build_fuzzers.sh b/contrib/catch2/fuzzing/build_fuzzers.sh index 9788c68..9788c68 100755 --- a/fuzzing/build_fuzzers.sh +++ b/contrib/catch2/fuzzing/build_fuzzers.sh diff --git a/fuzzing/fuzz_TestSpecParser.cpp b/contrib/catch2/fuzzing/fuzz_TestSpecParser.cpp index 3aba8c8..3aba8c8 100644 --- a/fuzzing/fuzz_TestSpecParser.cpp +++ b/contrib/catch2/fuzzing/fuzz_TestSpecParser.cpp diff --git a/fuzzing/fuzz_XmlWriter.cpp b/contrib/catch2/fuzzing/fuzz_XmlWriter.cpp index 70c4ed8..70c4ed8 100644 --- a/fuzzing/fuzz_XmlWriter.cpp +++ b/contrib/catch2/fuzzing/fuzz_XmlWriter.cpp diff --git a/fuzzing/fuzz_textflow.cpp b/contrib/catch2/fuzzing/fuzz_textflow.cpp index 7000f42..7000f42 100644 --- a/fuzzing/fuzz_textflow.cpp +++ b/contrib/catch2/fuzzing/fuzz_textflow.cpp diff --git a/mdsnippets.json b/contrib/catch2/mdsnippets.json index 5a60dad..5a60dad 100644 --- a/mdsnippets.json +++ b/contrib/catch2/mdsnippets.json diff --git a/meson.build b/contrib/catch2/meson.build index 6346d44..6346d44 100644 --- a/meson.build +++ b/contrib/catch2/meson.build diff --git a/meson_options.txt b/contrib/catch2/meson_options.txt index b460f2d..b460f2d 100644 --- a/meson_options.txt +++ b/contrib/catch2/meson_options.txt diff --git a/src/CMakeLists.txt b/contrib/catch2/src/CMakeLists.txt index c40de04..c40de04 100644 --- a/src/CMakeLists.txt +++ b/contrib/catch2/src/CMakeLists.txt diff --git a/src/catch2/benchmark/catch_benchmark.hpp b/contrib/catch2/src/catch2/benchmark/catch_benchmark.hpp index d0f88cf..d0f88cf 100644 --- a/src/catch2/benchmark/catch_benchmark.hpp +++ b/contrib/catch2/src/catch2/benchmark/catch_benchmark.hpp diff --git a/src/catch2/benchmark/catch_benchmark_all.hpp b/contrib/catch2/src/catch2/benchmark/catch_benchmark_all.hpp index 56fc7c7..56fc7c7 100644 --- a/src/catch2/benchmark/catch_benchmark_all.hpp +++ b/contrib/catch2/src/catch2/benchmark/catch_benchmark_all.hpp diff --git a/src/catch2/benchmark/catch_chronometer.cpp b/contrib/catch2/src/catch2/benchmark/catch_chronometer.cpp index 92f03c9..92f03c9 100644 --- a/src/catch2/benchmark/catch_chronometer.cpp +++ b/contrib/catch2/src/catch2/benchmark/catch_chronometer.cpp diff --git a/src/catch2/benchmark/catch_chronometer.hpp b/contrib/catch2/src/catch2/benchmark/catch_chronometer.hpp index 95498e6..95498e6 100644 --- a/src/catch2/benchmark/catch_chronometer.hpp +++ b/contrib/catch2/src/catch2/benchmark/catch_chronometer.hpp diff --git a/src/catch2/benchmark/catch_clock.hpp b/contrib/catch2/src/catch2/benchmark/catch_clock.hpp index 4068c4d..4068c4d 100644 --- a/src/catch2/benchmark/catch_clock.hpp +++ b/contrib/catch2/src/catch2/benchmark/catch_clock.hpp diff --git a/src/catch2/benchmark/catch_constructor.hpp b/contrib/catch2/src/catch2/benchmark/catch_constructor.hpp index 853bd6c..853bd6c 100644 --- a/src/catch2/benchmark/catch_constructor.hpp +++ b/contrib/catch2/src/catch2/benchmark/catch_constructor.hpp diff --git a/src/catch2/benchmark/catch_environment.hpp b/contrib/catch2/src/catch2/benchmark/catch_environment.hpp index da3f2fa..da3f2fa 100644 --- a/src/catch2/benchmark/catch_environment.hpp +++ b/contrib/catch2/src/catch2/benchmark/catch_environment.hpp diff --git a/src/catch2/benchmark/catch_estimate.hpp b/contrib/catch2/src/catch2/benchmark/catch_estimate.hpp index 64383a2..64383a2 100644 --- a/src/catch2/benchmark/catch_estimate.hpp +++ b/contrib/catch2/src/catch2/benchmark/catch_estimate.hpp diff --git a/src/catch2/benchmark/catch_execution_plan.hpp b/contrib/catch2/src/catch2/benchmark/catch_execution_plan.hpp index 17ca589..17ca589 100644 --- a/src/catch2/benchmark/catch_execution_plan.hpp +++ b/contrib/catch2/src/catch2/benchmark/catch_execution_plan.hpp diff --git a/src/catch2/benchmark/catch_optimizer.hpp b/contrib/catch2/src/catch2/benchmark/catch_optimizer.hpp index 61e6571..61e6571 100644 --- a/src/catch2/benchmark/catch_optimizer.hpp +++ b/contrib/catch2/src/catch2/benchmark/catch_optimizer.hpp diff --git a/src/catch2/benchmark/catch_outlier_classification.hpp b/contrib/catch2/src/catch2/benchmark/catch_outlier_classification.hpp index b9a1178..b9a1178 100644 --- a/src/catch2/benchmark/catch_outlier_classification.hpp +++ b/contrib/catch2/src/catch2/benchmark/catch_outlier_classification.hpp diff --git a/src/catch2/benchmark/catch_sample_analysis.hpp b/contrib/catch2/src/catch2/benchmark/catch_sample_analysis.hpp index aeb87d0..aeb87d0 100644 --- a/src/catch2/benchmark/catch_sample_analysis.hpp +++ b/contrib/catch2/src/catch2/benchmark/catch_sample_analysis.hpp diff --git a/src/catch2/benchmark/detail/catch_analyse.cpp b/contrib/catch2/src/catch2/benchmark/detail/catch_analyse.cpp index 14d7f45..14d7f45 100644 --- a/src/catch2/benchmark/detail/catch_analyse.cpp +++ b/contrib/catch2/src/catch2/benchmark/detail/catch_analyse.cpp diff --git a/src/catch2/benchmark/detail/catch_analyse.hpp b/contrib/catch2/src/catch2/benchmark/detail/catch_analyse.hpp index 5e3f7b0..5e3f7b0 100644 --- a/src/catch2/benchmark/detail/catch_analyse.hpp +++ b/contrib/catch2/src/catch2/benchmark/detail/catch_analyse.hpp diff --git a/src/catch2/benchmark/detail/catch_benchmark_function.cpp b/contrib/catch2/src/catch2/benchmark/detail/catch_benchmark_function.cpp index 66d4e61..66d4e61 100644 --- a/src/catch2/benchmark/detail/catch_benchmark_function.cpp +++ b/contrib/catch2/src/catch2/benchmark/detail/catch_benchmark_function.cpp diff --git a/src/catch2/benchmark/detail/catch_benchmark_function.hpp b/contrib/catch2/src/catch2/benchmark/detail/catch_benchmark_function.hpp index a03cb11..a03cb11 100644 --- a/src/catch2/benchmark/detail/catch_benchmark_function.hpp +++ b/contrib/catch2/src/catch2/benchmark/detail/catch_benchmark_function.hpp diff --git a/src/catch2/benchmark/detail/catch_benchmark_stats.hpp b/contrib/catch2/src/catch2/benchmark/detail/catch_benchmark_stats.hpp index 3633bc9..3633bc9 100644 --- a/src/catch2/benchmark/detail/catch_benchmark_stats.hpp +++ b/contrib/catch2/src/catch2/benchmark/detail/catch_benchmark_stats.hpp diff --git a/src/catch2/benchmark/detail/catch_benchmark_stats_fwd.hpp b/contrib/catch2/src/catch2/benchmark/detail/catch_benchmark_stats_fwd.hpp index 2ccc25d..2ccc25d 100644 --- a/src/catch2/benchmark/detail/catch_benchmark_stats_fwd.hpp +++ b/contrib/catch2/src/catch2/benchmark/detail/catch_benchmark_stats_fwd.hpp diff --git a/src/catch2/benchmark/detail/catch_complete_invoke.hpp b/contrib/catch2/src/catch2/benchmark/detail/catch_complete_invoke.hpp index 4dff4b7..4dff4b7 100644 --- a/src/catch2/benchmark/detail/catch_complete_invoke.hpp +++ b/contrib/catch2/src/catch2/benchmark/detail/catch_complete_invoke.hpp diff --git a/src/catch2/benchmark/detail/catch_estimate_clock.hpp b/contrib/catch2/src/catch2/benchmark/detail/catch_estimate_clock.hpp index 6da24ce..6da24ce 100644 --- a/src/catch2/benchmark/detail/catch_estimate_clock.hpp +++ b/contrib/catch2/src/catch2/benchmark/detail/catch_estimate_clock.hpp diff --git a/src/catch2/benchmark/detail/catch_measure.hpp b/contrib/catch2/src/catch2/benchmark/detail/catch_measure.hpp index a804907..a804907 100644 --- a/src/catch2/benchmark/detail/catch_measure.hpp +++ b/contrib/catch2/src/catch2/benchmark/detail/catch_measure.hpp diff --git a/src/catch2/benchmark/detail/catch_repeat.hpp b/contrib/catch2/src/catch2/benchmark/detail/catch_repeat.hpp index 08c0337..08c0337 100644 --- a/src/catch2/benchmark/detail/catch_repeat.hpp +++ b/contrib/catch2/src/catch2/benchmark/detail/catch_repeat.hpp diff --git a/src/catch2/benchmark/detail/catch_run_for_at_least.cpp b/contrib/catch2/src/catch2/benchmark/detail/catch_run_for_at_least.cpp index 3ebdcc0..3ebdcc0 100644 --- a/src/catch2/benchmark/detail/catch_run_for_at_least.cpp +++ b/contrib/catch2/src/catch2/benchmark/detail/catch_run_for_at_least.cpp diff --git a/src/catch2/benchmark/detail/catch_run_for_at_least.hpp b/contrib/catch2/src/catch2/benchmark/detail/catch_run_for_at_least.hpp index 4dfa8bb..4dfa8bb 100644 --- a/src/catch2/benchmark/detail/catch_run_for_at_least.hpp +++ b/contrib/catch2/src/catch2/benchmark/detail/catch_run_for_at_least.hpp diff --git a/src/catch2/benchmark/detail/catch_stats.cpp b/contrib/catch2/src/catch2/benchmark/detail/catch_stats.cpp index 2a5a2e0..2a5a2e0 100644 --- a/src/catch2/benchmark/detail/catch_stats.cpp +++ b/contrib/catch2/src/catch2/benchmark/detail/catch_stats.cpp diff --git a/src/catch2/benchmark/detail/catch_stats.hpp b/contrib/catch2/src/catch2/benchmark/detail/catch_stats.hpp index 3bea612..3bea612 100644 --- a/src/catch2/benchmark/detail/catch_stats.hpp +++ b/contrib/catch2/src/catch2/benchmark/detail/catch_stats.hpp diff --git a/src/catch2/benchmark/detail/catch_timing.hpp b/contrib/catch2/src/catch2/benchmark/detail/catch_timing.hpp index da56719..da56719 100644 --- a/src/catch2/benchmark/detail/catch_timing.hpp +++ b/contrib/catch2/src/catch2/benchmark/detail/catch_timing.hpp diff --git a/src/catch2/catch_all.hpp b/contrib/catch2/src/catch2/catch_all.hpp index 5715995..5715995 100644 --- a/src/catch2/catch_all.hpp +++ b/contrib/catch2/src/catch2/catch_all.hpp diff --git a/src/catch2/catch_approx.cpp b/contrib/catch2/src/catch2/catch_approx.cpp index 08c6799..08c6799 100644 --- a/src/catch2/catch_approx.cpp +++ b/contrib/catch2/src/catch2/catch_approx.cpp diff --git a/src/catch2/catch_approx.hpp b/contrib/catch2/src/catch2/catch_approx.hpp index de4d2ab..de4d2ab 100644 --- a/src/catch2/catch_approx.hpp +++ b/contrib/catch2/src/catch2/catch_approx.hpp diff --git a/src/catch2/catch_assertion_info.hpp b/contrib/catch2/src/catch2/catch_assertion_info.hpp index 9d2f91f..9d2f91f 100644 --- a/src/catch2/catch_assertion_info.hpp +++ b/contrib/catch2/src/catch2/catch_assertion_info.hpp diff --git a/src/catch2/catch_assertion_result.cpp b/contrib/catch2/src/catch2/catch_assertion_result.cpp index dba8622..dba8622 100644 --- a/src/catch2/catch_assertion_result.cpp +++ b/contrib/catch2/src/catch2/catch_assertion_result.cpp diff --git a/src/catch2/catch_assertion_result.hpp b/contrib/catch2/src/catch2/catch_assertion_result.hpp index 4888287..4888287 100644 --- a/src/catch2/catch_assertion_result.hpp +++ b/contrib/catch2/src/catch2/catch_assertion_result.hpp diff --git a/src/catch2/catch_case_sensitive.hpp b/contrib/catch2/src/catch2/catch_case_sensitive.hpp index cc89110..cc89110 100644 --- a/src/catch2/catch_case_sensitive.hpp +++ b/contrib/catch2/src/catch2/catch_case_sensitive.hpp diff --git a/src/catch2/catch_config.cpp b/contrib/catch2/src/catch2/catch_config.cpp index 352c1f4..352c1f4 100644 --- a/src/catch2/catch_config.cpp +++ b/contrib/catch2/src/catch2/catch_config.cpp diff --git a/src/catch2/catch_config.hpp b/contrib/catch2/src/catch2/catch_config.hpp index 17e983e..17e983e 100644 --- a/src/catch2/catch_config.hpp +++ b/contrib/catch2/src/catch2/catch_config.hpp diff --git a/src/catch2/catch_get_random_seed.cpp b/contrib/catch2/src/catch2/catch_get_random_seed.cpp index b122516..b122516 100644 --- a/src/catch2/catch_get_random_seed.cpp +++ b/contrib/catch2/src/catch2/catch_get_random_seed.cpp diff --git a/src/catch2/catch_get_random_seed.hpp b/contrib/catch2/src/catch2/catch_get_random_seed.hpp index 3ab2a4a..3ab2a4a 100644 --- a/src/catch2/catch_get_random_seed.hpp +++ b/contrib/catch2/src/catch2/catch_get_random_seed.hpp diff --git a/src/catch2/catch_message.cpp b/contrib/catch2/src/catch2/catch_message.cpp index 7b09ab8..7b09ab8 100644 --- a/src/catch2/catch_message.cpp +++ b/contrib/catch2/src/catch2/catch_message.cpp diff --git a/src/catch2/catch_message.hpp b/contrib/catch2/src/catch2/catch_message.hpp index 0a73834..0a73834 100644 --- a/src/catch2/catch_message.hpp +++ b/contrib/catch2/src/catch2/catch_message.hpp diff --git a/src/catch2/catch_registry_hub.cpp b/contrib/catch2/src/catch2/catch_registry_hub.cpp index 3a59467..3a59467 100644 --- a/src/catch2/catch_registry_hub.cpp +++ b/contrib/catch2/src/catch2/catch_registry_hub.cpp diff --git a/src/catch2/catch_section_info.hpp b/contrib/catch2/src/catch2/catch_section_info.hpp index 7de8441..7de8441 100644 --- a/src/catch2/catch_section_info.hpp +++ b/contrib/catch2/src/catch2/catch_section_info.hpp diff --git a/src/catch2/catch_session.cpp b/contrib/catch2/src/catch2/catch_session.cpp index a4a410c..a4a410c 100644 --- a/src/catch2/catch_session.cpp +++ b/contrib/catch2/src/catch2/catch_session.cpp diff --git a/src/catch2/catch_session.hpp b/contrib/catch2/src/catch2/catch_session.hpp index c1de6d5..c1de6d5 100644 --- a/src/catch2/catch_session.hpp +++ b/contrib/catch2/src/catch2/catch_session.hpp diff --git a/src/catch2/catch_tag_alias.hpp b/contrib/catch2/src/catch2/catch_tag_alias.hpp index dc91f21..dc91f21 100644 --- a/src/catch2/catch_tag_alias.hpp +++ b/contrib/catch2/src/catch2/catch_tag_alias.hpp diff --git a/src/catch2/catch_tag_alias_autoregistrar.cpp b/contrib/catch2/src/catch2/catch_tag_alias_autoregistrar.cpp index 9b6633a..9b6633a 100644 --- a/src/catch2/catch_tag_alias_autoregistrar.cpp +++ b/contrib/catch2/src/catch2/catch_tag_alias_autoregistrar.cpp diff --git a/src/catch2/catch_tag_alias_autoregistrar.hpp b/contrib/catch2/src/catch2/catch_tag_alias_autoregistrar.hpp index 9f80f72..9f80f72 100644 --- a/src/catch2/catch_tag_alias_autoregistrar.hpp +++ b/contrib/catch2/src/catch2/catch_tag_alias_autoregistrar.hpp diff --git a/src/catch2/catch_template_test_macros.hpp b/contrib/catch2/src/catch2/catch_template_test_macros.hpp index 3baee51..3baee51 100644 --- a/src/catch2/catch_template_test_macros.hpp +++ b/contrib/catch2/src/catch2/catch_template_test_macros.hpp diff --git a/src/catch2/catch_test_case_info.cpp b/contrib/catch2/src/catch2/catch_test_case_info.cpp index 9d64e53..9d64e53 100644 --- a/src/catch2/catch_test_case_info.cpp +++ b/contrib/catch2/src/catch2/catch_test_case_info.cpp diff --git a/src/catch2/catch_test_case_info.hpp b/contrib/catch2/src/catch2/catch_test_case_info.hpp index 3466660..3466660 100644 --- a/src/catch2/catch_test_case_info.hpp +++ b/contrib/catch2/src/catch2/catch_test_case_info.hpp diff --git a/src/catch2/catch_test_macros.hpp b/contrib/catch2/src/catch2/catch_test_macros.hpp index 6ee2129..6ee2129 100644 --- a/src/catch2/catch_test_macros.hpp +++ b/contrib/catch2/src/catch2/catch_test_macros.hpp diff --git a/src/catch2/catch_test_spec.cpp b/contrib/catch2/src/catch2/catch_test_spec.cpp index f32f986..f32f986 100644 --- a/src/catch2/catch_test_spec.cpp +++ b/contrib/catch2/src/catch2/catch_test_spec.cpp diff --git a/src/catch2/catch_test_spec.hpp b/contrib/catch2/src/catch2/catch_test_spec.hpp index f12baa6..f12baa6 100644 --- a/src/catch2/catch_test_spec.hpp +++ b/contrib/catch2/src/catch2/catch_test_spec.hpp diff --git a/src/catch2/catch_timer.cpp b/contrib/catch2/src/catch2/catch_timer.cpp index efdd8b7..efdd8b7 100644 --- a/src/catch2/catch_timer.cpp +++ b/contrib/catch2/src/catch2/catch_timer.cpp diff --git a/src/catch2/catch_timer.hpp b/contrib/catch2/src/catch2/catch_timer.hpp index f42589f..f42589f 100644 --- a/src/catch2/catch_timer.hpp +++ b/contrib/catch2/src/catch2/catch_timer.hpp diff --git a/src/catch2/catch_tostring.cpp b/contrib/catch2/src/catch2/catch_tostring.cpp index 83327cf..83327cf 100644 --- a/src/catch2/catch_tostring.cpp +++ b/contrib/catch2/src/catch2/catch_tostring.cpp diff --git a/src/catch2/catch_tostring.hpp b/contrib/catch2/src/catch2/catch_tostring.hpp index 5f84196..5f84196 100644 --- a/src/catch2/catch_tostring.hpp +++ b/contrib/catch2/src/catch2/catch_tostring.hpp diff --git a/src/catch2/catch_totals.cpp b/contrib/catch2/src/catch2/catch_totals.cpp index bd1954f..bd1954f 100644 --- a/src/catch2/catch_totals.cpp +++ b/contrib/catch2/src/catch2/catch_totals.cpp diff --git a/src/catch2/catch_totals.hpp b/contrib/catch2/src/catch2/catch_totals.hpp index 386392c..386392c 100644 --- a/src/catch2/catch_totals.hpp +++ b/contrib/catch2/src/catch2/catch_totals.hpp diff --git a/src/catch2/catch_translate_exception.cpp b/contrib/catch2/src/catch2/catch_translate_exception.cpp index c4b2894..c4b2894 100644 --- a/src/catch2/catch_translate_exception.cpp +++ b/contrib/catch2/src/catch2/catch_translate_exception.cpp diff --git a/src/catch2/catch_translate_exception.hpp b/contrib/catch2/src/catch2/catch_translate_exception.hpp index 2bf8d36..2bf8d36 100644 --- a/src/catch2/catch_translate_exception.hpp +++ b/contrib/catch2/src/catch2/catch_translate_exception.hpp diff --git a/src/catch2/catch_user_config.hpp.in b/contrib/catch2/src/catch2/catch_user_config.hpp.in index 3acda68..3acda68 100644 --- a/src/catch2/catch_user_config.hpp.in +++ b/contrib/catch2/src/catch2/catch_user_config.hpp.in diff --git a/src/catch2/catch_version.cpp b/contrib/catch2/src/catch2/catch_version.cpp index 2c40ccc..2c40ccc 100644 --- a/src/catch2/catch_version.cpp +++ b/contrib/catch2/src/catch2/catch_version.cpp diff --git a/src/catch2/catch_version.hpp b/contrib/catch2/src/catch2/catch_version.hpp index af698fa..af698fa 100644 --- a/src/catch2/catch_version.hpp +++ b/contrib/catch2/src/catch2/catch_version.hpp diff --git a/src/catch2/catch_version_macros.hpp b/contrib/catch2/src/catch2/catch_version_macros.hpp index 42655fa..42655fa 100644 --- a/src/catch2/catch_version_macros.hpp +++ b/contrib/catch2/src/catch2/catch_version_macros.hpp diff --git a/src/catch2/generators/catch_generator_exception.cpp b/contrib/catch2/src/catch2/generators/catch_generator_exception.cpp index 6432403..6432403 100644 --- a/src/catch2/generators/catch_generator_exception.cpp +++ b/contrib/catch2/src/catch2/generators/catch_generator_exception.cpp diff --git a/src/catch2/generators/catch_generator_exception.hpp b/contrib/catch2/src/catch2/generators/catch_generator_exception.hpp index f353042..f353042 100644 --- a/src/catch2/generators/catch_generator_exception.hpp +++ b/contrib/catch2/src/catch2/generators/catch_generator_exception.hpp diff --git a/src/catch2/generators/catch_generators.cpp b/contrib/catch2/src/catch2/generators/catch_generators.cpp index 3514e9f..3514e9f 100644 --- a/src/catch2/generators/catch_generators.cpp +++ b/contrib/catch2/src/catch2/generators/catch_generators.cpp diff --git a/src/catch2/generators/catch_generators.hpp b/contrib/catch2/src/catch2/generators/catch_generators.hpp index 0f35a99..0f35a99 100644 --- a/src/catch2/generators/catch_generators.hpp +++ b/contrib/catch2/src/catch2/generators/catch_generators.hpp diff --git a/src/catch2/generators/catch_generators_adapters.hpp b/contrib/catch2/src/catch2/generators/catch_generators_adapters.hpp index d5fc1e1..d5fc1e1 100644 --- a/src/catch2/generators/catch_generators_adapters.hpp +++ b/contrib/catch2/src/catch2/generators/catch_generators_adapters.hpp diff --git a/src/catch2/generators/catch_generators_all.hpp b/contrib/catch2/src/catch2/generators/catch_generators_all.hpp index c12d314..c12d314 100644 --- a/src/catch2/generators/catch_generators_all.hpp +++ b/contrib/catch2/src/catch2/generators/catch_generators_all.hpp diff --git a/src/catch2/generators/catch_generators_random.cpp b/contrib/catch2/src/catch2/generators/catch_generators_random.cpp index 00a8e63..00a8e63 100644 --- a/src/catch2/generators/catch_generators_random.cpp +++ b/contrib/catch2/src/catch2/generators/catch_generators_random.cpp diff --git a/src/catch2/generators/catch_generators_random.hpp b/contrib/catch2/src/catch2/generators/catch_generators_random.hpp index 7128356..7128356 100644 --- a/src/catch2/generators/catch_generators_random.hpp +++ b/contrib/catch2/src/catch2/generators/catch_generators_random.hpp diff --git a/src/catch2/generators/catch_generators_range.hpp b/contrib/catch2/src/catch2/generators/catch_generators_range.hpp index 55d673c..55d673c 100644 --- a/src/catch2/generators/catch_generators_range.hpp +++ b/contrib/catch2/src/catch2/generators/catch_generators_range.hpp diff --git a/src/catch2/interfaces/catch_interfaces_all.hpp b/contrib/catch2/src/catch2/interfaces/catch_interfaces_all.hpp index a99fdcd..a99fdcd 100644 --- a/src/catch2/interfaces/catch_interfaces_all.hpp +++ b/contrib/catch2/src/catch2/interfaces/catch_interfaces_all.hpp diff --git a/src/catch2/interfaces/catch_interfaces_capture.cpp b/contrib/catch2/src/catch2/interfaces/catch_interfaces_capture.cpp index 9b40ee5..9b40ee5 100644 --- a/src/catch2/interfaces/catch_interfaces_capture.cpp +++ b/contrib/catch2/src/catch2/interfaces/catch_interfaces_capture.cpp diff --git a/src/catch2/interfaces/catch_interfaces_capture.hpp b/contrib/catch2/src/catch2/interfaces/catch_interfaces_capture.hpp index 9e5431d..9e5431d 100644 --- a/src/catch2/interfaces/catch_interfaces_capture.hpp +++ b/contrib/catch2/src/catch2/interfaces/catch_interfaces_capture.hpp diff --git a/src/catch2/interfaces/catch_interfaces_config.cpp b/contrib/catch2/src/catch2/interfaces/catch_interfaces_config.cpp index 655bc1b..655bc1b 100644 --- a/src/catch2/interfaces/catch_interfaces_config.cpp +++ b/contrib/catch2/src/catch2/interfaces/catch_interfaces_config.cpp diff --git a/src/catch2/interfaces/catch_interfaces_config.hpp b/contrib/catch2/src/catch2/interfaces/catch_interfaces_config.hpp index eb05480..eb05480 100644 --- a/src/catch2/interfaces/catch_interfaces_config.hpp +++ b/contrib/catch2/src/catch2/interfaces/catch_interfaces_config.hpp diff --git a/src/catch2/interfaces/catch_interfaces_enum_values_registry.hpp b/contrib/catch2/src/catch2/interfaces/catch_interfaces_enum_values_registry.hpp index 38b052d..38b052d 100644 --- a/src/catch2/interfaces/catch_interfaces_enum_values_registry.hpp +++ b/contrib/catch2/src/catch2/interfaces/catch_interfaces_enum_values_registry.hpp diff --git a/src/catch2/interfaces/catch_interfaces_exception.cpp b/contrib/catch2/src/catch2/interfaces/catch_interfaces_exception.cpp index 44c272d..44c272d 100644 --- a/src/catch2/interfaces/catch_interfaces_exception.cpp +++ b/contrib/catch2/src/catch2/interfaces/catch_interfaces_exception.cpp diff --git a/src/catch2/interfaces/catch_interfaces_exception.hpp b/contrib/catch2/src/catch2/interfaces/catch_interfaces_exception.hpp index fcc2a8f..fcc2a8f 100644 --- a/src/catch2/interfaces/catch_interfaces_exception.hpp +++ b/contrib/catch2/src/catch2/interfaces/catch_interfaces_exception.hpp diff --git a/src/catch2/interfaces/catch_interfaces_generatortracker.cpp b/contrib/catch2/src/catch2/interfaces/catch_interfaces_generatortracker.cpp index e9fa5dd..e9fa5dd 100644 --- a/src/catch2/interfaces/catch_interfaces_generatortracker.cpp +++ b/contrib/catch2/src/catch2/interfaces/catch_interfaces_generatortracker.cpp diff --git a/src/catch2/interfaces/catch_interfaces_generatortracker.hpp b/contrib/catch2/src/catch2/interfaces/catch_interfaces_generatortracker.hpp index d70cb59..d70cb59 100644 --- a/src/catch2/interfaces/catch_interfaces_generatortracker.hpp +++ b/contrib/catch2/src/catch2/interfaces/catch_interfaces_generatortracker.hpp diff --git a/src/catch2/interfaces/catch_interfaces_registry_hub.cpp b/contrib/catch2/src/catch2/interfaces/catch_interfaces_registry_hub.cpp index cd688a4..cd688a4 100644 --- a/src/catch2/interfaces/catch_interfaces_registry_hub.cpp +++ b/contrib/catch2/src/catch2/interfaces/catch_interfaces_registry_hub.cpp diff --git a/src/catch2/interfaces/catch_interfaces_registry_hub.hpp b/contrib/catch2/src/catch2/interfaces/catch_interfaces_registry_hub.hpp index 113f223..113f223 100644 --- a/src/catch2/interfaces/catch_interfaces_registry_hub.hpp +++ b/contrib/catch2/src/catch2/interfaces/catch_interfaces_registry_hub.hpp diff --git a/src/catch2/interfaces/catch_interfaces_reporter.cpp b/contrib/catch2/src/catch2/interfaces/catch_interfaces_reporter.cpp index 90536bb..90536bb 100644 --- a/src/catch2/interfaces/catch_interfaces_reporter.cpp +++ b/contrib/catch2/src/catch2/interfaces/catch_interfaces_reporter.cpp diff --git a/src/catch2/interfaces/catch_interfaces_reporter.hpp b/contrib/catch2/src/catch2/interfaces/catch_interfaces_reporter.hpp index a052c5d..a052c5d 100644 --- a/src/catch2/interfaces/catch_interfaces_reporter.hpp +++ b/contrib/catch2/src/catch2/interfaces/catch_interfaces_reporter.hpp diff --git a/src/catch2/interfaces/catch_interfaces_reporter_factory.cpp b/contrib/catch2/src/catch2/interfaces/catch_interfaces_reporter_factory.cpp index 4fe992f..4fe992f 100644 --- a/src/catch2/interfaces/catch_interfaces_reporter_factory.cpp +++ b/contrib/catch2/src/catch2/interfaces/catch_interfaces_reporter_factory.cpp diff --git a/src/catch2/interfaces/catch_interfaces_reporter_factory.hpp b/contrib/catch2/src/catch2/interfaces/catch_interfaces_reporter_factory.hpp index 83ddd7b..83ddd7b 100644 --- a/src/catch2/interfaces/catch_interfaces_reporter_factory.hpp +++ b/contrib/catch2/src/catch2/interfaces/catch_interfaces_reporter_factory.hpp diff --git a/src/catch2/interfaces/catch_interfaces_tag_alias_registry.hpp b/contrib/catch2/src/catch2/interfaces/catch_interfaces_tag_alias_registry.hpp index 5da0f8d..5da0f8d 100644 --- a/src/catch2/interfaces/catch_interfaces_tag_alias_registry.hpp +++ b/contrib/catch2/src/catch2/interfaces/catch_interfaces_tag_alias_registry.hpp diff --git a/src/catch2/interfaces/catch_interfaces_test_invoker.hpp b/contrib/catch2/src/catch2/interfaces/catch_interfaces_test_invoker.hpp index 124a7f7..124a7f7 100644 --- a/src/catch2/interfaces/catch_interfaces_test_invoker.hpp +++ b/contrib/catch2/src/catch2/interfaces/catch_interfaces_test_invoker.hpp diff --git a/src/catch2/interfaces/catch_interfaces_testcase.cpp b/contrib/catch2/src/catch2/interfaces/catch_interfaces_testcase.cpp index a543116..a543116 100644 --- a/src/catch2/interfaces/catch_interfaces_testcase.cpp +++ b/contrib/catch2/src/catch2/interfaces/catch_interfaces_testcase.cpp diff --git a/src/catch2/interfaces/catch_interfaces_testcase.hpp b/contrib/catch2/src/catch2/interfaces/catch_interfaces_testcase.hpp index daee848..daee848 100644 --- a/src/catch2/interfaces/catch_interfaces_testcase.hpp +++ b/contrib/catch2/src/catch2/interfaces/catch_interfaces_testcase.hpp diff --git a/src/catch2/internal/catch_assertion_handler.cpp b/contrib/catch2/src/catch2/internal/catch_assertion_handler.cpp index 9a28e79..9a28e79 100644 --- a/src/catch2/internal/catch_assertion_handler.cpp +++ b/contrib/catch2/src/catch2/internal/catch_assertion_handler.cpp diff --git a/src/catch2/internal/catch_assertion_handler.hpp b/contrib/catch2/src/catch2/internal/catch_assertion_handler.hpp index c71c689..c71c689 100644 --- a/src/catch2/internal/catch_assertion_handler.hpp +++ b/contrib/catch2/src/catch2/internal/catch_assertion_handler.hpp diff --git a/src/catch2/internal/catch_case_insensitive_comparisons.cpp b/contrib/catch2/src/catch2/internal/catch_case_insensitive_comparisons.cpp index b3e7b53..b3e7b53 100644 --- a/src/catch2/internal/catch_case_insensitive_comparisons.cpp +++ b/contrib/catch2/src/catch2/internal/catch_case_insensitive_comparisons.cpp diff --git a/src/catch2/internal/catch_case_insensitive_comparisons.hpp b/contrib/catch2/src/catch2/internal/catch_case_insensitive_comparisons.hpp index 33b7782..33b7782 100644 --- a/src/catch2/internal/catch_case_insensitive_comparisons.hpp +++ b/contrib/catch2/src/catch2/internal/catch_case_insensitive_comparisons.hpp diff --git a/src/catch2/internal/catch_clara.cpp b/contrib/catch2/src/catch2/internal/catch_clara.cpp index f9dd913..f9dd913 100644 --- a/src/catch2/internal/catch_clara.cpp +++ b/contrib/catch2/src/catch2/internal/catch_clara.cpp diff --git a/src/catch2/internal/catch_clara.hpp b/contrib/catch2/src/catch2/internal/catch_clara.hpp index d869593..d869593 100644 --- a/src/catch2/internal/catch_clara.hpp +++ b/contrib/catch2/src/catch2/internal/catch_clara.hpp diff --git a/src/catch2/internal/catch_commandline.cpp b/contrib/catch2/src/catch2/internal/catch_commandline.cpp index 212f177..212f177 100644 --- a/src/catch2/internal/catch_commandline.cpp +++ b/contrib/catch2/src/catch2/internal/catch_commandline.cpp diff --git a/src/catch2/internal/catch_commandline.hpp b/contrib/catch2/src/catch2/internal/catch_commandline.hpp index 8cc2254..8cc2254 100644 --- a/src/catch2/internal/catch_commandline.hpp +++ b/contrib/catch2/src/catch2/internal/catch_commandline.hpp diff --git a/src/catch2/internal/catch_compare_traits.hpp b/contrib/catch2/src/catch2/internal/catch_compare_traits.hpp index 6304b1f..6304b1f 100644 --- a/src/catch2/internal/catch_compare_traits.hpp +++ b/contrib/catch2/src/catch2/internal/catch_compare_traits.hpp diff --git a/src/catch2/internal/catch_compiler_capabilities.hpp b/contrib/catch2/src/catch2/internal/catch_compiler_capabilities.hpp index 4cf1d65..4cf1d65 100644 --- a/src/catch2/internal/catch_compiler_capabilities.hpp +++ b/contrib/catch2/src/catch2/internal/catch_compiler_capabilities.hpp diff --git a/src/catch2/internal/catch_config_android_logwrite.hpp b/contrib/catch2/src/catch2/internal/catch_config_android_logwrite.hpp index 490ee37..490ee37 100644 --- a/src/catch2/internal/catch_config_android_logwrite.hpp +++ b/contrib/catch2/src/catch2/internal/catch_config_android_logwrite.hpp diff --git a/src/catch2/internal/catch_config_counter.hpp b/contrib/catch2/src/catch2/internal/catch_config_counter.hpp index a482ce3..a482ce3 100644 --- a/src/catch2/internal/catch_config_counter.hpp +++ b/contrib/catch2/src/catch2/internal/catch_config_counter.hpp diff --git a/src/catch2/internal/catch_config_prefix_messages.hpp b/contrib/catch2/src/catch2/internal/catch_config_prefix_messages.hpp index be1e9a9..be1e9a9 100644 --- a/src/catch2/internal/catch_config_prefix_messages.hpp +++ b/contrib/catch2/src/catch2/internal/catch_config_prefix_messages.hpp diff --git a/src/catch2/internal/catch_config_static_analysis_support.hpp b/contrib/catch2/src/catch2/internal/catch_config_static_analysis_support.hpp index 81bdf39..81bdf39 100644 --- a/src/catch2/internal/catch_config_static_analysis_support.hpp +++ b/contrib/catch2/src/catch2/internal/catch_config_static_analysis_support.hpp diff --git a/src/catch2/internal/catch_config_uncaught_exceptions.hpp b/contrib/catch2/src/catch2/internal/catch_config_uncaught_exceptions.hpp index 20b1dfc..20b1dfc 100644 --- a/src/catch2/internal/catch_config_uncaught_exceptions.hpp +++ b/contrib/catch2/src/catch2/internal/catch_config_uncaught_exceptions.hpp diff --git a/src/catch2/internal/catch_config_wchar.hpp b/contrib/catch2/src/catch2/internal/catch_config_wchar.hpp index 90d85d0..90d85d0 100644 --- a/src/catch2/internal/catch_config_wchar.hpp +++ b/contrib/catch2/src/catch2/internal/catch_config_wchar.hpp diff --git a/src/catch2/internal/catch_console_colour.cpp b/contrib/catch2/src/catch2/internal/catch_console_colour.cpp index 142de9e..142de9e 100644 --- a/src/catch2/internal/catch_console_colour.cpp +++ b/contrib/catch2/src/catch2/internal/catch_console_colour.cpp diff --git a/src/catch2/internal/catch_console_colour.hpp b/contrib/catch2/src/catch2/internal/catch_console_colour.hpp index d914431..d914431 100644 --- a/src/catch2/internal/catch_console_colour.hpp +++ b/contrib/catch2/src/catch2/internal/catch_console_colour.hpp diff --git a/src/catch2/internal/catch_console_width.hpp b/contrib/catch2/src/catch2/internal/catch_console_width.hpp index 1655361..1655361 100644 --- a/src/catch2/internal/catch_console_width.hpp +++ b/contrib/catch2/src/catch2/internal/catch_console_width.hpp diff --git a/src/catch2/internal/catch_container_nonmembers.hpp b/contrib/catch2/src/catch2/internal/catch_container_nonmembers.hpp index 33b28a9..33b28a9 100644 --- a/src/catch2/internal/catch_container_nonmembers.hpp +++ b/contrib/catch2/src/catch2/internal/catch_container_nonmembers.hpp diff --git a/src/catch2/internal/catch_context.cpp b/contrib/catch2/src/catch2/internal/catch_context.cpp index 8acf1ed..8acf1ed 100644 --- a/src/catch2/internal/catch_context.cpp +++ b/contrib/catch2/src/catch2/internal/catch_context.cpp diff --git a/src/catch2/internal/catch_context.hpp b/contrib/catch2/src/catch2/internal/catch_context.hpp index 4d8a5da..4d8a5da 100644 --- a/src/catch2/internal/catch_context.hpp +++ b/contrib/catch2/src/catch2/internal/catch_context.hpp diff --git a/src/catch2/internal/catch_debug_console.cpp b/contrib/catch2/src/catch2/internal/catch_debug_console.cpp index 40dd0a6..40dd0a6 100644 --- a/src/catch2/internal/catch_debug_console.cpp +++ b/contrib/catch2/src/catch2/internal/catch_debug_console.cpp diff --git a/src/catch2/internal/catch_debug_console.hpp b/contrib/catch2/src/catch2/internal/catch_debug_console.hpp index 8784f78..8784f78 100644 --- a/src/catch2/internal/catch_debug_console.hpp +++ b/contrib/catch2/src/catch2/internal/catch_debug_console.hpp diff --git a/src/catch2/internal/catch_debugger.cpp b/contrib/catch2/src/catch2/internal/catch_debugger.cpp index bd3be17..bd3be17 100644 --- a/src/catch2/internal/catch_debugger.cpp +++ b/contrib/catch2/src/catch2/internal/catch_debugger.cpp diff --git a/src/catch2/internal/catch_debugger.hpp b/contrib/catch2/src/catch2/internal/catch_debugger.hpp index 25c5a26..25c5a26 100644 --- a/src/catch2/internal/catch_debugger.hpp +++ b/contrib/catch2/src/catch2/internal/catch_debugger.hpp diff --git a/src/catch2/internal/catch_decomposer.cpp b/contrib/catch2/src/catch2/internal/catch_decomposer.cpp index 17a7bc9..17a7bc9 100644 --- a/src/catch2/internal/catch_decomposer.cpp +++ b/contrib/catch2/src/catch2/internal/catch_decomposer.cpp diff --git a/src/catch2/internal/catch_decomposer.hpp b/contrib/catch2/src/catch2/internal/catch_decomposer.hpp index adce89f..adce89f 100644 --- a/src/catch2/internal/catch_decomposer.hpp +++ b/contrib/catch2/src/catch2/internal/catch_decomposer.hpp diff --git a/src/catch2/internal/catch_enforce.cpp b/contrib/catch2/src/catch2/internal/catch_enforce.cpp index 3f69640..3f69640 100644 --- a/src/catch2/internal/catch_enforce.cpp +++ b/contrib/catch2/src/catch2/internal/catch_enforce.cpp diff --git a/src/catch2/internal/catch_enforce.hpp b/contrib/catch2/src/catch2/internal/catch_enforce.hpp index 076cea3..076cea3 100644 --- a/src/catch2/internal/catch_enforce.hpp +++ b/contrib/catch2/src/catch2/internal/catch_enforce.hpp diff --git a/src/catch2/internal/catch_enum_values_registry.cpp b/contrib/catch2/src/catch2/internal/catch_enum_values_registry.cpp index a94b608..a94b608 100644 --- a/src/catch2/internal/catch_enum_values_registry.cpp +++ b/contrib/catch2/src/catch2/internal/catch_enum_values_registry.cpp diff --git a/src/catch2/internal/catch_enum_values_registry.hpp b/contrib/catch2/src/catch2/internal/catch_enum_values_registry.hpp index de994c3..de994c3 100644 --- a/src/catch2/internal/catch_enum_values_registry.hpp +++ b/contrib/catch2/src/catch2/internal/catch_enum_values_registry.hpp diff --git a/src/catch2/internal/catch_errno_guard.cpp b/contrib/catch2/src/catch2/internal/catch_errno_guard.cpp index 3bbf8b4..3bbf8b4 100644 --- a/src/catch2/internal/catch_errno_guard.cpp +++ b/contrib/catch2/src/catch2/internal/catch_errno_guard.cpp diff --git a/src/catch2/internal/catch_errno_guard.hpp b/contrib/catch2/src/catch2/internal/catch_errno_guard.hpp index df1237d..df1237d 100644 --- a/src/catch2/internal/catch_errno_guard.hpp +++ b/contrib/catch2/src/catch2/internal/catch_errno_guard.hpp diff --git a/src/catch2/internal/catch_exception_translator_registry.cpp b/contrib/catch2/src/catch2/internal/catch_exception_translator_registry.cpp index 1eb6114..1eb6114 100644 --- a/src/catch2/internal/catch_exception_translator_registry.cpp +++ b/contrib/catch2/src/catch2/internal/catch_exception_translator_registry.cpp diff --git a/src/catch2/internal/catch_exception_translator_registry.hpp b/contrib/catch2/src/catch2/internal/catch_exception_translator_registry.hpp index 3123e93..3123e93 100644 --- a/src/catch2/internal/catch_exception_translator_registry.hpp +++ b/contrib/catch2/src/catch2/internal/catch_exception_translator_registry.hpp diff --git a/src/catch2/internal/catch_fatal_condition_handler.cpp b/contrib/catch2/src/catch2/internal/catch_fatal_condition_handler.cpp index 9ef5b21..9ef5b21 100644 --- a/src/catch2/internal/catch_fatal_condition_handler.cpp +++ b/contrib/catch2/src/catch2/internal/catch_fatal_condition_handler.cpp diff --git a/src/catch2/internal/catch_fatal_condition_handler.hpp b/contrib/catch2/src/catch2/internal/catch_fatal_condition_handler.hpp index 81728b5..81728b5 100644 --- a/src/catch2/internal/catch_fatal_condition_handler.hpp +++ b/contrib/catch2/src/catch2/internal/catch_fatal_condition_handler.hpp diff --git a/src/catch2/internal/catch_floating_point_helpers.cpp b/contrib/catch2/src/catch2/internal/catch_floating_point_helpers.cpp index 9631ed6..9631ed6 100644 --- a/src/catch2/internal/catch_floating_point_helpers.cpp +++ b/contrib/catch2/src/catch2/internal/catch_floating_point_helpers.cpp diff --git a/src/catch2/internal/catch_floating_point_helpers.hpp b/contrib/catch2/src/catch2/internal/catch_floating_point_helpers.hpp index b214372..b214372 100644 --- a/src/catch2/internal/catch_floating_point_helpers.hpp +++ b/contrib/catch2/src/catch2/internal/catch_floating_point_helpers.hpp diff --git a/src/catch2/internal/catch_getenv.cpp b/contrib/catch2/src/catch2/internal/catch_getenv.cpp index a9a592c..a9a592c 100644 --- a/src/catch2/internal/catch_getenv.cpp +++ b/contrib/catch2/src/catch2/internal/catch_getenv.cpp diff --git a/src/catch2/internal/catch_getenv.hpp b/contrib/catch2/src/catch2/internal/catch_getenv.hpp index 31ef797..31ef797 100644 --- a/src/catch2/internal/catch_getenv.hpp +++ b/contrib/catch2/src/catch2/internal/catch_getenv.hpp diff --git a/src/catch2/internal/catch_is_permutation.hpp b/contrib/catch2/src/catch2/internal/catch_is_permutation.hpp index c77a6d3..c77a6d3 100644 --- a/src/catch2/internal/catch_is_permutation.hpp +++ b/contrib/catch2/src/catch2/internal/catch_is_permutation.hpp diff --git a/src/catch2/internal/catch_istream.cpp b/contrib/catch2/src/catch2/internal/catch_istream.cpp index 2867ce7..2867ce7 100644 --- a/src/catch2/internal/catch_istream.cpp +++ b/contrib/catch2/src/catch2/internal/catch_istream.cpp diff --git a/src/catch2/internal/catch_istream.hpp b/contrib/catch2/src/catch2/internal/catch_istream.hpp index e6b9a2d..e6b9a2d 100644 --- a/src/catch2/internal/catch_istream.hpp +++ b/contrib/catch2/src/catch2/internal/catch_istream.hpp diff --git a/src/catch2/internal/catch_jsonwriter.cpp b/contrib/catch2/src/catch2/internal/catch_jsonwriter.cpp index 1a96e34..1a96e34 100644 --- a/src/catch2/internal/catch_jsonwriter.cpp +++ b/contrib/catch2/src/catch2/internal/catch_jsonwriter.cpp diff --git a/src/catch2/internal/catch_jsonwriter.hpp b/contrib/catch2/src/catch2/internal/catch_jsonwriter.hpp index 23b56d1..23b56d1 100644 --- a/src/catch2/internal/catch_jsonwriter.hpp +++ b/contrib/catch2/src/catch2/internal/catch_jsonwriter.hpp diff --git a/src/catch2/internal/catch_lazy_expr.cpp b/contrib/catch2/src/catch2/internal/catch_lazy_expr.cpp index 56a5ae5..56a5ae5 100644 --- a/src/catch2/internal/catch_lazy_expr.cpp +++ b/contrib/catch2/src/catch2/internal/catch_lazy_expr.cpp diff --git a/src/catch2/internal/catch_lazy_expr.hpp b/contrib/catch2/src/catch2/internal/catch_lazy_expr.hpp index c6ff224..c6ff224 100644 --- a/src/catch2/internal/catch_lazy_expr.hpp +++ b/contrib/catch2/src/catch2/internal/catch_lazy_expr.hpp diff --git a/src/catch2/internal/catch_leak_detector.cpp b/contrib/catch2/src/catch2/internal/catch_leak_detector.cpp index 691bc77..691bc77 100644 --- a/src/catch2/internal/catch_leak_detector.cpp +++ b/contrib/catch2/src/catch2/internal/catch_leak_detector.cpp diff --git a/src/catch2/internal/catch_leak_detector.hpp b/contrib/catch2/src/catch2/internal/catch_leak_detector.hpp index 94c8f32..94c8f32 100644 --- a/src/catch2/internal/catch_leak_detector.hpp +++ b/contrib/catch2/src/catch2/internal/catch_leak_detector.hpp diff --git a/src/catch2/internal/catch_list.cpp b/contrib/catch2/src/catch2/internal/catch_list.cpp index 5bd06a2..5bd06a2 100644 --- a/src/catch2/internal/catch_list.cpp +++ b/contrib/catch2/src/catch2/internal/catch_list.cpp diff --git a/src/catch2/internal/catch_list.hpp b/contrib/catch2/src/catch2/internal/catch_list.hpp index 9b4abd8..9b4abd8 100644 --- a/src/catch2/internal/catch_list.hpp +++ b/contrib/catch2/src/catch2/internal/catch_list.hpp diff --git a/src/catch2/internal/catch_logical_traits.hpp b/contrib/catch2/src/catch2/internal/catch_logical_traits.hpp index bd87565..bd87565 100644 --- a/src/catch2/internal/catch_logical_traits.hpp +++ b/contrib/catch2/src/catch2/internal/catch_logical_traits.hpp diff --git a/src/catch2/internal/catch_main.cpp b/contrib/catch2/src/catch2/internal/catch_main.cpp index 503b540..503b540 100644 --- a/src/catch2/internal/catch_main.cpp +++ b/contrib/catch2/src/catch2/internal/catch_main.cpp diff --git a/src/catch2/internal/catch_message_info.cpp b/contrib/catch2/src/catch2/internal/catch_message_info.cpp index e0e9dc7..e0e9dc7 100644 --- a/src/catch2/internal/catch_message_info.cpp +++ b/contrib/catch2/src/catch2/internal/catch_message_info.cpp diff --git a/src/catch2/internal/catch_message_info.hpp b/contrib/catch2/src/catch2/internal/catch_message_info.hpp index 1ef43fd..1ef43fd 100644 --- a/src/catch2/internal/catch_message_info.hpp +++ b/contrib/catch2/src/catch2/internal/catch_message_info.hpp diff --git a/src/catch2/internal/catch_meta.hpp b/contrib/catch2/src/catch2/internal/catch_meta.hpp index 6fbc13a..6fbc13a 100644 --- a/src/catch2/internal/catch_meta.hpp +++ b/contrib/catch2/src/catch2/internal/catch_meta.hpp diff --git a/src/catch2/internal/catch_move_and_forward.hpp b/contrib/catch2/src/catch2/internal/catch_move_and_forward.hpp index 383d85c..383d85c 100644 --- a/src/catch2/internal/catch_move_and_forward.hpp +++ b/contrib/catch2/src/catch2/internal/catch_move_and_forward.hpp diff --git a/src/catch2/internal/catch_noncopyable.hpp b/contrib/catch2/src/catch2/internal/catch_noncopyable.hpp index eb0823e..eb0823e 100644 --- a/src/catch2/internal/catch_noncopyable.hpp +++ b/contrib/catch2/src/catch2/internal/catch_noncopyable.hpp diff --git a/src/catch2/internal/catch_optional.hpp b/contrib/catch2/src/catch2/internal/catch_optional.hpp index d1e953a..d1e953a 100644 --- a/src/catch2/internal/catch_optional.hpp +++ b/contrib/catch2/src/catch2/internal/catch_optional.hpp diff --git a/src/catch2/internal/catch_output_redirect.cpp b/contrib/catch2/src/catch2/internal/catch_output_redirect.cpp index 245e137..245e137 100644 --- a/src/catch2/internal/catch_output_redirect.cpp +++ b/contrib/catch2/src/catch2/internal/catch_output_redirect.cpp diff --git a/src/catch2/internal/catch_output_redirect.hpp b/contrib/catch2/src/catch2/internal/catch_output_redirect.hpp index 51b796b..51b796b 100644 --- a/src/catch2/internal/catch_output_redirect.hpp +++ b/contrib/catch2/src/catch2/internal/catch_output_redirect.hpp diff --git a/src/catch2/internal/catch_parse_numbers.cpp b/contrib/catch2/src/catch2/internal/catch_parse_numbers.cpp index d949ac1..d949ac1 100644 --- a/src/catch2/internal/catch_parse_numbers.cpp +++ b/contrib/catch2/src/catch2/internal/catch_parse_numbers.cpp diff --git a/src/catch2/internal/catch_parse_numbers.hpp b/contrib/catch2/src/catch2/internal/catch_parse_numbers.hpp index 3dabf95..3dabf95 100644 --- a/src/catch2/internal/catch_parse_numbers.hpp +++ b/contrib/catch2/src/catch2/internal/catch_parse_numbers.hpp diff --git a/src/catch2/internal/catch_platform.hpp b/contrib/catch2/src/catch2/internal/catch_platform.hpp index b653a58..b653a58 100644 --- a/src/catch2/internal/catch_platform.hpp +++ b/contrib/catch2/src/catch2/internal/catch_platform.hpp diff --git a/src/catch2/internal/catch_polyfills.cpp b/contrib/catch2/src/catch2/internal/catch_polyfills.cpp index 776c224..776c224 100644 --- a/src/catch2/internal/catch_polyfills.cpp +++ b/contrib/catch2/src/catch2/internal/catch_polyfills.cpp diff --git a/src/catch2/internal/catch_polyfills.hpp b/contrib/catch2/src/catch2/internal/catch_polyfills.hpp index 4503f8f..4503f8f 100644 --- a/src/catch2/internal/catch_polyfills.hpp +++ b/contrib/catch2/src/catch2/internal/catch_polyfills.hpp diff --git a/src/catch2/internal/catch_preprocessor.hpp b/contrib/catch2/src/catch2/internal/catch_preprocessor.hpp index 08e844d..08e844d 100644 --- a/src/catch2/internal/catch_preprocessor.hpp +++ b/contrib/catch2/src/catch2/internal/catch_preprocessor.hpp diff --git a/src/catch2/internal/catch_preprocessor_internal_stringify.hpp b/contrib/catch2/src/catch2/internal/catch_preprocessor_internal_stringify.hpp index 2fd64e1..2fd64e1 100644 --- a/src/catch2/internal/catch_preprocessor_internal_stringify.hpp +++ b/contrib/catch2/src/catch2/internal/catch_preprocessor_internal_stringify.hpp diff --git a/src/catch2/internal/catch_preprocessor_remove_parens.hpp b/contrib/catch2/src/catch2/internal/catch_preprocessor_remove_parens.hpp index dee11cd..dee11cd 100644 --- a/src/catch2/internal/catch_preprocessor_remove_parens.hpp +++ b/contrib/catch2/src/catch2/internal/catch_preprocessor_remove_parens.hpp diff --git a/src/catch2/internal/catch_random_floating_point_helpers.hpp b/contrib/catch2/src/catch2/internal/catch_random_floating_point_helpers.hpp index c59c053..c59c053 100644 --- a/src/catch2/internal/catch_random_floating_point_helpers.hpp +++ b/contrib/catch2/src/catch2/internal/catch_random_floating_point_helpers.hpp diff --git a/src/catch2/internal/catch_random_integer_helpers.hpp b/contrib/catch2/src/catch2/internal/catch_random_integer_helpers.hpp index be4bbe9..be4bbe9 100644 --- a/src/catch2/internal/catch_random_integer_helpers.hpp +++ b/contrib/catch2/src/catch2/internal/catch_random_integer_helpers.hpp diff --git a/src/catch2/internal/catch_random_number_generator.cpp b/contrib/catch2/src/catch2/internal/catch_random_number_generator.cpp index c88cd8f..c88cd8f 100644 --- a/src/catch2/internal/catch_random_number_generator.cpp +++ b/contrib/catch2/src/catch2/internal/catch_random_number_generator.cpp diff --git a/src/catch2/internal/catch_random_number_generator.hpp b/contrib/catch2/src/catch2/internal/catch_random_number_generator.hpp index e4129be..e4129be 100644 --- a/src/catch2/internal/catch_random_number_generator.hpp +++ b/contrib/catch2/src/catch2/internal/catch_random_number_generator.hpp diff --git a/src/catch2/internal/catch_random_seed_generation.cpp b/contrib/catch2/src/catch2/internal/catch_random_seed_generation.cpp index fdc3fa1..fdc3fa1 100644 --- a/src/catch2/internal/catch_random_seed_generation.cpp +++ b/contrib/catch2/src/catch2/internal/catch_random_seed_generation.cpp diff --git a/src/catch2/internal/catch_random_seed_generation.hpp b/contrib/catch2/src/catch2/internal/catch_random_seed_generation.hpp index d0d6fb2..d0d6fb2 100644 --- a/src/catch2/internal/catch_random_seed_generation.hpp +++ b/contrib/catch2/src/catch2/internal/catch_random_seed_generation.hpp diff --git a/src/catch2/internal/catch_reporter_registry.cpp b/contrib/catch2/src/catch2/internal/catch_reporter_registry.cpp index cea8c4d..cea8c4d 100644 --- a/src/catch2/internal/catch_reporter_registry.cpp +++ b/contrib/catch2/src/catch2/internal/catch_reporter_registry.cpp diff --git a/src/catch2/internal/catch_reporter_registry.hpp b/contrib/catch2/src/catch2/internal/catch_reporter_registry.hpp index 92a8892..92a8892 100644 --- a/src/catch2/internal/catch_reporter_registry.hpp +++ b/contrib/catch2/src/catch2/internal/catch_reporter_registry.hpp diff --git a/src/catch2/internal/catch_reporter_spec_parser.cpp b/contrib/catch2/src/catch2/internal/catch_reporter_spec_parser.cpp index 2b08758..2b08758 100644 --- a/src/catch2/internal/catch_reporter_spec_parser.cpp +++ b/contrib/catch2/src/catch2/internal/catch_reporter_spec_parser.cpp diff --git a/src/catch2/internal/catch_reporter_spec_parser.hpp b/contrib/catch2/src/catch2/internal/catch_reporter_spec_parser.hpp index 9f447ee..9f447ee 100644 --- a/src/catch2/internal/catch_reporter_spec_parser.hpp +++ b/contrib/catch2/src/catch2/internal/catch_reporter_spec_parser.hpp diff --git a/src/catch2/internal/catch_result_type.hpp b/contrib/catch2/src/catch2/internal/catch_result_type.hpp index 69a6ef1..69a6ef1 100644 --- a/src/catch2/internal/catch_result_type.hpp +++ b/contrib/catch2/src/catch2/internal/catch_result_type.hpp diff --git a/src/catch2/internal/catch_reusable_string_stream.cpp b/contrib/catch2/src/catch2/internal/catch_reusable_string_stream.cpp index 33eafde..33eafde 100644 --- a/src/catch2/internal/catch_reusable_string_stream.cpp +++ b/contrib/catch2/src/catch2/internal/catch_reusable_string_stream.cpp diff --git a/src/catch2/internal/catch_reusable_string_stream.hpp b/contrib/catch2/src/catch2/internal/catch_reusable_string_stream.hpp index 5b864f3..5b864f3 100644 --- a/src/catch2/internal/catch_reusable_string_stream.hpp +++ b/contrib/catch2/src/catch2/internal/catch_reusable_string_stream.hpp diff --git a/src/catch2/internal/catch_run_context.cpp b/contrib/catch2/src/catch2/internal/catch_run_context.cpp index 2a102fb..2a102fb 100644 --- a/src/catch2/internal/catch_run_context.cpp +++ b/contrib/catch2/src/catch2/internal/catch_run_context.cpp diff --git a/src/catch2/internal/catch_run_context.hpp b/contrib/catch2/src/catch2/internal/catch_run_context.hpp index c66fec0..c66fec0 100644 --- a/src/catch2/internal/catch_run_context.hpp +++ b/contrib/catch2/src/catch2/internal/catch_run_context.hpp diff --git a/src/catch2/internal/catch_section.cpp b/contrib/catch2/src/catch2/internal/catch_section.cpp index 677c216..677c216 100644 --- a/src/catch2/internal/catch_section.cpp +++ b/contrib/catch2/src/catch2/internal/catch_section.cpp diff --git a/src/catch2/internal/catch_section.hpp b/contrib/catch2/src/catch2/internal/catch_section.hpp index e56c79f..e56c79f 100644 --- a/src/catch2/internal/catch_section.hpp +++ b/contrib/catch2/src/catch2/internal/catch_section.hpp diff --git a/src/catch2/internal/catch_sharding.hpp b/contrib/catch2/src/catch2/internal/catch_sharding.hpp index 22561f4..22561f4 100644 --- a/src/catch2/internal/catch_sharding.hpp +++ b/contrib/catch2/src/catch2/internal/catch_sharding.hpp diff --git a/src/catch2/internal/catch_singletons.cpp b/contrib/catch2/src/catch2/internal/catch_singletons.cpp index 4e856de..4e856de 100644 --- a/src/catch2/internal/catch_singletons.cpp +++ b/contrib/catch2/src/catch2/internal/catch_singletons.cpp diff --git a/src/catch2/internal/catch_singletons.hpp b/contrib/catch2/src/catch2/internal/catch_singletons.hpp index a28a13d..a28a13d 100644 --- a/src/catch2/internal/catch_singletons.hpp +++ b/contrib/catch2/src/catch2/internal/catch_singletons.hpp diff --git a/src/catch2/internal/catch_source_line_info.cpp b/contrib/catch2/src/catch2/internal/catch_source_line_info.cpp index f55f1c9..f55f1c9 100644 --- a/src/catch2/internal/catch_source_line_info.cpp +++ b/contrib/catch2/src/catch2/internal/catch_source_line_info.cpp diff --git a/src/catch2/internal/catch_source_line_info.hpp b/contrib/catch2/src/catch2/internal/catch_source_line_info.hpp index c598052..c598052 100644 --- a/src/catch2/internal/catch_source_line_info.hpp +++ b/contrib/catch2/src/catch2/internal/catch_source_line_info.hpp diff --git a/src/catch2/internal/catch_startup_exception_registry.cpp b/contrib/catch2/src/catch2/internal/catch_startup_exception_registry.cpp index 1607663..1607663 100644 --- a/src/catch2/internal/catch_startup_exception_registry.cpp +++ b/contrib/catch2/src/catch2/internal/catch_startup_exception_registry.cpp diff --git a/src/catch2/internal/catch_startup_exception_registry.hpp b/contrib/catch2/src/catch2/internal/catch_startup_exception_registry.hpp index aef4667..aef4667 100644 --- a/src/catch2/internal/catch_startup_exception_registry.hpp +++ b/contrib/catch2/src/catch2/internal/catch_startup_exception_registry.hpp diff --git a/src/catch2/internal/catch_stdstreams.cpp b/contrib/catch2/src/catch2/internal/catch_stdstreams.cpp index a4502b2..a4502b2 100644 --- a/src/catch2/internal/catch_stdstreams.cpp +++ b/contrib/catch2/src/catch2/internal/catch_stdstreams.cpp diff --git a/src/catch2/internal/catch_stdstreams.hpp b/contrib/catch2/src/catch2/internal/catch_stdstreams.hpp index 02aec63..02aec63 100644 --- a/src/catch2/internal/catch_stdstreams.hpp +++ b/contrib/catch2/src/catch2/internal/catch_stdstreams.hpp diff --git a/src/catch2/internal/catch_stream_end_stop.hpp b/contrib/catch2/src/catch2/internal/catch_stream_end_stop.hpp index 66d678c..66d678c 100644 --- a/src/catch2/internal/catch_stream_end_stop.hpp +++ b/contrib/catch2/src/catch2/internal/catch_stream_end_stop.hpp diff --git a/src/catch2/internal/catch_string_manip.cpp b/contrib/catch2/src/catch2/internal/catch_string_manip.cpp index ce1abaa..ce1abaa 100644 --- a/src/catch2/internal/catch_string_manip.cpp +++ b/contrib/catch2/src/catch2/internal/catch_string_manip.cpp diff --git a/src/catch2/internal/catch_string_manip.hpp b/contrib/catch2/src/catch2/internal/catch_string_manip.hpp index dc0c552..dc0c552 100644 --- a/src/catch2/internal/catch_string_manip.hpp +++ b/contrib/catch2/src/catch2/internal/catch_string_manip.hpp diff --git a/src/catch2/internal/catch_stringref.cpp b/contrib/catch2/src/catch2/internal/catch_stringref.cpp index 232498e..232498e 100644 --- a/src/catch2/internal/catch_stringref.cpp +++ b/contrib/catch2/src/catch2/internal/catch_stringref.cpp diff --git a/src/catch2/internal/catch_stringref.hpp b/contrib/catch2/src/catch2/internal/catch_stringref.hpp index 421ce71..421ce71 100644 --- a/src/catch2/internal/catch_stringref.hpp +++ b/contrib/catch2/src/catch2/internal/catch_stringref.hpp diff --git a/src/catch2/internal/catch_tag_alias_registry.cpp b/contrib/catch2/src/catch2/internal/catch_tag_alias_registry.cpp index 510df16..510df16 100644 --- a/src/catch2/internal/catch_tag_alias_registry.cpp +++ b/contrib/catch2/src/catch2/internal/catch_tag_alias_registry.cpp diff --git a/src/catch2/internal/catch_tag_alias_registry.hpp b/contrib/catch2/src/catch2/internal/catch_tag_alias_registry.hpp index 64c0f8f..64c0f8f 100644 --- a/src/catch2/internal/catch_tag_alias_registry.hpp +++ b/contrib/catch2/src/catch2/internal/catch_tag_alias_registry.hpp diff --git a/src/catch2/internal/catch_template_test_registry.hpp b/contrib/catch2/src/catch2/internal/catch_template_test_registry.hpp index 0ea354b..0ea354b 100644 --- a/src/catch2/internal/catch_template_test_registry.hpp +++ b/contrib/catch2/src/catch2/internal/catch_template_test_registry.hpp diff --git a/src/catch2/internal/catch_test_case_info_hasher.cpp b/contrib/catch2/src/catch2/internal/catch_test_case_info_hasher.cpp index e1731eb..e1731eb 100644 --- a/src/catch2/internal/catch_test_case_info_hasher.cpp +++ b/contrib/catch2/src/catch2/internal/catch_test_case_info_hasher.cpp diff --git a/src/catch2/internal/catch_test_case_info_hasher.hpp b/contrib/catch2/src/catch2/internal/catch_test_case_info_hasher.hpp index b0422dc..b0422dc 100644 --- a/src/catch2/internal/catch_test_case_info_hasher.hpp +++ b/contrib/catch2/src/catch2/internal/catch_test_case_info_hasher.hpp diff --git a/src/catch2/internal/catch_test_case_registry_impl.cpp b/contrib/catch2/src/catch2/internal/catch_test_case_registry_impl.cpp index e77e7bc..e77e7bc 100644 --- a/src/catch2/internal/catch_test_case_registry_impl.cpp +++ b/contrib/catch2/src/catch2/internal/catch_test_case_registry_impl.cpp diff --git a/src/catch2/internal/catch_test_case_registry_impl.hpp b/contrib/catch2/src/catch2/internal/catch_test_case_registry_impl.hpp index fbca89f..fbca89f 100644 --- a/src/catch2/internal/catch_test_case_registry_impl.hpp +++ b/contrib/catch2/src/catch2/internal/catch_test_case_registry_impl.hpp diff --git a/src/catch2/internal/catch_test_case_tracker.cpp b/contrib/catch2/src/catch2/internal/catch_test_case_tracker.cpp index 1470b91..1470b91 100644 --- a/src/catch2/internal/catch_test_case_tracker.cpp +++ b/contrib/catch2/src/catch2/internal/catch_test_case_tracker.cpp diff --git a/src/catch2/internal/catch_test_case_tracker.hpp b/contrib/catch2/src/catch2/internal/catch_test_case_tracker.hpp index 50278c9..50278c9 100644 --- a/src/catch2/internal/catch_test_case_tracker.hpp +++ b/contrib/catch2/src/catch2/internal/catch_test_case_tracker.hpp diff --git a/src/catch2/internal/catch_test_failure_exception.cpp b/contrib/catch2/src/catch2/internal/catch_test_failure_exception.cpp index 8ea3131..8ea3131 100644 --- a/src/catch2/internal/catch_test_failure_exception.cpp +++ b/contrib/catch2/src/catch2/internal/catch_test_failure_exception.cpp diff --git a/src/catch2/internal/catch_test_failure_exception.hpp b/contrib/catch2/src/catch2/internal/catch_test_failure_exception.hpp index 1ef8836..1ef8836 100644 --- a/src/catch2/internal/catch_test_failure_exception.hpp +++ b/contrib/catch2/src/catch2/internal/catch_test_failure_exception.hpp diff --git a/src/catch2/internal/catch_test_macro_impl.hpp b/contrib/catch2/src/catch2/internal/catch_test_macro_impl.hpp index ccd5bb3..ccd5bb3 100644 --- a/src/catch2/internal/catch_test_macro_impl.hpp +++ b/contrib/catch2/src/catch2/internal/catch_test_macro_impl.hpp diff --git a/src/catch2/internal/catch_test_registry.cpp b/contrib/catch2/src/catch2/internal/catch_test_registry.cpp index d017c50..d017c50 100644 --- a/src/catch2/internal/catch_test_registry.cpp +++ b/contrib/catch2/src/catch2/internal/catch_test_registry.cpp diff --git a/src/catch2/internal/catch_test_registry.hpp b/contrib/catch2/src/catch2/internal/catch_test_registry.hpp index 5c3a226..5c3a226 100644 --- a/src/catch2/internal/catch_test_registry.hpp +++ b/contrib/catch2/src/catch2/internal/catch_test_registry.hpp diff --git a/src/catch2/internal/catch_test_run_info.hpp b/contrib/catch2/src/catch2/internal/catch_test_run_info.hpp index 90357b0..90357b0 100644 --- a/src/catch2/internal/catch_test_run_info.hpp +++ b/contrib/catch2/src/catch2/internal/catch_test_run_info.hpp diff --git a/src/catch2/internal/catch_test_spec_parser.cpp b/contrib/catch2/src/catch2/internal/catch_test_spec_parser.cpp index d6e4cb5..d6e4cb5 100644 --- a/src/catch2/internal/catch_test_spec_parser.cpp +++ b/contrib/catch2/src/catch2/internal/catch_test_spec_parser.cpp diff --git a/src/catch2/internal/catch_test_spec_parser.hpp b/contrib/catch2/src/catch2/internal/catch_test_spec_parser.hpp index aa2917d..aa2917d 100644 --- a/src/catch2/internal/catch_test_spec_parser.hpp +++ b/contrib/catch2/src/catch2/internal/catch_test_spec_parser.hpp diff --git a/src/catch2/internal/catch_textflow.cpp b/contrib/catch2/src/catch2/internal/catch_textflow.cpp index 1c21d20..1c21d20 100644 --- a/src/catch2/internal/catch_textflow.cpp +++ b/contrib/catch2/src/catch2/internal/catch_textflow.cpp diff --git a/src/catch2/internal/catch_textflow.hpp b/contrib/catch2/src/catch2/internal/catch_textflow.hpp index 2d9d78a..2d9d78a 100644 --- a/src/catch2/internal/catch_textflow.hpp +++ b/contrib/catch2/src/catch2/internal/catch_textflow.hpp diff --git a/src/catch2/internal/catch_to_string.hpp b/contrib/catch2/src/catch2/internal/catch_to_string.hpp index c746216..c746216 100644 --- a/src/catch2/internal/catch_to_string.hpp +++ b/contrib/catch2/src/catch2/internal/catch_to_string.hpp diff --git a/src/catch2/internal/catch_uncaught_exceptions.cpp b/contrib/catch2/src/catch2/internal/catch_uncaught_exceptions.cpp index 8cfabc0..8cfabc0 100644 --- a/src/catch2/internal/catch_uncaught_exceptions.cpp +++ b/contrib/catch2/src/catch2/internal/catch_uncaught_exceptions.cpp diff --git a/src/catch2/internal/catch_uncaught_exceptions.hpp b/contrib/catch2/src/catch2/internal/catch_uncaught_exceptions.hpp index 8520864..8520864 100644 --- a/src/catch2/internal/catch_uncaught_exceptions.hpp +++ b/contrib/catch2/src/catch2/internal/catch_uncaught_exceptions.hpp diff --git a/src/catch2/internal/catch_uniform_floating_point_distribution.hpp b/contrib/catch2/src/catch2/internal/catch_uniform_floating_point_distribution.hpp index 23d03b4..23d03b4 100644 --- a/src/catch2/internal/catch_uniform_floating_point_distribution.hpp +++ b/contrib/catch2/src/catch2/internal/catch_uniform_floating_point_distribution.hpp diff --git a/src/catch2/internal/catch_uniform_integer_distribution.hpp b/contrib/catch2/src/catch2/internal/catch_uniform_integer_distribution.hpp index 799a93e..799a93e 100644 --- a/src/catch2/internal/catch_uniform_integer_distribution.hpp +++ b/contrib/catch2/src/catch2/internal/catch_uniform_integer_distribution.hpp diff --git a/src/catch2/internal/catch_unique_name.hpp b/contrib/catch2/src/catch2/internal/catch_unique_name.hpp index c6e1c2c..c6e1c2c 100644 --- a/src/catch2/internal/catch_unique_name.hpp +++ b/contrib/catch2/src/catch2/internal/catch_unique_name.hpp diff --git a/src/catch2/internal/catch_unique_ptr.hpp b/contrib/catch2/src/catch2/internal/catch_unique_ptr.hpp index 49cbc78..49cbc78 100644 --- a/src/catch2/internal/catch_unique_ptr.hpp +++ b/contrib/catch2/src/catch2/internal/catch_unique_ptr.hpp diff --git a/src/catch2/internal/catch_void_type.hpp b/contrib/catch2/src/catch2/internal/catch_void_type.hpp index dacc83d..dacc83d 100644 --- a/src/catch2/internal/catch_void_type.hpp +++ b/contrib/catch2/src/catch2/internal/catch_void_type.hpp diff --git a/src/catch2/internal/catch_wildcard_pattern.cpp b/contrib/catch2/src/catch2/internal/catch_wildcard_pattern.cpp index 09c5a40..09c5a40 100644 --- a/src/catch2/internal/catch_wildcard_pattern.cpp +++ b/contrib/catch2/src/catch2/internal/catch_wildcard_pattern.cpp diff --git a/src/catch2/internal/catch_wildcard_pattern.hpp b/contrib/catch2/src/catch2/internal/catch_wildcard_pattern.hpp index 4f41085..4f41085 100644 --- a/src/catch2/internal/catch_wildcard_pattern.hpp +++ b/contrib/catch2/src/catch2/internal/catch_wildcard_pattern.hpp diff --git a/src/catch2/internal/catch_windows_h_proxy.hpp b/contrib/catch2/src/catch2/internal/catch_windows_h_proxy.hpp index e3b9149..e3b9149 100644 --- a/src/catch2/internal/catch_windows_h_proxy.hpp +++ b/contrib/catch2/src/catch2/internal/catch_windows_h_proxy.hpp diff --git a/src/catch2/internal/catch_xmlwriter.cpp b/contrib/catch2/src/catch2/internal/catch_xmlwriter.cpp index ccf63a5..ccf63a5 100644 --- a/src/catch2/internal/catch_xmlwriter.cpp +++ b/contrib/catch2/src/catch2/internal/catch_xmlwriter.cpp diff --git a/src/catch2/internal/catch_xmlwriter.hpp b/contrib/catch2/src/catch2/internal/catch_xmlwriter.hpp index 22b42c5..22b42c5 100644 --- a/src/catch2/internal/catch_xmlwriter.hpp +++ b/contrib/catch2/src/catch2/internal/catch_xmlwriter.hpp diff --git a/src/catch2/matchers/catch_matchers.cpp b/contrib/catch2/src/catch2/matchers/catch_matchers.cpp index 123b304..123b304 100644 --- a/src/catch2/matchers/catch_matchers.cpp +++ b/contrib/catch2/src/catch2/matchers/catch_matchers.cpp diff --git a/src/catch2/matchers/catch_matchers.hpp b/contrib/catch2/src/catch2/matchers/catch_matchers.hpp index 3d996c3..3d996c3 100644 --- a/src/catch2/matchers/catch_matchers.hpp +++ b/contrib/catch2/src/catch2/matchers/catch_matchers.hpp diff --git a/src/catch2/matchers/catch_matchers_all.hpp b/contrib/catch2/src/catch2/matchers/catch_matchers_all.hpp index 83fe538..83fe538 100644 --- a/src/catch2/matchers/catch_matchers_all.hpp +++ b/contrib/catch2/src/catch2/matchers/catch_matchers_all.hpp diff --git a/src/catch2/matchers/catch_matchers_container_properties.cpp b/contrib/catch2/src/catch2/matchers/catch_matchers_container_properties.cpp index f0c535b..f0c535b 100644 --- a/src/catch2/matchers/catch_matchers_container_properties.cpp +++ b/contrib/catch2/src/catch2/matchers/catch_matchers_container_properties.cpp diff --git a/src/catch2/matchers/catch_matchers_container_properties.hpp b/contrib/catch2/src/catch2/matchers/catch_matchers_container_properties.hpp index 5f3fc7f..5f3fc7f 100644 --- a/src/catch2/matchers/catch_matchers_container_properties.hpp +++ b/contrib/catch2/src/catch2/matchers/catch_matchers_container_properties.hpp diff --git a/src/catch2/matchers/catch_matchers_contains.hpp b/contrib/catch2/src/catch2/matchers/catch_matchers_contains.hpp index 877d692..877d692 100644 --- a/src/catch2/matchers/catch_matchers_contains.hpp +++ b/contrib/catch2/src/catch2/matchers/catch_matchers_contains.hpp diff --git a/src/catch2/matchers/catch_matchers_exception.cpp b/contrib/catch2/src/catch2/matchers/catch_matchers_exception.cpp index 8147390..8147390 100644 --- a/src/catch2/matchers/catch_matchers_exception.cpp +++ b/contrib/catch2/src/catch2/matchers/catch_matchers_exception.cpp diff --git a/src/catch2/matchers/catch_matchers_exception.hpp b/contrib/catch2/src/catch2/matchers/catch_matchers_exception.hpp index e7c3a63..e7c3a63 100644 --- a/src/catch2/matchers/catch_matchers_exception.hpp +++ b/contrib/catch2/src/catch2/matchers/catch_matchers_exception.hpp diff --git a/src/catch2/matchers/catch_matchers_floating_point.cpp b/contrib/catch2/src/catch2/matchers/catch_matchers_floating_point.cpp index fc7b444..fc7b444 100644 --- a/src/catch2/matchers/catch_matchers_floating_point.cpp +++ b/contrib/catch2/src/catch2/matchers/catch_matchers_floating_point.cpp diff --git a/src/catch2/matchers/catch_matchers_floating_point.hpp b/contrib/catch2/src/catch2/matchers/catch_matchers_floating_point.hpp index 7681663..7681663 100644 --- a/src/catch2/matchers/catch_matchers_floating_point.hpp +++ b/contrib/catch2/src/catch2/matchers/catch_matchers_floating_point.hpp diff --git a/src/catch2/matchers/catch_matchers_predicate.cpp b/contrib/catch2/src/catch2/matchers/catch_matchers_predicate.cpp index f544537..f544537 100644 --- a/src/catch2/matchers/catch_matchers_predicate.cpp +++ b/contrib/catch2/src/catch2/matchers/catch_matchers_predicate.cpp diff --git a/src/catch2/matchers/catch_matchers_predicate.hpp b/contrib/catch2/src/catch2/matchers/catch_matchers_predicate.hpp index 2d1cc33..2d1cc33 100644 --- a/src/catch2/matchers/catch_matchers_predicate.hpp +++ b/contrib/catch2/src/catch2/matchers/catch_matchers_predicate.hpp diff --git a/src/catch2/matchers/catch_matchers_quantifiers.cpp b/contrib/catch2/src/catch2/matchers/catch_matchers_quantifiers.cpp index 5a75246..5a75246 100644 --- a/src/catch2/matchers/catch_matchers_quantifiers.cpp +++ b/contrib/catch2/src/catch2/matchers/catch_matchers_quantifiers.cpp diff --git a/src/catch2/matchers/catch_matchers_quantifiers.hpp b/contrib/catch2/src/catch2/matchers/catch_matchers_quantifiers.hpp index 977b0c7..977b0c7 100644 --- a/src/catch2/matchers/catch_matchers_quantifiers.hpp +++ b/contrib/catch2/src/catch2/matchers/catch_matchers_quantifiers.hpp diff --git a/src/catch2/matchers/catch_matchers_range_equals.hpp b/contrib/catch2/src/catch2/matchers/catch_matchers_range_equals.hpp index 8130f60..8130f60 100644 --- a/src/catch2/matchers/catch_matchers_range_equals.hpp +++ b/contrib/catch2/src/catch2/matchers/catch_matchers_range_equals.hpp diff --git a/src/catch2/matchers/catch_matchers_string.cpp b/contrib/catch2/src/catch2/matchers/catch_matchers_string.cpp index 5500284..5500284 100644 --- a/src/catch2/matchers/catch_matchers_string.cpp +++ b/contrib/catch2/src/catch2/matchers/catch_matchers_string.cpp diff --git a/src/catch2/matchers/catch_matchers_string.hpp b/contrib/catch2/src/catch2/matchers/catch_matchers_string.hpp index 61a385d..61a385d 100644 --- a/src/catch2/matchers/catch_matchers_string.hpp +++ b/contrib/catch2/src/catch2/matchers/catch_matchers_string.hpp diff --git a/src/catch2/matchers/catch_matchers_templated.cpp b/contrib/catch2/src/catch2/matchers/catch_matchers_templated.cpp index 2fc529d..2fc529d 100644 --- a/src/catch2/matchers/catch_matchers_templated.cpp +++ b/contrib/catch2/src/catch2/matchers/catch_matchers_templated.cpp diff --git a/src/catch2/matchers/catch_matchers_templated.hpp b/contrib/catch2/src/catch2/matchers/catch_matchers_templated.hpp index ba0661a..ba0661a 100644 --- a/src/catch2/matchers/catch_matchers_templated.hpp +++ b/contrib/catch2/src/catch2/matchers/catch_matchers_templated.hpp diff --git a/src/catch2/matchers/catch_matchers_vector.hpp b/contrib/catch2/src/catch2/matchers/catch_matchers_vector.hpp index fffbfdf..fffbfdf 100644 --- a/src/catch2/matchers/catch_matchers_vector.hpp +++ b/contrib/catch2/src/catch2/matchers/catch_matchers_vector.hpp diff --git a/src/catch2/matchers/internal/catch_matchers_impl.cpp b/contrib/catch2/src/catch2/matchers/internal/catch_matchers_impl.cpp index 41b462e..41b462e 100644 --- a/src/catch2/matchers/internal/catch_matchers_impl.cpp +++ b/contrib/catch2/src/catch2/matchers/internal/catch_matchers_impl.cpp diff --git a/src/catch2/matchers/internal/catch_matchers_impl.hpp b/contrib/catch2/src/catch2/matchers/internal/catch_matchers_impl.hpp index 24a3f8b..24a3f8b 100644 --- a/src/catch2/matchers/internal/catch_matchers_impl.hpp +++ b/contrib/catch2/src/catch2/matchers/internal/catch_matchers_impl.hpp diff --git a/src/catch2/meson.build b/contrib/catch2/src/catch2/meson.build index 2eb5399..2eb5399 100644 --- a/src/catch2/meson.build +++ b/contrib/catch2/src/catch2/meson.build diff --git a/src/catch2/reporters/catch_reporter_automake.cpp b/contrib/catch2/src/catch2/reporters/catch_reporter_automake.cpp index 5e506a6..5e506a6 100644 --- a/src/catch2/reporters/catch_reporter_automake.cpp +++ b/contrib/catch2/src/catch2/reporters/catch_reporter_automake.cpp diff --git a/src/catch2/reporters/catch_reporter_automake.hpp b/contrib/catch2/src/catch2/reporters/catch_reporter_automake.hpp index a639428..a639428 100644 --- a/src/catch2/reporters/catch_reporter_automake.hpp +++ b/contrib/catch2/src/catch2/reporters/catch_reporter_automake.hpp diff --git a/src/catch2/reporters/catch_reporter_common_base.cpp b/contrib/catch2/src/catch2/reporters/catch_reporter_common_base.cpp index a1ca76a..a1ca76a 100644 --- a/src/catch2/reporters/catch_reporter_common_base.cpp +++ b/contrib/catch2/src/catch2/reporters/catch_reporter_common_base.cpp diff --git a/src/catch2/reporters/catch_reporter_common_base.hpp b/contrib/catch2/src/catch2/reporters/catch_reporter_common_base.hpp index b4f0a9f..b4f0a9f 100644 --- a/src/catch2/reporters/catch_reporter_common_base.hpp +++ b/contrib/catch2/src/catch2/reporters/catch_reporter_common_base.hpp diff --git a/src/catch2/reporters/catch_reporter_compact.cpp b/contrib/catch2/src/catch2/reporters/catch_reporter_compact.cpp index 0f85594..0f85594 100644 --- a/src/catch2/reporters/catch_reporter_compact.cpp +++ b/contrib/catch2/src/catch2/reporters/catch_reporter_compact.cpp diff --git a/src/catch2/reporters/catch_reporter_compact.hpp b/contrib/catch2/src/catch2/reporters/catch_reporter_compact.hpp index d95bbff..d95bbff 100644 --- a/src/catch2/reporters/catch_reporter_compact.hpp +++ b/contrib/catch2/src/catch2/reporters/catch_reporter_compact.hpp diff --git a/src/catch2/reporters/catch_reporter_console.cpp b/contrib/catch2/src/catch2/reporters/catch_reporter_console.cpp index 9529f39..9529f39 100644 --- a/src/catch2/reporters/catch_reporter_console.cpp +++ b/contrib/catch2/src/catch2/reporters/catch_reporter_console.cpp diff --git a/src/catch2/reporters/catch_reporter_console.hpp b/contrib/catch2/src/catch2/reporters/catch_reporter_console.hpp index 2437726..2437726 100644 --- a/src/catch2/reporters/catch_reporter_console.hpp +++ b/contrib/catch2/src/catch2/reporters/catch_reporter_console.hpp diff --git a/src/catch2/reporters/catch_reporter_cumulative_base.cpp b/contrib/catch2/src/catch2/reporters/catch_reporter_cumulative_base.cpp index 0916963..0916963 100644 --- a/src/catch2/reporters/catch_reporter_cumulative_base.cpp +++ b/contrib/catch2/src/catch2/reporters/catch_reporter_cumulative_base.cpp diff --git a/src/catch2/reporters/catch_reporter_cumulative_base.hpp b/contrib/catch2/src/catch2/reporters/catch_reporter_cumulative_base.hpp index 267b39f..267b39f 100644 --- a/src/catch2/reporters/catch_reporter_cumulative_base.hpp +++ b/contrib/catch2/src/catch2/reporters/catch_reporter_cumulative_base.hpp diff --git a/src/catch2/reporters/catch_reporter_event_listener.cpp b/contrib/catch2/src/catch2/reporters/catch_reporter_event_listener.cpp index e94063b..e94063b 100644 --- a/src/catch2/reporters/catch_reporter_event_listener.cpp +++ b/contrib/catch2/src/catch2/reporters/catch_reporter_event_listener.cpp diff --git a/src/catch2/reporters/catch_reporter_event_listener.hpp b/contrib/catch2/src/catch2/reporters/catch_reporter_event_listener.hpp index 346263e..346263e 100644 --- a/src/catch2/reporters/catch_reporter_event_listener.hpp +++ b/contrib/catch2/src/catch2/reporters/catch_reporter_event_listener.hpp diff --git a/src/catch2/reporters/catch_reporter_helpers.cpp b/contrib/catch2/src/catch2/reporters/catch_reporter_helpers.cpp index ffb32ff..ffb32ff 100644 --- a/src/catch2/reporters/catch_reporter_helpers.cpp +++ b/contrib/catch2/src/catch2/reporters/catch_reporter_helpers.cpp diff --git a/src/catch2/reporters/catch_reporter_helpers.hpp b/contrib/catch2/src/catch2/reporters/catch_reporter_helpers.hpp index 316cb40..316cb40 100644 --- a/src/catch2/reporters/catch_reporter_helpers.hpp +++ b/contrib/catch2/src/catch2/reporters/catch_reporter_helpers.hpp diff --git a/src/catch2/reporters/catch_reporter_json.cpp b/contrib/catch2/src/catch2/reporters/catch_reporter_json.cpp index 6a8e655..6a8e655 100644 --- a/src/catch2/reporters/catch_reporter_json.cpp +++ b/contrib/catch2/src/catch2/reporters/catch_reporter_json.cpp diff --git a/src/catch2/reporters/catch_reporter_json.hpp b/contrib/catch2/src/catch2/reporters/catch_reporter_json.hpp index c938ca3..c938ca3 100644 --- a/src/catch2/reporters/catch_reporter_json.hpp +++ b/contrib/catch2/src/catch2/reporters/catch_reporter_json.hpp diff --git a/src/catch2/reporters/catch_reporter_junit.cpp b/contrib/catch2/src/catch2/reporters/catch_reporter_junit.cpp index 27bdfe2..27bdfe2 100644 --- a/src/catch2/reporters/catch_reporter_junit.cpp +++ b/contrib/catch2/src/catch2/reporters/catch_reporter_junit.cpp diff --git a/src/catch2/reporters/catch_reporter_junit.hpp b/contrib/catch2/src/catch2/reporters/catch_reporter_junit.hpp index 7cb53c2..7cb53c2 100644 --- a/src/catch2/reporters/catch_reporter_junit.hpp +++ b/contrib/catch2/src/catch2/reporters/catch_reporter_junit.hpp diff --git a/src/catch2/reporters/catch_reporter_multi.cpp b/contrib/catch2/src/catch2/reporters/catch_reporter_multi.cpp index 531902b..531902b 100644 --- a/src/catch2/reporters/catch_reporter_multi.cpp +++ b/contrib/catch2/src/catch2/reporters/catch_reporter_multi.cpp diff --git a/src/catch2/reporters/catch_reporter_multi.hpp b/contrib/catch2/src/catch2/reporters/catch_reporter_multi.hpp index 6611383..6611383 100644 --- a/src/catch2/reporters/catch_reporter_multi.hpp +++ b/contrib/catch2/src/catch2/reporters/catch_reporter_multi.hpp diff --git a/src/catch2/reporters/catch_reporter_registrars.cpp b/contrib/catch2/src/catch2/reporters/catch_reporter_registrars.cpp index 2a3ac95..2a3ac95 100644 --- a/src/catch2/reporters/catch_reporter_registrars.cpp +++ b/contrib/catch2/src/catch2/reporters/catch_reporter_registrars.cpp diff --git a/src/catch2/reporters/catch_reporter_registrars.hpp b/contrib/catch2/src/catch2/reporters/catch_reporter_registrars.hpp index a93963f..a93963f 100644 --- a/src/catch2/reporters/catch_reporter_registrars.hpp +++ b/contrib/catch2/src/catch2/reporters/catch_reporter_registrars.hpp diff --git a/src/catch2/reporters/catch_reporter_sonarqube.cpp b/contrib/catch2/src/catch2/reporters/catch_reporter_sonarqube.cpp index 2c3eb1c..2c3eb1c 100644 --- a/src/catch2/reporters/catch_reporter_sonarqube.cpp +++ b/contrib/catch2/src/catch2/reporters/catch_reporter_sonarqube.cpp diff --git a/src/catch2/reporters/catch_reporter_sonarqube.hpp b/contrib/catch2/src/catch2/reporters/catch_reporter_sonarqube.hpp index 509f411..509f411 100644 --- a/src/catch2/reporters/catch_reporter_sonarqube.hpp +++ b/contrib/catch2/src/catch2/reporters/catch_reporter_sonarqube.hpp diff --git a/src/catch2/reporters/catch_reporter_streaming_base.cpp b/contrib/catch2/src/catch2/reporters/catch_reporter_streaming_base.cpp index f1cc425..f1cc425 100644 --- a/src/catch2/reporters/catch_reporter_streaming_base.cpp +++ b/contrib/catch2/src/catch2/reporters/catch_reporter_streaming_base.cpp diff --git a/src/catch2/reporters/catch_reporter_streaming_base.hpp b/contrib/catch2/src/catch2/reporters/catch_reporter_streaming_base.hpp index 5448000..5448000 100644 --- a/src/catch2/reporters/catch_reporter_streaming_base.hpp +++ b/contrib/catch2/src/catch2/reporters/catch_reporter_streaming_base.hpp diff --git a/src/catch2/reporters/catch_reporter_tap.cpp b/contrib/catch2/src/catch2/reporters/catch_reporter_tap.cpp index 67d406f..67d406f 100644 --- a/src/catch2/reporters/catch_reporter_tap.cpp +++ b/contrib/catch2/src/catch2/reporters/catch_reporter_tap.cpp diff --git a/src/catch2/reporters/catch_reporter_tap.hpp b/contrib/catch2/src/catch2/reporters/catch_reporter_tap.hpp index e6889bb..e6889bb 100644 --- a/src/catch2/reporters/catch_reporter_tap.hpp +++ b/contrib/catch2/src/catch2/reporters/catch_reporter_tap.hpp diff --git a/src/catch2/reporters/catch_reporter_teamcity.cpp b/contrib/catch2/src/catch2/reporters/catch_reporter_teamcity.cpp index 38aa55a..38aa55a 100644 --- a/src/catch2/reporters/catch_reporter_teamcity.cpp +++ b/contrib/catch2/src/catch2/reporters/catch_reporter_teamcity.cpp diff --git a/src/catch2/reporters/catch_reporter_teamcity.hpp b/contrib/catch2/src/catch2/reporters/catch_reporter_teamcity.hpp index 662e989..662e989 100644 --- a/src/catch2/reporters/catch_reporter_teamcity.hpp +++ b/contrib/catch2/src/catch2/reporters/catch_reporter_teamcity.hpp diff --git a/src/catch2/reporters/catch_reporter_xml.cpp b/contrib/catch2/src/catch2/reporters/catch_reporter_xml.cpp index 35a3028..35a3028 100644 --- a/src/catch2/reporters/catch_reporter_xml.cpp +++ b/contrib/catch2/src/catch2/reporters/catch_reporter_xml.cpp diff --git a/src/catch2/reporters/catch_reporter_xml.hpp b/contrib/catch2/src/catch2/reporters/catch_reporter_xml.hpp index bead7a8..bead7a8 100644 --- a/src/catch2/reporters/catch_reporter_xml.hpp +++ b/contrib/catch2/src/catch2/reporters/catch_reporter_xml.hpp diff --git a/src/catch2/reporters/catch_reporters_all.hpp b/contrib/catch2/src/catch2/reporters/catch_reporters_all.hpp index 5c713fe..5c713fe 100644 --- a/src/catch2/reporters/catch_reporters_all.hpp +++ b/contrib/catch2/src/catch2/reporters/catch_reporters_all.hpp diff --git a/tests/BUILD.bazel b/contrib/catch2/tests/BUILD.bazel index 5f0362f..5f0362f 100644 --- a/tests/BUILD.bazel +++ b/contrib/catch2/tests/BUILD.bazel diff --git a/tests/CMakeLists.txt b/contrib/catch2/tests/CMakeLists.txt index 37a5977..37a5977 100644 --- a/tests/CMakeLists.txt +++ b/contrib/catch2/tests/CMakeLists.txt diff --git a/tests/ExtraTests/CMakeLists.txt b/contrib/catch2/tests/ExtraTests/CMakeLists.txt index 3c38d67..3c38d67 100644 --- a/tests/ExtraTests/CMakeLists.txt +++ b/contrib/catch2/tests/ExtraTests/CMakeLists.txt diff --git a/tests/ExtraTests/ToDo.txt b/contrib/catch2/tests/ExtraTests/ToDo.txt index a2ed36b..a2ed36b 100644 --- a/tests/ExtraTests/ToDo.txt +++ b/contrib/catch2/tests/ExtraTests/ToDo.txt diff --git a/tests/ExtraTests/X01-PrefixedMacros.cpp b/contrib/catch2/tests/ExtraTests/X01-PrefixedMacros.cpp index d1c246e..d1c246e 100644 --- a/tests/ExtraTests/X01-PrefixedMacros.cpp +++ b/contrib/catch2/tests/ExtraTests/X01-PrefixedMacros.cpp diff --git a/tests/ExtraTests/X02-DisabledMacros.cpp b/contrib/catch2/tests/ExtraTests/X02-DisabledMacros.cpp index 231adfb..231adfb 100644 --- a/tests/ExtraTests/X02-DisabledMacros.cpp +++ b/contrib/catch2/tests/ExtraTests/X02-DisabledMacros.cpp diff --git a/tests/ExtraTests/X03-DisabledExceptions-DefaultHandler.cpp b/contrib/catch2/tests/ExtraTests/X03-DisabledExceptions-DefaultHandler.cpp index 5b6d4b1..5b6d4b1 100644 --- a/tests/ExtraTests/X03-DisabledExceptions-DefaultHandler.cpp +++ b/contrib/catch2/tests/ExtraTests/X03-DisabledExceptions-DefaultHandler.cpp diff --git a/tests/ExtraTests/X04-DisabledExceptions-CustomHandler.cpp b/contrib/catch2/tests/ExtraTests/X04-DisabledExceptions-CustomHandler.cpp index e1f1e70..e1f1e70 100644 --- a/tests/ExtraTests/X04-DisabledExceptions-CustomHandler.cpp +++ b/contrib/catch2/tests/ExtraTests/X04-DisabledExceptions-CustomHandler.cpp diff --git a/tests/ExtraTests/X05-DeferredStaticChecks.cpp b/contrib/catch2/tests/ExtraTests/X05-DeferredStaticChecks.cpp index 8005dbc..8005dbc 100644 --- a/tests/ExtraTests/X05-DeferredStaticChecks.cpp +++ b/contrib/catch2/tests/ExtraTests/X05-DeferredStaticChecks.cpp diff --git a/tests/ExtraTests/X10-FallbackStringifier.cpp b/contrib/catch2/tests/ExtraTests/X10-FallbackStringifier.cpp index 8525a81..8525a81 100644 --- a/tests/ExtraTests/X10-FallbackStringifier.cpp +++ b/contrib/catch2/tests/ExtraTests/X10-FallbackStringifier.cpp diff --git a/tests/ExtraTests/X11-DisableStringification.cpp b/contrib/catch2/tests/ExtraTests/X11-DisableStringification.cpp index f899627..f899627 100644 --- a/tests/ExtraTests/X11-DisableStringification.cpp +++ b/contrib/catch2/tests/ExtraTests/X11-DisableStringification.cpp diff --git a/tests/ExtraTests/X12-CustomDebugBreakMacro.cpp b/contrib/catch2/tests/ExtraTests/X12-CustomDebugBreakMacro.cpp index e6d3e38..e6d3e38 100644 --- a/tests/ExtraTests/X12-CustomDebugBreakMacro.cpp +++ b/contrib/catch2/tests/ExtraTests/X12-CustomDebugBreakMacro.cpp diff --git a/tests/ExtraTests/X20-AssertionStartingEventGoesBeforeAssertionIsEvaluated.cpp b/contrib/catch2/tests/ExtraTests/X20-AssertionStartingEventGoesBeforeAssertionIsEvaluated.cpp index 6f44bf6..6f44bf6 100644 --- a/tests/ExtraTests/X20-AssertionStartingEventGoesBeforeAssertionIsEvaluated.cpp +++ b/contrib/catch2/tests/ExtraTests/X20-AssertionStartingEventGoesBeforeAssertionIsEvaluated.cpp diff --git a/tests/ExtraTests/X21-PartialTestCaseEvents.cpp b/contrib/catch2/tests/ExtraTests/X21-PartialTestCaseEvents.cpp index aa5204c..aa5204c 100644 --- a/tests/ExtraTests/X21-PartialTestCaseEvents.cpp +++ b/contrib/catch2/tests/ExtraTests/X21-PartialTestCaseEvents.cpp diff --git a/tests/ExtraTests/X22-BenchmarksInCumulativeReporter.cpp b/contrib/catch2/tests/ExtraTests/X22-BenchmarksInCumulativeReporter.cpp index 33399a6..33399a6 100644 --- a/tests/ExtraTests/X22-BenchmarksInCumulativeReporter.cpp +++ b/contrib/catch2/tests/ExtraTests/X22-BenchmarksInCumulativeReporter.cpp diff --git a/tests/ExtraTests/X23-CasingInReporterNames.cpp b/contrib/catch2/tests/ExtraTests/X23-CasingInReporterNames.cpp index 3052373..3052373 100644 --- a/tests/ExtraTests/X23-CasingInReporterNames.cpp +++ b/contrib/catch2/tests/ExtraTests/X23-CasingInReporterNames.cpp diff --git a/tests/ExtraTests/X24-ListenerStdoutCaptureInMultireporter.cpp b/contrib/catch2/tests/ExtraTests/X24-ListenerStdoutCaptureInMultireporter.cpp index ffb706e..ffb706e 100644 --- a/tests/ExtraTests/X24-ListenerStdoutCaptureInMultireporter.cpp +++ b/contrib/catch2/tests/ExtraTests/X24-ListenerStdoutCaptureInMultireporter.cpp diff --git a/tests/ExtraTests/X25-ListenerCanAskForCapturedStdout.cpp b/contrib/catch2/tests/ExtraTests/X25-ListenerCanAskForCapturedStdout.cpp index de75ef4..de75ef4 100644 --- a/tests/ExtraTests/X25-ListenerCanAskForCapturedStdout.cpp +++ b/contrib/catch2/tests/ExtraTests/X25-ListenerCanAskForCapturedStdout.cpp diff --git a/tests/ExtraTests/X26-ReporterPreferencesForPassingAssertionsIsRespected.cpp b/contrib/catch2/tests/ExtraTests/X26-ReporterPreferencesForPassingAssertionsIsRespected.cpp index 90a5885..90a5885 100644 --- a/tests/ExtraTests/X26-ReporterPreferencesForPassingAssertionsIsRespected.cpp +++ b/contrib/catch2/tests/ExtraTests/X26-ReporterPreferencesForPassingAssertionsIsRespected.cpp diff --git a/tests/ExtraTests/X27-CapturedStdoutInTestCaseEvents.cpp b/contrib/catch2/tests/ExtraTests/X27-CapturedStdoutInTestCaseEvents.cpp index 61aaa02..61aaa02 100644 --- a/tests/ExtraTests/X27-CapturedStdoutInTestCaseEvents.cpp +++ b/contrib/catch2/tests/ExtraTests/X27-CapturedStdoutInTestCaseEvents.cpp diff --git a/tests/ExtraTests/X28-ListenersGetEventsBeforeReporters.cpp b/contrib/catch2/tests/ExtraTests/X28-ListenersGetEventsBeforeReporters.cpp index 4098c8b..4098c8b 100644 --- a/tests/ExtraTests/X28-ListenersGetEventsBeforeReporters.cpp +++ b/contrib/catch2/tests/ExtraTests/X28-ListenersGetEventsBeforeReporters.cpp diff --git a/tests/ExtraTests/X29-CustomArgumentsForReporters.cpp b/contrib/catch2/tests/ExtraTests/X29-CustomArgumentsForReporters.cpp index 13d9fc1..13d9fc1 100644 --- a/tests/ExtraTests/X29-CustomArgumentsForReporters.cpp +++ b/contrib/catch2/tests/ExtraTests/X29-CustomArgumentsForReporters.cpp diff --git a/tests/ExtraTests/X30-BazelReporter.cpp b/contrib/catch2/tests/ExtraTests/X30-BazelReporter.cpp index 2a25941..2a25941 100644 --- a/tests/ExtraTests/X30-BazelReporter.cpp +++ b/contrib/catch2/tests/ExtraTests/X30-BazelReporter.cpp diff --git a/tests/ExtraTests/X31-DuplicatedTestCases.cpp b/contrib/catch2/tests/ExtraTests/X31-DuplicatedTestCases.cpp index 0d09175..0d09175 100644 --- a/tests/ExtraTests/X31-DuplicatedTestCases.cpp +++ b/contrib/catch2/tests/ExtraTests/X31-DuplicatedTestCases.cpp diff --git a/tests/ExtraTests/X32-DuplicatedTestCasesDifferentTags.cpp b/contrib/catch2/tests/ExtraTests/X32-DuplicatedTestCasesDifferentTags.cpp index b0aa417..b0aa417 100644 --- a/tests/ExtraTests/X32-DuplicatedTestCasesDifferentTags.cpp +++ b/contrib/catch2/tests/ExtraTests/X32-DuplicatedTestCasesDifferentTags.cpp diff --git a/tests/ExtraTests/X33-DuplicatedTestCaseMethods.cpp b/contrib/catch2/tests/ExtraTests/X33-DuplicatedTestCaseMethods.cpp index d3c3449..d3c3449 100644 --- a/tests/ExtraTests/X33-DuplicatedTestCaseMethods.cpp +++ b/contrib/catch2/tests/ExtraTests/X33-DuplicatedTestCaseMethods.cpp diff --git a/tests/ExtraTests/X34-DuplicatedTestCaseMethodsDifferentFixtures.cpp b/contrib/catch2/tests/ExtraTests/X34-DuplicatedTestCaseMethodsDifferentFixtures.cpp index 397cf15..397cf15 100644 --- a/tests/ExtraTests/X34-DuplicatedTestCaseMethodsDifferentFixtures.cpp +++ b/contrib/catch2/tests/ExtraTests/X34-DuplicatedTestCaseMethodsDifferentFixtures.cpp diff --git a/tests/ExtraTests/X35-DuplicatedReporterNames.cpp b/contrib/catch2/tests/ExtraTests/X35-DuplicatedReporterNames.cpp index a978448..a978448 100644 --- a/tests/ExtraTests/X35-DuplicatedReporterNames.cpp +++ b/contrib/catch2/tests/ExtraTests/X35-DuplicatedReporterNames.cpp diff --git a/tests/ExtraTests/X36-ReportingCrashWithJunitReporter.cpp b/contrib/catch2/tests/ExtraTests/X36-ReportingCrashWithJunitReporter.cpp index 34f4cd8..34f4cd8 100644 --- a/tests/ExtraTests/X36-ReportingCrashWithJunitReporter.cpp +++ b/contrib/catch2/tests/ExtraTests/X36-ReportingCrashWithJunitReporter.cpp diff --git a/tests/ExtraTests/X90-WindowsHeaderInclusion.cpp b/contrib/catch2/tests/ExtraTests/X90-WindowsHeaderInclusion.cpp index dff542a..dff542a 100644 --- a/tests/ExtraTests/X90-WindowsHeaderInclusion.cpp +++ b/contrib/catch2/tests/ExtraTests/X90-WindowsHeaderInclusion.cpp diff --git a/tests/ExtraTests/X91-AmalgamatedCatch.cpp b/contrib/catch2/tests/ExtraTests/X91-AmalgamatedCatch.cpp index 78d45a2..78d45a2 100644 --- a/tests/ExtraTests/X91-AmalgamatedCatch.cpp +++ b/contrib/catch2/tests/ExtraTests/X91-AmalgamatedCatch.cpp diff --git a/tests/ExtraTests/X92-NoTests.cpp b/contrib/catch2/tests/ExtraTests/X92-NoTests.cpp index 9591e54..9591e54 100644 --- a/tests/ExtraTests/X92-NoTests.cpp +++ b/contrib/catch2/tests/ExtraTests/X92-NoTests.cpp diff --git a/tests/ExtraTests/X93-AllSkipped.cpp b/contrib/catch2/tests/ExtraTests/X93-AllSkipped.cpp index 8e7d0af..8e7d0af 100644 --- a/tests/ExtraTests/X93-AllSkipped.cpp +++ b/contrib/catch2/tests/ExtraTests/X93-AllSkipped.cpp diff --git a/tests/SelfTest/Baselines/automake.std.approved.txt b/contrib/catch2/tests/SelfTest/Baselines/automake.std.approved.txt index 057be06..057be06 100644 --- a/tests/SelfTest/Baselines/automake.std.approved.txt +++ b/contrib/catch2/tests/SelfTest/Baselines/automake.std.approved.txt diff --git a/tests/SelfTest/Baselines/automake.sw.approved.txt b/contrib/catch2/tests/SelfTest/Baselines/automake.sw.approved.txt index 7be343a..7be343a 100644 --- a/tests/SelfTest/Baselines/automake.sw.approved.txt +++ b/contrib/catch2/tests/SelfTest/Baselines/automake.sw.approved.txt diff --git a/tests/SelfTest/Baselines/automake.sw.multi.approved.txt b/contrib/catch2/tests/SelfTest/Baselines/automake.sw.multi.approved.txt index 0cd24e4..0cd24e4 100644 --- a/tests/SelfTest/Baselines/automake.sw.multi.approved.txt +++ b/contrib/catch2/tests/SelfTest/Baselines/automake.sw.multi.approved.txt diff --git a/tests/SelfTest/Baselines/compact.sw.approved.txt b/contrib/catch2/tests/SelfTest/Baselines/compact.sw.approved.txt index b7f48cb..b7f48cb 100644 --- a/tests/SelfTest/Baselines/compact.sw.approved.txt +++ b/contrib/catch2/tests/SelfTest/Baselines/compact.sw.approved.txt diff --git a/tests/SelfTest/Baselines/compact.sw.multi.approved.txt b/contrib/catch2/tests/SelfTest/Baselines/compact.sw.multi.approved.txt index 9e3e537..9e3e537 100644 --- a/tests/SelfTest/Baselines/compact.sw.multi.approved.txt +++ b/contrib/catch2/tests/SelfTest/Baselines/compact.sw.multi.approved.txt diff --git a/tests/SelfTest/Baselines/console.std.approved.txt b/contrib/catch2/tests/SelfTest/Baselines/console.std.approved.txt index 495264b..495264b 100644 --- a/tests/SelfTest/Baselines/console.std.approved.txt +++ b/contrib/catch2/tests/SelfTest/Baselines/console.std.approved.txt diff --git a/tests/SelfTest/Baselines/console.sw.approved.txt b/contrib/catch2/tests/SelfTest/Baselines/console.sw.approved.txt index a53f40d..a53f40d 100644 --- a/tests/SelfTest/Baselines/console.sw.approved.txt +++ b/contrib/catch2/tests/SelfTest/Baselines/console.sw.approved.txt diff --git a/tests/SelfTest/Baselines/console.sw.multi.approved.txt b/contrib/catch2/tests/SelfTest/Baselines/console.sw.multi.approved.txt index 418ccf7..418ccf7 100644 --- a/tests/SelfTest/Baselines/console.sw.multi.approved.txt +++ b/contrib/catch2/tests/SelfTest/Baselines/console.sw.multi.approved.txt diff --git a/tests/SelfTest/Baselines/console.swa4.approved.txt b/contrib/catch2/tests/SelfTest/Baselines/console.swa4.approved.txt index 41b7612..41b7612 100644 --- a/tests/SelfTest/Baselines/console.swa4.approved.txt +++ b/contrib/catch2/tests/SelfTest/Baselines/console.swa4.approved.txt diff --git a/tests/SelfTest/Baselines/default.sw.multi.approved.txt b/contrib/catch2/tests/SelfTest/Baselines/default.sw.multi.approved.txt index bb17484..bb17484 100644 --- a/tests/SelfTest/Baselines/default.sw.multi.approved.txt +++ b/contrib/catch2/tests/SelfTest/Baselines/default.sw.multi.approved.txt diff --git a/tests/SelfTest/Baselines/junit.sw.approved.txt b/contrib/catch2/tests/SelfTest/Baselines/junit.sw.approved.txt index c3571f3..c3571f3 100644 --- a/tests/SelfTest/Baselines/junit.sw.approved.txt +++ b/contrib/catch2/tests/SelfTest/Baselines/junit.sw.approved.txt diff --git a/tests/SelfTest/Baselines/junit.sw.multi.approved.txt b/contrib/catch2/tests/SelfTest/Baselines/junit.sw.multi.approved.txt index 55f5520..55f5520 100644 --- a/tests/SelfTest/Baselines/junit.sw.multi.approved.txt +++ b/contrib/catch2/tests/SelfTest/Baselines/junit.sw.multi.approved.txt diff --git a/tests/SelfTest/Baselines/sonarqube.sw.approved.txt b/contrib/catch2/tests/SelfTest/Baselines/sonarqube.sw.approved.txt index af06793..af06793 100644 --- a/tests/SelfTest/Baselines/sonarqube.sw.approved.txt +++ b/contrib/catch2/tests/SelfTest/Baselines/sonarqube.sw.approved.txt diff --git a/tests/SelfTest/Baselines/sonarqube.sw.multi.approved.txt b/contrib/catch2/tests/SelfTest/Baselines/sonarqube.sw.multi.approved.txt index 2d8d510..2d8d510 100644 --- a/tests/SelfTest/Baselines/sonarqube.sw.multi.approved.txt +++ b/contrib/catch2/tests/SelfTest/Baselines/sonarqube.sw.multi.approved.txt diff --git a/tests/SelfTest/Baselines/tap.sw.approved.txt b/contrib/catch2/tests/SelfTest/Baselines/tap.sw.approved.txt index 801946d..801946d 100644 --- a/tests/SelfTest/Baselines/tap.sw.approved.txt +++ b/contrib/catch2/tests/SelfTest/Baselines/tap.sw.approved.txt diff --git a/tests/SelfTest/Baselines/tap.sw.multi.approved.txt b/contrib/catch2/tests/SelfTest/Baselines/tap.sw.multi.approved.txt index 7f281f6..7f281f6 100644 --- a/tests/SelfTest/Baselines/tap.sw.multi.approved.txt +++ b/contrib/catch2/tests/SelfTest/Baselines/tap.sw.multi.approved.txt diff --git a/tests/SelfTest/Baselines/teamcity.sw.approved.txt b/contrib/catch2/tests/SelfTest/Baselines/teamcity.sw.approved.txt index db48552..db48552 100644 --- a/tests/SelfTest/Baselines/teamcity.sw.approved.txt +++ b/contrib/catch2/tests/SelfTest/Baselines/teamcity.sw.approved.txt diff --git a/tests/SelfTest/Baselines/teamcity.sw.multi.approved.txt b/contrib/catch2/tests/SelfTest/Baselines/teamcity.sw.multi.approved.txt index dde8a68..dde8a68 100644 --- a/tests/SelfTest/Baselines/teamcity.sw.multi.approved.txt +++ b/contrib/catch2/tests/SelfTest/Baselines/teamcity.sw.multi.approved.txt diff --git a/tests/SelfTest/Baselines/xml.sw.approved.txt b/contrib/catch2/tests/SelfTest/Baselines/xml.sw.approved.txt index aa6bd6a..aa6bd6a 100644 --- a/tests/SelfTest/Baselines/xml.sw.approved.txt +++ b/contrib/catch2/tests/SelfTest/Baselines/xml.sw.approved.txt diff --git a/tests/SelfTest/Baselines/xml.sw.multi.approved.txt b/contrib/catch2/tests/SelfTest/Baselines/xml.sw.multi.approved.txt index 7f3b498..7f3b498 100644 --- a/tests/SelfTest/Baselines/xml.sw.multi.approved.txt +++ b/contrib/catch2/tests/SelfTest/Baselines/xml.sw.multi.approved.txt diff --git a/tests/SelfTest/IntrospectiveTests/Algorithms.tests.cpp b/contrib/catch2/tests/SelfTest/IntrospectiveTests/Algorithms.tests.cpp index fa17cf8..fa17cf8 100644 --- a/tests/SelfTest/IntrospectiveTests/Algorithms.tests.cpp +++ b/contrib/catch2/tests/SelfTest/IntrospectiveTests/Algorithms.tests.cpp diff --git a/tests/SelfTest/IntrospectiveTests/AssertionHandler.tests.cpp b/contrib/catch2/tests/SelfTest/IntrospectiveTests/AssertionHandler.tests.cpp index ab09607..ab09607 100644 --- a/tests/SelfTest/IntrospectiveTests/AssertionHandler.tests.cpp +++ b/contrib/catch2/tests/SelfTest/IntrospectiveTests/AssertionHandler.tests.cpp diff --git a/tests/SelfTest/IntrospectiveTests/Clara.tests.cpp b/contrib/catch2/tests/SelfTest/IntrospectiveTests/Clara.tests.cpp index 53023b5..53023b5 100644 --- a/tests/SelfTest/IntrospectiveTests/Clara.tests.cpp +++ b/contrib/catch2/tests/SelfTest/IntrospectiveTests/Clara.tests.cpp diff --git a/tests/SelfTest/IntrospectiveTests/CmdLine.tests.cpp b/contrib/catch2/tests/SelfTest/IntrospectiveTests/CmdLine.tests.cpp index 404bad2..404bad2 100644 --- a/tests/SelfTest/IntrospectiveTests/CmdLine.tests.cpp +++ b/contrib/catch2/tests/SelfTest/IntrospectiveTests/CmdLine.tests.cpp diff --git a/tests/SelfTest/IntrospectiveTests/CmdLineHelpers.tests.cpp b/contrib/catch2/tests/SelfTest/IntrospectiveTests/CmdLineHelpers.tests.cpp index 4df8ecf..4df8ecf 100644 --- a/tests/SelfTest/IntrospectiveTests/CmdLineHelpers.tests.cpp +++ b/contrib/catch2/tests/SelfTest/IntrospectiveTests/CmdLineHelpers.tests.cpp diff --git a/tests/SelfTest/IntrospectiveTests/ColourImpl.tests.cpp b/contrib/catch2/tests/SelfTest/IntrospectiveTests/ColourImpl.tests.cpp index 615fda1..615fda1 100644 --- a/tests/SelfTest/IntrospectiveTests/ColourImpl.tests.cpp +++ b/contrib/catch2/tests/SelfTest/IntrospectiveTests/ColourImpl.tests.cpp diff --git a/tests/SelfTest/IntrospectiveTests/Details.tests.cpp b/contrib/catch2/tests/SelfTest/IntrospectiveTests/Details.tests.cpp index 5566bb5..5566bb5 100644 --- a/tests/SelfTest/IntrospectiveTests/Details.tests.cpp +++ b/contrib/catch2/tests/SelfTest/IntrospectiveTests/Details.tests.cpp diff --git a/tests/SelfTest/IntrospectiveTests/FloatingPoint.tests.cpp b/contrib/catch2/tests/SelfTest/IntrospectiveTests/FloatingPoint.tests.cpp index d218170..d218170 100644 --- a/tests/SelfTest/IntrospectiveTests/FloatingPoint.tests.cpp +++ b/contrib/catch2/tests/SelfTest/IntrospectiveTests/FloatingPoint.tests.cpp diff --git a/tests/SelfTest/IntrospectiveTests/GeneratorsImpl.tests.cpp b/contrib/catch2/tests/SelfTest/IntrospectiveTests/GeneratorsImpl.tests.cpp index 14c9011..14c9011 100644 --- a/tests/SelfTest/IntrospectiveTests/GeneratorsImpl.tests.cpp +++ b/contrib/catch2/tests/SelfTest/IntrospectiveTests/GeneratorsImpl.tests.cpp diff --git a/tests/SelfTest/IntrospectiveTests/Integer.tests.cpp b/contrib/catch2/tests/SelfTest/IntrospectiveTests/Integer.tests.cpp index 8955f40..8955f40 100644 --- a/tests/SelfTest/IntrospectiveTests/Integer.tests.cpp +++ b/contrib/catch2/tests/SelfTest/IntrospectiveTests/Integer.tests.cpp diff --git a/tests/SelfTest/IntrospectiveTests/InternalBenchmark.tests.cpp b/contrib/catch2/tests/SelfTest/IntrospectiveTests/InternalBenchmark.tests.cpp index 69251d9..69251d9 100644 --- a/tests/SelfTest/IntrospectiveTests/InternalBenchmark.tests.cpp +++ b/contrib/catch2/tests/SelfTest/IntrospectiveTests/InternalBenchmark.tests.cpp diff --git a/tests/SelfTest/IntrospectiveTests/Json.tests.cpp b/contrib/catch2/tests/SelfTest/IntrospectiveTests/Json.tests.cpp index 8204e3c..8204e3c 100644 --- a/tests/SelfTest/IntrospectiveTests/Json.tests.cpp +++ b/contrib/catch2/tests/SelfTest/IntrospectiveTests/Json.tests.cpp diff --git a/tests/SelfTest/IntrospectiveTests/Parse.tests.cpp b/contrib/catch2/tests/SelfTest/IntrospectiveTests/Parse.tests.cpp index 7791355..7791355 100644 --- a/tests/SelfTest/IntrospectiveTests/Parse.tests.cpp +++ b/contrib/catch2/tests/SelfTest/IntrospectiveTests/Parse.tests.cpp diff --git a/tests/SelfTest/IntrospectiveTests/PartTracker.tests.cpp b/contrib/catch2/tests/SelfTest/IntrospectiveTests/PartTracker.tests.cpp index c13ec57..c13ec57 100644 --- a/tests/SelfTest/IntrospectiveTests/PartTracker.tests.cpp +++ b/contrib/catch2/tests/SelfTest/IntrospectiveTests/PartTracker.tests.cpp diff --git a/tests/SelfTest/IntrospectiveTests/RandomNumberGeneration.tests.cpp b/contrib/catch2/tests/SelfTest/IntrospectiveTests/RandomNumberGeneration.tests.cpp index 8932321..8932321 100644 --- a/tests/SelfTest/IntrospectiveTests/RandomNumberGeneration.tests.cpp +++ b/contrib/catch2/tests/SelfTest/IntrospectiveTests/RandomNumberGeneration.tests.cpp diff --git a/tests/SelfTest/IntrospectiveTests/Reporters.tests.cpp b/contrib/catch2/tests/SelfTest/IntrospectiveTests/Reporters.tests.cpp index 86ba117..86ba117 100644 --- a/tests/SelfTest/IntrospectiveTests/Reporters.tests.cpp +++ b/contrib/catch2/tests/SelfTest/IntrospectiveTests/Reporters.tests.cpp diff --git a/tests/SelfTest/IntrospectiveTests/Sharding.tests.cpp b/contrib/catch2/tests/SelfTest/IntrospectiveTests/Sharding.tests.cpp index 8e6009d..8e6009d 100644 --- a/tests/SelfTest/IntrospectiveTests/Sharding.tests.cpp +++ b/contrib/catch2/tests/SelfTest/IntrospectiveTests/Sharding.tests.cpp diff --git a/tests/SelfTest/IntrospectiveTests/Stream.tests.cpp b/contrib/catch2/tests/SelfTest/IntrospectiveTests/Stream.tests.cpp index 738cb52..738cb52 100644 --- a/tests/SelfTest/IntrospectiveTests/Stream.tests.cpp +++ b/contrib/catch2/tests/SelfTest/IntrospectiveTests/Stream.tests.cpp diff --git a/tests/SelfTest/IntrospectiveTests/String.tests.cpp b/contrib/catch2/tests/SelfTest/IntrospectiveTests/String.tests.cpp index 43c58b4..43c58b4 100644 --- a/tests/SelfTest/IntrospectiveTests/String.tests.cpp +++ b/contrib/catch2/tests/SelfTest/IntrospectiveTests/String.tests.cpp diff --git a/tests/SelfTest/IntrospectiveTests/StringManip.tests.cpp b/contrib/catch2/tests/SelfTest/IntrospectiveTests/StringManip.tests.cpp index f30573c..f30573c 100644 --- a/tests/SelfTest/IntrospectiveTests/StringManip.tests.cpp +++ b/contrib/catch2/tests/SelfTest/IntrospectiveTests/StringManip.tests.cpp diff --git a/tests/SelfTest/IntrospectiveTests/Tag.tests.cpp b/contrib/catch2/tests/SelfTest/IntrospectiveTests/Tag.tests.cpp index 4372375..4372375 100644 --- a/tests/SelfTest/IntrospectiveTests/Tag.tests.cpp +++ b/contrib/catch2/tests/SelfTest/IntrospectiveTests/Tag.tests.cpp diff --git a/tests/SelfTest/IntrospectiveTests/TestCaseInfoHasher.tests.cpp b/contrib/catch2/tests/SelfTest/IntrospectiveTests/TestCaseInfoHasher.tests.cpp index 03cb3f0..03cb3f0 100644 --- a/tests/SelfTest/IntrospectiveTests/TestCaseInfoHasher.tests.cpp +++ b/contrib/catch2/tests/SelfTest/IntrospectiveTests/TestCaseInfoHasher.tests.cpp diff --git a/tests/SelfTest/IntrospectiveTests/TestSpec.tests.cpp b/contrib/catch2/tests/SelfTest/IntrospectiveTests/TestSpec.tests.cpp index 11a7a7a..11a7a7a 100644 --- a/tests/SelfTest/IntrospectiveTests/TestSpec.tests.cpp +++ b/contrib/catch2/tests/SelfTest/IntrospectiveTests/TestSpec.tests.cpp diff --git a/tests/SelfTest/IntrospectiveTests/TestSpecParser.tests.cpp b/contrib/catch2/tests/SelfTest/IntrospectiveTests/TestSpecParser.tests.cpp index ae27b40..ae27b40 100644 --- a/tests/SelfTest/IntrospectiveTests/TestSpecParser.tests.cpp +++ b/contrib/catch2/tests/SelfTest/IntrospectiveTests/TestSpecParser.tests.cpp diff --git a/tests/SelfTest/IntrospectiveTests/TextFlow.tests.cpp b/contrib/catch2/tests/SelfTest/IntrospectiveTests/TextFlow.tests.cpp index de03ed0..de03ed0 100644 --- a/tests/SelfTest/IntrospectiveTests/TextFlow.tests.cpp +++ b/contrib/catch2/tests/SelfTest/IntrospectiveTests/TextFlow.tests.cpp diff --git a/tests/SelfTest/IntrospectiveTests/ToString.tests.cpp b/contrib/catch2/tests/SelfTest/IntrospectiveTests/ToString.tests.cpp index e190460..e190460 100644 --- a/tests/SelfTest/IntrospectiveTests/ToString.tests.cpp +++ b/contrib/catch2/tests/SelfTest/IntrospectiveTests/ToString.tests.cpp diff --git a/tests/SelfTest/IntrospectiveTests/Traits.tests.cpp b/contrib/catch2/tests/SelfTest/IntrospectiveTests/Traits.tests.cpp index 459e0d4..459e0d4 100644 --- a/tests/SelfTest/IntrospectiveTests/Traits.tests.cpp +++ b/contrib/catch2/tests/SelfTest/IntrospectiveTests/Traits.tests.cpp diff --git a/tests/SelfTest/IntrospectiveTests/UniquePtr.tests.cpp b/contrib/catch2/tests/SelfTest/IntrospectiveTests/UniquePtr.tests.cpp index 420bf1b..420bf1b 100644 --- a/tests/SelfTest/IntrospectiveTests/UniquePtr.tests.cpp +++ b/contrib/catch2/tests/SelfTest/IntrospectiveTests/UniquePtr.tests.cpp diff --git a/tests/SelfTest/IntrospectiveTests/Xml.tests.cpp b/contrib/catch2/tests/SelfTest/IntrospectiveTests/Xml.tests.cpp index b5982b8..b5982b8 100644 --- a/tests/SelfTest/IntrospectiveTests/Xml.tests.cpp +++ b/contrib/catch2/tests/SelfTest/IntrospectiveTests/Xml.tests.cpp diff --git a/tests/SelfTest/Misc/invalid-test-names.input b/contrib/catch2/tests/SelfTest/Misc/invalid-test-names.input index e2bc88e..e2bc88e 100644 --- a/tests/SelfTest/Misc/invalid-test-names.input +++ b/contrib/catch2/tests/SelfTest/Misc/invalid-test-names.input diff --git a/tests/SelfTest/Misc/plain-old-tests.input b/contrib/catch2/tests/SelfTest/Misc/plain-old-tests.input index ae6d9f1..ae6d9f1 100644 --- a/tests/SelfTest/Misc/plain-old-tests.input +++ b/contrib/catch2/tests/SelfTest/Misc/plain-old-tests.input diff --git a/tests/SelfTest/Misc/special-characters-in-file.input b/contrib/catch2/tests/SelfTest/Misc/special-characters-in-file.input index b68a6bf..b68a6bf 100644 --- a/tests/SelfTest/Misc/special-characters-in-file.input +++ b/contrib/catch2/tests/SelfTest/Misc/special-characters-in-file.input diff --git a/tests/SelfTest/TestRegistrations.cpp b/contrib/catch2/tests/SelfTest/TestRegistrations.cpp index d7a6966..d7a6966 100644 --- a/tests/SelfTest/TestRegistrations.cpp +++ b/contrib/catch2/tests/SelfTest/TestRegistrations.cpp diff --git a/tests/SelfTest/TimingTests/Sleep.tests.cpp b/contrib/catch2/tests/SelfTest/TimingTests/Sleep.tests.cpp index 4e9e385..4e9e385 100644 --- a/tests/SelfTest/TimingTests/Sleep.tests.cpp +++ b/contrib/catch2/tests/SelfTest/TimingTests/Sleep.tests.cpp diff --git a/tests/SelfTest/UsageTests/Approx.tests.cpp b/contrib/catch2/tests/SelfTest/UsageTests/Approx.tests.cpp index c3d41cb..c3d41cb 100644 --- a/tests/SelfTest/UsageTests/Approx.tests.cpp +++ b/contrib/catch2/tests/SelfTest/UsageTests/Approx.tests.cpp diff --git a/tests/SelfTest/UsageTests/BDD.tests.cpp b/contrib/catch2/tests/SelfTest/UsageTests/BDD.tests.cpp index 5ac8605..5ac8605 100644 --- a/tests/SelfTest/UsageTests/BDD.tests.cpp +++ b/contrib/catch2/tests/SelfTest/UsageTests/BDD.tests.cpp diff --git a/tests/SelfTest/UsageTests/Benchmark.tests.cpp b/contrib/catch2/tests/SelfTest/UsageTests/Benchmark.tests.cpp index c489eaa..c489eaa 100644 --- a/tests/SelfTest/UsageTests/Benchmark.tests.cpp +++ b/contrib/catch2/tests/SelfTest/UsageTests/Benchmark.tests.cpp diff --git a/tests/SelfTest/UsageTests/Class.tests.cpp b/contrib/catch2/tests/SelfTest/UsageTests/Class.tests.cpp index 75510f1..75510f1 100644 --- a/tests/SelfTest/UsageTests/Class.tests.cpp +++ b/contrib/catch2/tests/SelfTest/UsageTests/Class.tests.cpp diff --git a/tests/SelfTest/UsageTests/Compilation.tests.cpp b/contrib/catch2/tests/SelfTest/UsageTests/Compilation.tests.cpp index 7a6a1f2..7a6a1f2 100644 --- a/tests/SelfTest/UsageTests/Compilation.tests.cpp +++ b/contrib/catch2/tests/SelfTest/UsageTests/Compilation.tests.cpp diff --git a/tests/SelfTest/UsageTests/Condition.tests.cpp b/contrib/catch2/tests/SelfTest/UsageTests/Condition.tests.cpp index 211dd3b..211dd3b 100644 --- a/tests/SelfTest/UsageTests/Condition.tests.cpp +++ b/contrib/catch2/tests/SelfTest/UsageTests/Condition.tests.cpp diff --git a/tests/SelfTest/UsageTests/Decomposition.tests.cpp b/contrib/catch2/tests/SelfTest/UsageTests/Decomposition.tests.cpp index e92f740..e92f740 100644 --- a/tests/SelfTest/UsageTests/Decomposition.tests.cpp +++ b/contrib/catch2/tests/SelfTest/UsageTests/Decomposition.tests.cpp diff --git a/tests/SelfTest/UsageTests/EnumToString.tests.cpp b/contrib/catch2/tests/SelfTest/UsageTests/EnumToString.tests.cpp index 268a7ca..268a7ca 100644 --- a/tests/SelfTest/UsageTests/EnumToString.tests.cpp +++ b/contrib/catch2/tests/SelfTest/UsageTests/EnumToString.tests.cpp diff --git a/tests/SelfTest/UsageTests/Exception.tests.cpp b/contrib/catch2/tests/SelfTest/UsageTests/Exception.tests.cpp index 7c6b0c8..7c6b0c8 100644 --- a/tests/SelfTest/UsageTests/Exception.tests.cpp +++ b/contrib/catch2/tests/SelfTest/UsageTests/Exception.tests.cpp diff --git a/tests/SelfTest/UsageTests/Generators.tests.cpp b/contrib/catch2/tests/SelfTest/UsageTests/Generators.tests.cpp index f04cf4f..f04cf4f 100644 --- a/tests/SelfTest/UsageTests/Generators.tests.cpp +++ b/contrib/catch2/tests/SelfTest/UsageTests/Generators.tests.cpp diff --git a/tests/SelfTest/UsageTests/Matchers.tests.cpp b/contrib/catch2/tests/SelfTest/UsageTests/Matchers.tests.cpp index 7c4501c..7c4501c 100644 --- a/tests/SelfTest/UsageTests/Matchers.tests.cpp +++ b/contrib/catch2/tests/SelfTest/UsageTests/Matchers.tests.cpp diff --git a/tests/SelfTest/UsageTests/MatchersRanges.tests.cpp b/contrib/catch2/tests/SelfTest/UsageTests/MatchersRanges.tests.cpp index 4f906b9..4f906b9 100644 --- a/tests/SelfTest/UsageTests/MatchersRanges.tests.cpp +++ b/contrib/catch2/tests/SelfTest/UsageTests/MatchersRanges.tests.cpp diff --git a/tests/SelfTest/UsageTests/Message.tests.cpp b/contrib/catch2/tests/SelfTest/UsageTests/Message.tests.cpp index 7626e00..7626e00 100644 --- a/tests/SelfTest/UsageTests/Message.tests.cpp +++ b/contrib/catch2/tests/SelfTest/UsageTests/Message.tests.cpp diff --git a/tests/SelfTest/UsageTests/Misc.tests.cpp b/contrib/catch2/tests/SelfTest/UsageTests/Misc.tests.cpp index 3697f06..3697f06 100644 --- a/tests/SelfTest/UsageTests/Misc.tests.cpp +++ b/contrib/catch2/tests/SelfTest/UsageTests/Misc.tests.cpp diff --git a/tests/SelfTest/UsageTests/Skip.tests.cpp b/contrib/catch2/tests/SelfTest/UsageTests/Skip.tests.cpp index 661795e..661795e 100644 --- a/tests/SelfTest/UsageTests/Skip.tests.cpp +++ b/contrib/catch2/tests/SelfTest/UsageTests/Skip.tests.cpp diff --git a/tests/SelfTest/UsageTests/ToStringByte.tests.cpp b/contrib/catch2/tests/SelfTest/UsageTests/ToStringByte.tests.cpp index 624abbf..624abbf 100644 --- a/tests/SelfTest/UsageTests/ToStringByte.tests.cpp +++ b/contrib/catch2/tests/SelfTest/UsageTests/ToStringByte.tests.cpp diff --git a/tests/SelfTest/UsageTests/ToStringChrono.tests.cpp b/contrib/catch2/tests/SelfTest/UsageTests/ToStringChrono.tests.cpp index 744b899..744b899 100644 --- a/tests/SelfTest/UsageTests/ToStringChrono.tests.cpp +++ b/contrib/catch2/tests/SelfTest/UsageTests/ToStringChrono.tests.cpp diff --git a/tests/SelfTest/UsageTests/ToStringGeneral.tests.cpp b/contrib/catch2/tests/SelfTest/UsageTests/ToStringGeneral.tests.cpp index 78c0c80..78c0c80 100644 --- a/tests/SelfTest/UsageTests/ToStringGeneral.tests.cpp +++ b/contrib/catch2/tests/SelfTest/UsageTests/ToStringGeneral.tests.cpp diff --git a/tests/SelfTest/UsageTests/ToStringOptional.tests.cpp b/contrib/catch2/tests/SelfTest/UsageTests/ToStringOptional.tests.cpp index 3671771..3671771 100644 --- a/tests/SelfTest/UsageTests/ToStringOptional.tests.cpp +++ b/contrib/catch2/tests/SelfTest/UsageTests/ToStringOptional.tests.cpp diff --git a/tests/SelfTest/UsageTests/ToStringPair.tests.cpp b/contrib/catch2/tests/SelfTest/UsageTests/ToStringPair.tests.cpp index f5cb239..f5cb239 100644 --- a/tests/SelfTest/UsageTests/ToStringPair.tests.cpp +++ b/contrib/catch2/tests/SelfTest/UsageTests/ToStringPair.tests.cpp diff --git a/tests/SelfTest/UsageTests/ToStringTuple.tests.cpp b/contrib/catch2/tests/SelfTest/UsageTests/ToStringTuple.tests.cpp index 9d1d2c4..9d1d2c4 100644 --- a/tests/SelfTest/UsageTests/ToStringTuple.tests.cpp +++ b/contrib/catch2/tests/SelfTest/UsageTests/ToStringTuple.tests.cpp diff --git a/tests/SelfTest/UsageTests/ToStringVariant.tests.cpp b/contrib/catch2/tests/SelfTest/UsageTests/ToStringVariant.tests.cpp index 197ba55..197ba55 100644 --- a/tests/SelfTest/UsageTests/ToStringVariant.tests.cpp +++ b/contrib/catch2/tests/SelfTest/UsageTests/ToStringVariant.tests.cpp diff --git a/tests/SelfTest/UsageTests/ToStringVector.tests.cpp b/contrib/catch2/tests/SelfTest/UsageTests/ToStringVector.tests.cpp index c042744..c042744 100644 --- a/tests/SelfTest/UsageTests/ToStringVector.tests.cpp +++ b/contrib/catch2/tests/SelfTest/UsageTests/ToStringVector.tests.cpp diff --git a/tests/SelfTest/UsageTests/ToStringWhich.tests.cpp b/contrib/catch2/tests/SelfTest/UsageTests/ToStringWhich.tests.cpp index ec7a49e..ec7a49e 100644 --- a/tests/SelfTest/UsageTests/ToStringWhich.tests.cpp +++ b/contrib/catch2/tests/SelfTest/UsageTests/ToStringWhich.tests.cpp diff --git a/tests/SelfTest/UsageTests/Tricky.tests.cpp b/contrib/catch2/tests/SelfTest/UsageTests/Tricky.tests.cpp index 041d786..041d786 100644 --- a/tests/SelfTest/UsageTests/Tricky.tests.cpp +++ b/contrib/catch2/tests/SelfTest/UsageTests/Tricky.tests.cpp diff --git a/tests/SelfTest/UsageTests/VariadicMacros.tests.cpp b/contrib/catch2/tests/SelfTest/UsageTests/VariadicMacros.tests.cpp index 92048a0..92048a0 100644 --- a/tests/SelfTest/UsageTests/VariadicMacros.tests.cpp +++ b/contrib/catch2/tests/SelfTest/UsageTests/VariadicMacros.tests.cpp diff --git a/tests/SelfTest/helpers/parse_test_spec.cpp b/contrib/catch2/tests/SelfTest/helpers/parse_test_spec.cpp index aa64404..aa64404 100644 --- a/tests/SelfTest/helpers/parse_test_spec.cpp +++ b/contrib/catch2/tests/SelfTest/helpers/parse_test_spec.cpp diff --git a/tests/SelfTest/helpers/parse_test_spec.hpp b/contrib/catch2/tests/SelfTest/helpers/parse_test_spec.hpp index 39ee071..39ee071 100644 --- a/tests/SelfTest/helpers/parse_test_spec.hpp +++ b/contrib/catch2/tests/SelfTest/helpers/parse_test_spec.hpp diff --git a/tests/SelfTest/helpers/range_test_helpers.hpp b/contrib/catch2/tests/SelfTest/helpers/range_test_helpers.hpp index 22c2c3c..22c2c3c 100644 --- a/tests/SelfTest/helpers/range_test_helpers.hpp +++ b/contrib/catch2/tests/SelfTest/helpers/range_test_helpers.hpp diff --git a/tests/SelfTest/helpers/type_with_lit_0_comparisons.hpp b/contrib/catch2/tests/SelfTest/helpers/type_with_lit_0_comparisons.hpp index a8e517c..a8e517c 100644 --- a/tests/SelfTest/helpers/type_with_lit_0_comparisons.hpp +++ b/contrib/catch2/tests/SelfTest/helpers/type_with_lit_0_comparisons.hpp diff --git a/tests/TestScripts/ConfigureTestsCommon.py b/contrib/catch2/tests/TestScripts/ConfigureTestsCommon.py index 13b1d92..13b1d92 100644 --- a/tests/TestScripts/ConfigureTestsCommon.py +++ b/contrib/catch2/tests/TestScripts/ConfigureTestsCommon.py diff --git a/tests/TestScripts/DiscoverTests/CMakeLists.txt b/contrib/catch2/tests/TestScripts/DiscoverTests/CMakeLists.txt index f0b49f4..f0b49f4 100644 --- a/tests/TestScripts/DiscoverTests/CMakeLists.txt +++ b/contrib/catch2/tests/TestScripts/DiscoverTests/CMakeLists.txt diff --git a/tests/TestScripts/DiscoverTests/VerifyRegistration.py b/contrib/catch2/tests/TestScripts/DiscoverTests/VerifyRegistration.py index 33150ca..33150ca 100644 --- a/tests/TestScripts/DiscoverTests/VerifyRegistration.py +++ b/contrib/catch2/tests/TestScripts/DiscoverTests/VerifyRegistration.py diff --git a/tests/TestScripts/DiscoverTests/register-tests.cpp b/contrib/catch2/tests/TestScripts/DiscoverTests/register-tests.cpp index be533ab..be533ab 100644 --- a/tests/TestScripts/DiscoverTests/register-tests.cpp +++ b/contrib/catch2/tests/TestScripts/DiscoverTests/register-tests.cpp diff --git a/tests/TestScripts/testBazelReporter.py b/contrib/catch2/tests/TestScripts/testBazelReporter.py index 573eafd..573eafd 100644 --- a/tests/TestScripts/testBazelReporter.py +++ b/contrib/catch2/tests/TestScripts/testBazelReporter.py diff --git a/tests/TestScripts/testBazelSharding.py b/contrib/catch2/tests/TestScripts/testBazelSharding.py index d4a9aee..d4a9aee 100755 --- a/tests/TestScripts/testBazelSharding.py +++ b/contrib/catch2/tests/TestScripts/testBazelSharding.py diff --git a/tests/TestScripts/testConfigureDefaultReporter.py b/contrib/catch2/tests/TestScripts/testConfigureDefaultReporter.py index 119e1ca..119e1ca 100644 --- a/tests/TestScripts/testConfigureDefaultReporter.py +++ b/contrib/catch2/tests/TestScripts/testConfigureDefaultReporter.py diff --git a/tests/TestScripts/testConfigureDisable.py b/contrib/catch2/tests/TestScripts/testConfigureDisable.py index b024770..b024770 100644 --- a/tests/TestScripts/testConfigureDisable.py +++ b/contrib/catch2/tests/TestScripts/testConfigureDisable.py diff --git a/tests/TestScripts/testConfigureDisableStringification.py b/contrib/catch2/tests/TestScripts/testConfigureDisableStringification.py index 33f7853..33f7853 100644 --- a/tests/TestScripts/testConfigureDisableStringification.py +++ b/contrib/catch2/tests/TestScripts/testConfigureDisableStringification.py diff --git a/tests/TestScripts/testConfigureExperimentalRedirect.py b/contrib/catch2/tests/TestScripts/testConfigureExperimentalRedirect.py index 09ff51e..09ff51e 100644 --- a/tests/TestScripts/testConfigureExperimentalRedirect.py +++ b/contrib/catch2/tests/TestScripts/testConfigureExperimentalRedirect.py diff --git a/tests/TestScripts/testPartialTestCaseEvent.py b/contrib/catch2/tests/TestScripts/testPartialTestCaseEvent.py index 55c6e29..55c6e29 100755 --- a/tests/TestScripts/testPartialTestCaseEvent.py +++ b/contrib/catch2/tests/TestScripts/testPartialTestCaseEvent.py diff --git a/tests/TestScripts/testRandomOrder.py b/contrib/catch2/tests/TestScripts/testRandomOrder.py index 2368423..2368423 100755 --- a/tests/TestScripts/testRandomOrder.py +++ b/contrib/catch2/tests/TestScripts/testRandomOrder.py diff --git a/tests/TestScripts/testSharding.py b/contrib/catch2/tests/TestScripts/testSharding.py index fa6f94d..fa6f94d 100755 --- a/tests/TestScripts/testSharding.py +++ b/contrib/catch2/tests/TestScripts/testSharding.py diff --git a/tests/meson.build b/contrib/catch2/tests/meson.build index 58302b7..58302b7 100644 --- a/tests/meson.build +++ b/contrib/catch2/tests/meson.build diff --git a/third_party/clara.hpp b/contrib/catch2/third_party/clara.hpp index eb4c727..eb4c727 100644 --- a/third_party/clara.hpp +++ b/contrib/catch2/third_party/clara.hpp diff --git a/tools/misc/CMakeLists.txt b/contrib/catch2/tools/misc/CMakeLists.txt index 59811df..59811df 100644 --- a/tools/misc/CMakeLists.txt +++ b/contrib/catch2/tools/misc/CMakeLists.txt diff --git a/tools/misc/appveyorBuildConfigurationScript.bat b/contrib/catch2/tools/misc/appveyorBuildConfigurationScript.bat index 727f829..727f829 100644 --- a/tools/misc/appveyorBuildConfigurationScript.bat +++ b/contrib/catch2/tools/misc/appveyorBuildConfigurationScript.bat diff --git a/tools/misc/appveyorMergeCoverageScript.py b/contrib/catch2/tools/misc/appveyorMergeCoverageScript.py index 5b71f6e..5b71f6e 100644 --- a/tools/misc/appveyorMergeCoverageScript.py +++ b/contrib/catch2/tools/misc/appveyorMergeCoverageScript.py diff --git a/tools/misc/appveyorTestRunScript.bat b/contrib/catch2/tools/misc/appveyorTestRunScript.bat index 661bae2..661bae2 100644 --- a/tools/misc/appveyorTestRunScript.bat +++ b/contrib/catch2/tools/misc/appveyorTestRunScript.bat diff --git a/tools/misc/coverage-helper.cpp b/contrib/catch2/tools/misc/coverage-helper.cpp index 9e7a8ca..9e7a8ca 100644 --- a/tools/misc/coverage-helper.cpp +++ b/contrib/catch2/tools/misc/coverage-helper.cpp diff --git a/tools/misc/installOpenCppCoverage.ps1 b/contrib/catch2/tools/misc/installOpenCppCoverage.ps1 index 215fe20..215fe20 100644 --- a/tools/misc/installOpenCppCoverage.ps1 +++ b/contrib/catch2/tools/misc/installOpenCppCoverage.ps1 diff --git a/tools/scripts/approvalTests.py b/contrib/catch2/tools/scripts/approvalTests.py index 4146b64..4146b64 100755 --- a/tools/scripts/approvalTests.py +++ b/contrib/catch2/tools/scripts/approvalTests.py diff --git a/tools/scripts/approve.py b/contrib/catch2/tools/scripts/approve.py index 6d73be5..6d73be5 100755 --- a/tools/scripts/approve.py +++ b/contrib/catch2/tools/scripts/approve.py diff --git a/tools/scripts/buildAndTest.cmd b/contrib/catch2/tools/scripts/buildAndTest.cmd index fa35912..fa35912 100644 --- a/tools/scripts/buildAndTest.cmd +++ b/contrib/catch2/tools/scripts/buildAndTest.cmd diff --git a/tools/scripts/buildAndTest.sh b/contrib/catch2/tools/scripts/buildAndTest.sh index 0383c97..0383c97 100755 --- a/tools/scripts/buildAndTest.sh +++ b/contrib/catch2/tools/scripts/buildAndTest.sh diff --git a/tools/scripts/checkConvenienceHeaders.py b/contrib/catch2/tools/scripts/checkConvenienceHeaders.py index 41b52ce..41b52ce 100755 --- a/tools/scripts/checkConvenienceHeaders.py +++ b/contrib/catch2/tools/scripts/checkConvenienceHeaders.py diff --git a/tools/scripts/checkDuplicateFilenames.py b/contrib/catch2/tools/scripts/checkDuplicateFilenames.py index b46a2b4..b46a2b4 100755 --- a/tools/scripts/checkDuplicateFilenames.py +++ b/contrib/catch2/tools/scripts/checkDuplicateFilenames.py diff --git a/tools/scripts/checkLicense.py b/contrib/catch2/tools/scripts/checkLicense.py index 7078d3e..7078d3e 100755 --- a/tools/scripts/checkLicense.py +++ b/contrib/catch2/tools/scripts/checkLicense.py diff --git a/tools/scripts/developBuild.py b/contrib/catch2/tools/scripts/developBuild.py index 8837770..8837770 100755 --- a/tools/scripts/developBuild.py +++ b/contrib/catch2/tools/scripts/developBuild.py diff --git a/tools/scripts/extractFeaturesFromReleaseNotes.py b/contrib/catch2/tools/scripts/extractFeaturesFromReleaseNotes.py index d8be043..d8be043 100644 --- a/tools/scripts/extractFeaturesFromReleaseNotes.py +++ b/contrib/catch2/tools/scripts/extractFeaturesFromReleaseNotes.py diff --git a/tools/scripts/fixWhitespace.py b/contrib/catch2/tools/scripts/fixWhitespace.py index 5840e79..5840e79 100755 --- a/tools/scripts/fixWhitespace.py +++ b/contrib/catch2/tools/scripts/fixWhitespace.py diff --git a/tools/scripts/generateAmalgamatedFiles.py b/contrib/catch2/tools/scripts/generateAmalgamatedFiles.py index e3e86aa..e3e86aa 100755 --- a/tools/scripts/generateAmalgamatedFiles.py +++ b/contrib/catch2/tools/scripts/generateAmalgamatedFiles.py diff --git a/tools/scripts/majorRelease.py b/contrib/catch2/tools/scripts/majorRelease.py index eb712b4..eb712b4 100755 --- a/tools/scripts/majorRelease.py +++ b/contrib/catch2/tools/scripts/majorRelease.py diff --git a/tools/scripts/minorRelease.py b/contrib/catch2/tools/scripts/minorRelease.py index 0992c8f..0992c8f 100755 --- a/tools/scripts/minorRelease.py +++ b/contrib/catch2/tools/scripts/minorRelease.py diff --git a/tools/scripts/patchRelease.py b/contrib/catch2/tools/scripts/patchRelease.py index 48256c1..48256c1 100755 --- a/tools/scripts/patchRelease.py +++ b/contrib/catch2/tools/scripts/patchRelease.py diff --git a/tools/scripts/releaseCommon.py b/contrib/catch2/tools/scripts/releaseCommon.py index 81efa76..81efa76 100644 --- a/tools/scripts/releaseCommon.py +++ b/contrib/catch2/tools/scripts/releaseCommon.py diff --git a/tools/scripts/scriptCommon.py b/contrib/catch2/tools/scripts/scriptCommon.py index 5894185..5894185 100644 --- a/tools/scripts/scriptCommon.py +++ b/contrib/catch2/tools/scripts/scriptCommon.py diff --git a/tools/scripts/updateDocumentSnippets.py b/contrib/catch2/tools/scripts/updateDocumentSnippets.py index a070eea..a070eea 100755 --- a/tools/scripts/updateDocumentSnippets.py +++ b/contrib/catch2/tools/scripts/updateDocumentSnippets.py diff --git a/tools/scripts/updateDocumentToC.py b/contrib/catch2/tools/scripts/updateDocumentToC.py index 1840cec..1840cec 100755 --- a/tools/scripts/updateDocumentToC.py +++ b/contrib/catch2/tools/scripts/updateDocumentToC.py diff --git a/nihil.cli/CMakeLists.txt b/nihil.cli/CMakeLists.txt new file mode 100644 index 0000000..b05ed3a --- /dev/null +++ b/nihil.cli/CMakeLists.txt @@ -0,0 +1,40 @@ +# This source code is released into the public domain. + +add_library(nihil.cli STATIC) +target_link_libraries(nihil.cli PRIVATE nihil.util) +target_sources(nihil.cli + PUBLIC FILE_SET modules TYPE CXX_MODULES FILES + nihil.cli.ccm + + command.ccm + command_tree.ccm + command_node.ccm + dispatch_command.ccm + registry.ccm + usage_error.ccm + + PRIVATE + command.cc + command_tree.cc + command_node.cc + dispatch_command.cc + registry.cc +) + +if(NIHIL_TESTS) + enable_testing() + + add_executable(nihil.cli.test + test.cc + ) + target_link_libraries(nihil.cli.test PRIVATE + nihil.cli + Catch2::Catch2WithMain + ) + + find_package(Catch2 REQUIRED) + + include(CTest) + include(Catch) + catch_discover_tests(nihil.cli.test) +endif() diff --git a/nihil.cli/command.cc b/nihil.cli/command.cc new file mode 100644 index 0000000..725b4eb --- /dev/null +++ b/nihil.cli/command.cc @@ -0,0 +1,52 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <expected> +#include <functional> +#include <iostream> +#include <print> +#include <string> + +// For EX_USAGE. While <sysexits.h> is deprecated, there's no other standard +// exit code for 'usage error'; some programs use 2 (common on Linux), but +// 2 is also used for many other exit codes. +#include <sysexits.h> + +module nihil.cli; + +import nihil.error; +import :registry; + +namespace nihil { + +//NOLINTNEXTLINE(bugprone-easily-swappable-parameters) +command::command(std::string_view path, std::string_view usage, + command_function_t handler) + : command_node(path) + , m_usage(usage) + , m_handler(std::move(handler)) +{ + register_command(this); +} + +auto command::usage(this command const &self) noexcept -> std::string_view +{ + return self.m_usage; +} + +auto command::invoke(int argc, char **argv) const + -> std::expected<int, error> +{ + try { + return std::invoke(m_handler, argc, argv); + } catch (usage_error const &err) { + std::print(std::cerr, "{}\n", err.what()); + std::print(std::cerr, "usage: {} {}", path(), usage()); + return EX_USAGE; + } +} + +} // namespace nihil diff --git a/nihil.cli/command.ccm b/nihil.cli/command.ccm new file mode 100644 index 0000000..74ef030 --- /dev/null +++ b/nihil.cli/command.ccm @@ -0,0 +1,48 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <expected> +#include <functional> +#include <string> + +export module nihil.cli:command; + +import nihil.error; +import :command_node; + +namespace nihil { + +export struct command; + +/* + * A command that can be invoked. Instantiating a command adds this command + * to the global command table. If an error occurs, the program will abort. + */ + +using command_handler_t = int (int, char **); +using command_function_t = std::function<command_handler_t>; + +export struct command final : command_node { + command(std::string_view path, std::string_view usage, + command_function_t); + + command(std::string_view path, std::string_view usage, auto &&fn) + : command(path, usage, command_function_t(fn)) + {} + + [[nodiscard]] auto usage(this command const &) noexcept + -> std::string_view; + + [[nodiscard]] auto invoke(int argc, char **argv) const + -> std::expected<int, error> override; + +private: + std::string_view m_path; + std::string_view m_usage; + command_function_t m_handler; +}; + +} // namespace nihil diff --git a/nihil.cli/command_node.cc b/nihil.cli/command_node.cc new file mode 100644 index 0000000..dd18716 --- /dev/null +++ b/nihil.cli/command_node.cc @@ -0,0 +1,49 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <expected> +#include <iostream> +#include <print> +#include <string> + +#include <unistd.h> + +module nihil.cli; + +import nihil.core; +import nihil.error; + +namespace nihil { + +//NOLINTNEXTLINE(bugprone-exception-escape) +command_node::command_node(std::string_view path) noexcept +try : m_path(path) +{ +} catch (std::exception const &exc) { + std::fprintf(stderr, "%s\n", exc.what()); + _exit(1); + /*NOTREACHED*/ +} + +command_node::~command_node() +{ +} + +auto command_node::path(this command_node const &self) noexcept + -> std::string_view +{ + return self.m_path; +} + +auto command_node::invoke(int, char **) const + -> std::expected<int, error> +{ + // If invoke() wasn't overridden, then this is an empty node, + // so the command was incomplete. + return std::unexpected(error(errc::incomplete_command)); +} + +} // namespace nihil diff --git a/nihil.cli/command_node.ccm b/nihil.cli/command_node.ccm new file mode 100644 index 0000000..546eb46 --- /dev/null +++ b/nihil.cli/command_node.ccm @@ -0,0 +1,35 @@ +/* + * This source code is released into the public domain. + */ + +module; + +/* + * command_node represents a possibly-invocable command. + */ + +#include <expected> +#include <string> + +export module nihil.cli:command_node; + +import nihil.error; + +namespace nihil { + +export struct command_node { + command_node(std::string_view path) noexcept; + + virtual ~command_node(); + + [[nodiscard]] auto path(this command_node const &) noexcept + -> std::string_view; + + [[nodiscard]] virtual auto invoke(int argc, char **argv) const + -> std::expected<int, error>; + +private: + std::string m_path; +}; + +} // namespace nihil diff --git a/nihil.cli/command_tree.cc b/nihil.cli/command_tree.cc new file mode 100644 index 0000000..2d14669 --- /dev/null +++ b/nihil.cli/command_tree.cc @@ -0,0 +1,179 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <iostream> +#include <memory> +#include <print> +#include <ranges> +#include <string> +#include <vector> + +module nihil.cli; + +namespace nihil { + +command_tree_node::command_tree_node() + : m_this_word("") + , m_command(std::make_shared<command_node>(command_node(""))) +{ +} + +command_tree_node::command_tree_node(command_tree_node *parent, + std::string_view this_word) + : m_parent(parent) + , m_this_word(this_word) +{ +} + +command_tree_node::command_tree_node(command_tree_node *parent, + std::string_view this_word, + std::shared_ptr<command_node> command) + : m_parent(parent) + , m_this_word(this_word) + , m_command(std::move(command)) +{ +} + +auto command_tree_node::get_child(this command_tree_node const &self, + std::string_view child) + -> command_tree_node const * +{ + if (auto it = self.m_children.find(std::string(child)); + it != self.m_children.end()) + return &it->second; + + return nullptr; +} + +auto command_tree_node::get_child(this command_tree_node &self, + std::string_view child) + -> command_tree_node * +{ + if (auto it = self.m_children.find(std::string(child)); + it != self.m_children.end()) + return &it->second; + + return nullptr; +} + +auto command_tree_node::get_or_create_child(this command_tree_node &self, + std::string_view child) + -> command_tree_node * +{ + // Return the existing child, if there is one. + if (auto ptr = self.get_child(child); ptr != nullptr) + return ptr; + + // Insert a new child. + auto [it, ok] = self.m_children.emplace( + child, + command_tree_node(&self, child)); + + // Give the child a dummy command. + auto path = self.m_parent != nullptr + ? std::format("{} {}", self.m_parent->path(), child) + : std::string(child); + + it->second.m_command = std::make_shared<command_node>(path); + + return &it->second; +} + +auto command_tree_node::command(this command_tree_node const &self) + -> std::shared_ptr<command_node> const & +{ + return self.m_command; +} + +auto command_tree_node::command(this command_tree_node &self, + std::shared_ptr<command_node> command) + -> void +{ + // TODO: Put this check back without tripping from the dummy command. + //if (self.m_command != nullptr) + // throw std::logic_error("duplicate command"); + self.m_command = std::move(command); +} + +auto command_tree_node::print_commands(this command_tree_node const &self) + -> void +{ + auto prefix = std::string(self.path()); + + for (auto &&[name, node] : self.m_children) { + auto command = prefix.empty() + ? name + : (prefix + ' ' + name); + std::print(std::cerr, " {}\n", command); + } +} + +auto command_tree_node::path(this command_tree_node const &self) + -> std::string_view +{ + return self.m_command->path(); +} + +auto command_tree::insert(this command_tree &self, + std::vector<std::string_view> const &path, + std::shared_ptr<command_node> command) + -> void +{ + auto *this_node = &self.m_root_node; + + // Find the node for this key. + for (auto &&this_word : path) + this_node = this_node->get_or_create_child(this_word); + + // Set the new value. + this_node->command(std::move(command)); +} + +auto command_tree::find(this command_tree const &self, int &argc, char **&argv) + -> command_tree_node const * +{ + auto *this_node = &self.m_root_node; + + // Iterate until we don't find a child command, then return that node. + while (argv[0] != nullptr) { + auto *next_node = this_node->get_child(argv[0]); + + if (next_node == nullptr) + return this_node; + + this_node = next_node; + + --argc; + ++argv; + } + + // We ran out of path without finding a valid command. Return this + // node; the caller will notice the missing command. + return this_node; +} + +auto build_command_tree() -> command_tree +{ + auto const &commands = get_registered_commands(); + auto tree = command_tree(); + + for (auto &&command : commands) { + auto split_path = std::vector<std::string_view>( + std::from_range, + command->path() + | std::views::split(' ') + | std::views::transform([] (auto &&r) { + return std::string_view(r); + })); + + // Throws std::logic_error on duplicates. + tree.insert(split_path, command); + } + + return tree; +} + +} // namespace nihil diff --git a/nihil.cli/command_tree.ccm b/nihil.cli/command_tree.ccm new file mode 100644 index 0000000..7297af7 --- /dev/null +++ b/nihil.cli/command_tree.ccm @@ -0,0 +1,107 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <map> +#include <memory> +#include <optional> +#include <ranges> +#include <vector> + +export module nihil.cli:command_tree; + +import :command; + +namespace nihil { + +/* + * command_tree_node represents a possibly-empty node in the command tree. + * For example, if two commands "add foo" and "add bar" are defined, + * then "add" will be implicitly created as an empty node. + */ +struct command_tree_node final { + command_tree_node(); + + command_tree_node(command_tree_node *parent, + std::string_view this_word); + + command_tree_node(command_tree_node *parent, + std::string_view this_word, + std::shared_ptr<command_node> command); + + /* + * Return a child node, or NULL if the child doesn't exist. + */ + [[nodiscard]] auto get_child(this command_tree_node const &self, + std::string_view child) + -> command_tree_node const *; + + [[nodiscard]] auto get_child(this command_tree_node &self, + std::string_view child) + -> command_tree_node *; + + /* + * Return a child node if it exists, or insert a new empty node. + */ + [[nodiscard]] auto get_or_create_child(this command_tree_node &self, + std::string_view child) + -> command_tree_node *; + + /* + * Set or get this node's command. + */ + [[nodiscard]] auto command(this command_tree_node const &self) + -> std::shared_ptr<command_node> const &; + auto command(this command_tree_node &self, + std::shared_ptr<command_node>) + -> void; + + /* + * Get the path of this command_node. + */ + [[nodiscard]] auto path(this command_tree_node const &self) + -> std::string_view; + + /* + * Print this node's children in a form useful to humans. + */ + auto print_commands(this command_tree_node const &self) -> void; + +private: + command_tree_node *m_parent = nullptr; + std::string m_this_word; + std::shared_ptr<command_node> m_command; + std::map<std::string, command_tree_node> + m_children; +}; + +/* + * The command tree stores commands in a tree structure suitable for searching. + */ +struct command_tree { + /* + * Add a node to the tree. Returns false if the node already exists. + */ + auto insert(this command_tree &self, + std::vector<std::string_view> const &path, + std::shared_ptr<command_node> command) + -> void; + + /* + * Find a node in the tree. + */ + auto find(this command_tree const &self, int &argc, char **&argv) + -> command_tree_node const *; + +private: + command_tree_node m_root_node; +}; + +/* + * Build a command tree from the registry. + */ +[[nodiscard]] auto build_command_tree() -> command_tree; + +} // namespace nihil diff --git a/nihil.cli/dispatch_command.cc b/nihil.cli/dispatch_command.cc new file mode 100644 index 0000000..736e16e --- /dev/null +++ b/nihil.cli/dispatch_command.cc @@ -0,0 +1,82 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <cstdio> +#include <functional> +#include <iostream> +#include <map> +#include <print> +#include <ranges> +#include <string> +#include <utility> + +#include <unistd.h> + +module nihil.cli; + +import nihil.core; + +namespace nihil { + +auto dispatch_command(int argc, char **argv) -> int +{ + auto tree = build_command_tree(); + + // The caller should have stripped argv[0] already. find() will + // strip all the remaining elements except the last, which means + // argv[0] will be set to something reasonable for the next call + // to getopt(). + + // find() never returns nullptr; at worst it will return the + // root node. + auto const *node = tree.find(argc, argv); + + // Get the command_node. + auto const &command = node->command(); + + // Reset getopt(3) for the command, in case main() used it already. + optreset = 1; + optind = 1; + + /* + * Set the program name to the existing progname plus the full path + * to the command being invoked; this makes error messages nicer. + */ + auto *old_progname = ::getprogname(); + + { + auto cprogname = std::format("{} {}", ::getprogname(), + command->path()); + ::setprogname(cprogname.c_str()); + } + + // Invoke the command see what it returns. + auto ret = command->invoke(argc, argv); + + // Restore the old progname. + ::setprogname(old_progname); + + // If the command produced an exit code, return it. + if (ret) + return *ret; + + /* + * We have special handling for some errors. + */ + + // Incomplete command: print the list of valid commands at this node. + if (ret.error() == errc::incomplete_command) { + std::print(std::cerr, "{}: usage:\n", ::getprogname()); + node->print_commands(); + return 1; + } + + // We didn't recognise the error, so just print it and exit. + std::print(std::cerr, "{}\n", ret.error()); + return 1; +} + +} // namespace nihil diff --git a/nihil.cli/dispatch_command.ccm b/nihil.cli/dispatch_command.ccm new file mode 100644 index 0000000..1ba55bb --- /dev/null +++ b/nihil.cli/dispatch_command.ccm @@ -0,0 +1,31 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <functional> +#include <iostream> +#include <map> +#include <string> +#include <utility> + +export module nihil.cli:dispatch_command; + +import nihil.util; +import :command; +import :usage_error; + +namespace nihil { + +/* + * Invoke a command (which must have been previously registered) using + * the provided argument vector. + * + * The caller should have already stripped the executable name from argv[0] + * so that the vector starts with the command name. This is implicitly + * done if main() uses getopt(). + */ +export [[nodiscard]] auto dispatch_command(int argc, char **argv) -> int; + +} // namespace nihil diff --git a/nihil.cli/nihil.cli.ccm b/nihil.cli/nihil.cli.ccm new file mode 100644 index 0000000..6d98c05 --- /dev/null +++ b/nihil.cli/nihil.cli.ccm @@ -0,0 +1,14 @@ +/* + * This source code is released into the public domain. + */ + +module; + +export module nihil.cli; + +export import :command; +export import :command_tree; +export import :command_node; +export import :dispatch_command; +export import :registry; +export import :usage_error; diff --git a/nihil.cli/registry.cc b/nihil.cli/registry.cc new file mode 100644 index 0000000..e35078d --- /dev/null +++ b/nihil.cli/registry.cc @@ -0,0 +1,57 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <cstdio> +#include <exception> +#include <memory> +#include <vector> + +module nihil.cli; + +namespace nihil { + +/* + * Get the registry storage. Because this is called from global ctors, + * it handles exceptions itself. + */ +auto get_registry() noexcept -> std::vector<std::shared_ptr<command_node>> & +try { + static auto commands = std::vector<std::shared_ptr<command_node>>(); + return commands; +} catch (std::exception const &exc) { + std::printf("%s\n", exc.what()); + std::exit(1); +} catch (...) { + std::printf("get_registered_commands(): unknown error\n"); + std::exit(1); +} + +/* + * Register a new command. + */ +auto register_command(command *cmd) noexcept -> void +try { + auto null_deleter = [] (command_node const *) -> void {}; + + auto &commands = get_registry(); + commands.emplace_back(cmd, null_deleter); +} catch (std::exception const &exc) { + std::printf("%s\n", exc.what()); + std::exit(1); +} catch (...) { + std::printf("get_registered_commands(): unknown error\n"); + std::exit(1); +} + +/* + * Get the list of registered commands. + */ +auto get_registered_commands() -> std::span<std::shared_ptr<command_node>> +{ + return {get_registry()}; +} + +} // namespace nihil diff --git a/nihil.cli/registry.ccm b/nihil.cli/registry.ccm new file mode 100644 index 0000000..0b9754d --- /dev/null +++ b/nihil.cli/registry.ccm @@ -0,0 +1,28 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <memory> +#include <span> + +export module nihil.cli:registry; + +namespace nihil { + +export struct command; +export struct command_node; + +/* + * Register a command. This is guaranteed not to throw; errors will print + * a diagnostic and exit. + */ +auto register_command(command *cmd) noexcept -> void; + +/* + * Get previously registered commands. + */ +auto get_registered_commands() -> std::span<std::shared_ptr<command_node>>; + +} // namespace nihil diff --git a/nihil.cli/test.cc b/nihil.cli/test.cc new file mode 100644 index 0000000..c82281e --- /dev/null +++ b/nihil.cli/test.cc @@ -0,0 +1,104 @@ +/* + * This source code is released into the public domain. + */ + +#include <iostream> +#include <vector> + +#include <catch2/catch_test_macros.hpp> + +import nihil.cli; +import nihil.util; + +namespace { + +auto cmd_sub1_called = false; +auto cmd_sub1 = nihil::command("cmd sub1", "", [](int, char **) -> int +{ + cmd_sub1_called = true; + return 0; +}); + +auto cmd_sub2_called = false; +auto cmd_sub2 = nihil::command("cmd sub2", "", [](int, char **) -> int +{ + cmd_sub2_called = true; + return 0; +}); + +} // anonymous namespace + +TEST_CASE("nihil.cli: dispatch_command: basic", "[nihil.cli]") +{ + SECTION("cmd sub1") { + auto args = std::vector<char const *>{ + "cmd", "sub1", nullptr + }; + auto argv = const_cast<char **>(args.data()); + + int ret = nihil::dispatch_command( + static_cast<int>(args.size()) - 1, argv); + REQUIRE(ret == 0); + REQUIRE(cmd_sub1_called == true); + REQUIRE(cmd_sub2_called == false); + } + + SECTION("cmd sub2") { + auto args = std::vector<char const *>{ + "cmd", "sub2", nullptr + }; + auto argv = const_cast<char **>(args.data()); + + int ret = nihil::dispatch_command( + static_cast<int>(args.size()) - 1, argv); + REQUIRE(ret == 0); + REQUIRE(cmd_sub2_called == true); + } +} + +TEST_CASE("nihil.cli: dispatch_command: unknown command", "[nihil.cli]") +{ + auto args = std::vector<char const *>{ + "nocomd", "sub", nullptr + }; + auto argv = const_cast<char **>(args.data()); + + auto output = std::string(); + auto ret = int{}; + { + auto capture = nihil::capture_stream(std::cerr); + ret = nihil::dispatch_command( + static_cast<int>(args.size()) - 1, argv); + std::cerr.flush(); + output = capture.str(); + } + + REQUIRE(ret == 1); + + auto *progname = ::getprogname(); + REQUIRE(output == std::format("{}: usage:\n cmd\n", progname)); +} + +TEST_CASE("nihil.cli: dispatch_command: incomplete command", "[nihil.cli]") +{ + auto args = std::vector<char const *>{ + "cmd", nullptr + }; + auto argv = const_cast<char **>(args.data()); + + auto output = std::string(); + auto ret = int{}; + { + auto capture = nihil::capture_stream(std::cerr); + ret = nihil::dispatch_command( + static_cast<int>(args.size()) - 1, argv); + std::cerr.flush(); + output = capture.str(); + } + + REQUIRE(ret == 1); + + auto *progname = ::getprogname(); + REQUIRE(output == std::format("{}: usage:\n cmd sub1\n cmd sub2\n", + progname)); +} diff --git a/nihil.cli/usage_error.ccm b/nihil.cli/usage_error.ccm new file mode 100644 index 0000000..61feba7 --- /dev/null +++ b/nihil.cli/usage_error.ccm @@ -0,0 +1,22 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <string> + +export module nihil.cli:usage_error; + +import nihil.error; + +namespace nihil { + +/* + * Exception thrown to indicate invalid command-line arguments. + */ +export struct usage_error : error { + usage_error(std::string_view what) : error(what) {} +}; + +} // namespace nihil diff --git a/nihil.config/CMakeLists.txt b/nihil.config/CMakeLists.txt new file mode 100644 index 0000000..8a52d3c --- /dev/null +++ b/nihil.config/CMakeLists.txt @@ -0,0 +1,31 @@ +# This source code is released into the public domain. + +add_library(nihil.config STATIC) +target_link_libraries(nihil.config PRIVATE + nihil.error + nihil.generator + nihil.posix + nihil.ucl +) +target_sources(nihil.config + PUBLIC FILE_SET modules TYPE CXX_MODULES FILES + nihil.config.ccm + read.ccm + store.ccm + write.ccm + + option.ccm + string.ccm + + PRIVATE + option.cc + read.cc + store.cc + string.cc + write.cc +) + +if(NIHIL_TESTS) + add_subdirectory(tests) + enable_testing() +endif() diff --git a/nihil.config/nihil.config.ccm b/nihil.config/nihil.config.ccm new file mode 100644 index 0000000..8957305 --- /dev/null +++ b/nihil.config/nihil.config.ccm @@ -0,0 +1,13 @@ +/* + * This source code is released into the public domain. + */ + +module; + +export module nihil.config; + +export import :option; +export import :read; +export import :store; +export import :string; +export import :write; diff --git a/nihil.config/option.cc b/nihil.config/option.cc new file mode 100644 index 0000000..886f4b6 --- /dev/null +++ b/nihil.config/option.cc @@ -0,0 +1,97 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <coroutine> +#include <expected> +#include <iostream> +#include <string> + +module nihil.config; + +import nihil.error; +import nihil.monad; +import nihil.ucl; + +namespace nihil::config { + +//NOLINTNEXTLINE(bugprone-easily-swappable-parameters) +option::option(std::string_view name, std::string_view description) + : m_name(name) + , m_description(description) +{ + auto okay = store::get().register_option(this); + if (okay) + return; + + std::print(std::cerr, + "INTERNAL ERROR: failed to register " + "configuration option '{}': {}", + m_name, okay.error()); + std::exit(1); +} + +option::~option() +{ + std::ignore = store::get().unregister_option(this); +} + +auto option::name(this option const &self) noexcept + -> std::string_view +{ + return self.m_name; +} + +auto option::description(this option const &self) noexcept + -> std::string_view +{ + return self.m_description; +} + +auto option::is_default(this option const &self) noexcept + -> bool +{ + return self.m_is_default; +} + +auto option::is_default(this option &self, bool b) -> void +{ + self.m_is_default = b; +} + +auto option::string(this option const &self) -> std::string +{ + return self.get_string(); +} + +auto option::string(this option &self, std::string_view value) + -> std::expected<void, error> +{ + co_await self.set_string(value); + self.is_default(false); + co_return {}; +} + +auto option::ucl(this option const &self) + -> std::expected<nihil::ucl::object, error> +{ + return self.get_ucl(); +} + +auto option::ucl(this option &self, nihil::ucl::object const &value) + -> std::expected<void, error> +{ + co_await self.set_ucl(value); + self.is_default(false); + co_return {}; +} + +auto operator<<(std::ostream &strm, option const &opt) +-> std::ostream & +{ + return strm << "<" << opt.name() << "=" << opt.string() << ">"; +} + +} // namespace nihil diff --git a/nihil.config/option.ccm b/nihil.config/option.ccm new file mode 100644 index 0000000..4b95793 --- /dev/null +++ b/nihil.config/option.ccm @@ -0,0 +1,105 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <expected> +#include <iosfwd> +#include <string> + +export module nihil.config:option; + +import nihil.error; +import nihil.ucl; + +namespace nihil::config { + +/* + * Base class for options; this is what config_store interacts with. + * + * Base classes should override the four protected functions: + * + * get_string() + * set_string() + * get_ucl() + * set_ucl() + * + * Overriding any other members is not permitted. + */ + +export struct option +{ + virtual ~option(); + + // Short name of this option. + [[nodiscard]] auto name(this option const &) noexcept + -> std::string_view; + + // Human-readable description of this option. + [[nodiscard]] auto description(this option const &) noexcept + -> std::string_view; + + // If true, this option is set to its default value. + [[nodiscard]] auto is_default(this option const &) noexcept + -> bool; + + /* + * Get or set this option as a string. The specific conversion + * method depends on the derived option type. + */ + [[nodiscard]] auto string(this option const &) -> std::string; + [[nodiscard]] auto string(this option &, std::string_view value) + -> std::expected<void, error>; + + /* + * Return this object as a UCL object. This is used when writing the + * configuration file. + */ + [[nodiscard]] auto ucl(this option const &) + -> std::expected<ucl::object, error>; + + /* + * Set this object from a UCL object. This is used when reading the + * configuration file. + */ + [[nodiscard]] auto ucl(this option &, ucl::object const &) + -> std::expected<void, error>; + + // Not copyable or movable. + option(option const &) = delete; + auto operator=(option const &) -> option& = delete; + +protected: + option(std::string_view name, std::string_view description); + + auto is_default(this option &, bool) -> void; + + /* + * Get or set this option as a string. + */ + [[nodiscard]] virtual auto get_string() const + -> std::string = 0; + [[nodiscard]] virtual auto set_string(std::string_view) + -> std::expected<void, error> = 0; + + /* + * Get or set this option as a UCL object. + */ + [[nodiscard]] virtual auto get_ucl() const + -> std::expected<ucl::object, error> = 0; + [[nodiscard]] virtual auto set_ucl(ucl::object const &) + -> std::expected<void, error> = 0; + +private: + std::string m_name; + std::string m_description; + bool m_is_default = true; +}; + +/* + * Make options printable. This is mostly useful for testing. + */ +export auto operator<<(std::ostream &strm, option const &opt) -> std::ostream &; + +} // namespace nihil diff --git a/nihil.config/read.cc b/nihil.config/read.cc new file mode 100644 index 0000000..48484fb --- /dev/null +++ b/nihil.config/read.cc @@ -0,0 +1,50 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <coroutine> +#include <expected> +#include <filesystem> +#include <format> +#include <iterator> +#include <string> + +module nihil.config; + +import nihil.error; +import nihil.monad; +import nihil.posix; +import nihil.ucl; + +namespace nihil::config { + +auto read_from(std::filesystem::path const &filename) + -> std::expected<void, error> +{ + // TODO: nihil.ucl should have a way to load UCL from a filename. + + std::string config_text; + auto err = read_file(filename, std::back_inserter(config_text)); + if (!err) { + // Ignore ENOENT, it simply means we haven't created the + // config file yet, so default values will be used. + if (err.error().root_cause() == std::errc::no_such_file_or_directory) + co_return {}; + auto errstr = std::format("cannot read {}", filename.string()); + co_return std::unexpected(error(errstr, err.error())); + } + + // Parse the UCL. + auto uclconfig = co_await ucl::parse(config_text); + + for (auto &&[key, value] : uclconfig) { + auto opt = co_await store::get().fetch(key); + co_await opt->ucl(value); + } + + co_return {}; +} + +} // namespace nihil::config diff --git a/nihil.config/read.ccm b/nihil.config/read.ccm new file mode 100644 index 0000000..9cf28c9 --- /dev/null +++ b/nihil.config/read.ccm @@ -0,0 +1,22 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <expected> +#include <filesystem> + +export module nihil.config:read; + +import nihil.error; + +namespace nihil::config { + +/* + * Load the configuration from a file. + */ +export [[nodiscard]] auto read_from(std::filesystem::path const &filename) + -> std::expected<void, error>; + +} // namespace nihil::config diff --git a/nihil.config/store.cc b/nihil.config/store.cc new file mode 100644 index 0000000..0fb8cc0 --- /dev/null +++ b/nihil.config/store.cc @@ -0,0 +1,95 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <coroutine> +#include <expected> +#include <filesystem> +#include <format> +#include <map> + +module nihil.config; + +import nihil.error; +import nihil.generator; +import nihil.monad; + +namespace nihil::config { + +store::store() = default; + +auto store::get() -> store & +{ + static auto instance = store(); + return instance; +} + + +auto store::register_option(this store &self, option *object) + -> std::expected<void, error> +{ + auto [it, okay] = self.m_options.insert( + std::pair{object->name(), object}); + + if (okay) + return {}; + + return std::unexpected(error(std::format( + "attempt to register duplicate " + "configuration option '{0}'", + object->name()))); +} + +auto store::unregister_option(this store &self, option *object) + -> std::expected<void, error> +{ + auto it = self.m_options.find(object->name()); + if (it == self.m_options.end()) + return std::unexpected(error(std::format( + "attempt to unregister non-existent " + "configuration option '{}'", + object->name()))); + + self.m_options.erase(it); + return {}; +} + +auto store::fetch(this store const &self, std::string_view name) + -> std::expected<option const *, error> +{ + if (auto it = self.m_options.find(name); it != self.m_options.end()) + return it->second; + + return std::unexpected(error(std::format( + "unknown configuration option '{}'", + name))); +} + +auto store::fetch(this store &self, std::string_view name) + -> std::expected<option *, error> +{ + auto opt = co_await static_cast<store const &>(self).fetch(name); + co_return const_cast<option *>(opt); +} + +auto store::all(this store const &self) -> nihil::generator<option const *> +{ + for (auto &&it : self.m_options) + co_yield it.second; +} + +auto store::all(this store &self) -> nihil::generator<option *> +{ + for (auto &&it : self.m_options) + co_yield it.second; +} + +auto get_option(std::string_view option_name) + -> std::expected<option *, error> +{ + co_return co_await store::get().fetch(option_name); +} + +} // namespace nihil::config diff --git a/nihil.config/store.ccm b/nihil.config/store.ccm new file mode 100644 index 0000000..4d37ce0 --- /dev/null +++ b/nihil.config/store.ccm @@ -0,0 +1,75 @@ +/* + * This source code is released into the public domain. + */ + +module; + +/* + * The configuration store. There should only be one of these. + */ + +#include <coroutine> +#include <expected> +#include <string> +#include <map> + +export module nihil.config:store; + +import nihil.generator; +import :option; + +namespace nihil::config { + +struct store final { + /* + * Get the global config store. + */ + [[nodiscard]] static auto get() -> store &; + + /* + * Register a new value with the config store. + */ + [[nodiscard]] auto register_option(this store &, option *object) + -> std::expected<void, error>; + + /* + * Remove a value from the config store. + */ + [[nodiscard]] auto unregister_option(this store &, option *object) + -> std::expected<void, error>; + + /* + * Fetch an existing value in the config store. + */ + [[nodiscard]] auto fetch(this store const &, std::string_view name) + -> std::expected<option const *, error>; + [[nodiscard]] auto fetch(this store &, std::string_view name) + -> std::expected<option *, error>; + + /* + * Fetch all values in the configuration store. + */ + [[nodiscard]] auto all(this store const &self) + -> nihil::generator<option const *>; + [[nodiscard]] auto all(this store &self) + -> nihil::generator<option *>; + + // Not movable or copyable. + store(store const &) = delete; + store(store &&) = delete; + store& operator=(store const &) = delete; + store& operator=(store &&) = delete; + +private: + store(); + + std::map<std::string_view, option *> m_options; +}; + +/* + * The public API. + */ +export auto get_option(std::string_view option_name) + -> std::expected<option *, error>; + +} // namespace nihil::config diff --git a/nihil.config/string.cc b/nihil.config/string.cc new file mode 100644 index 0000000..0ca4605 --- /dev/null +++ b/nihil.config/string.cc @@ -0,0 +1,62 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <coroutine> +#include <expected> +#include <format> +#include <string> + +module nihil.config; + +import nihil.error; +import nihil.monad; +import nihil.ucl; + +namespace nihil::config { + +string::string( + std::string &storage, + std::string_view name, + std::string_view description) noexcept + : option(name, description) + , m_storage(storage) +{ +} + +string::~string() = default; + +auto string::get_string() const -> std::string +{ + return m_storage; +} + +auto string::set_string(std::string_view new_value) + -> std::expected<void, error> +{ + m_storage = new_value; + return {}; +} + +auto string::get_ucl() const -> std::expected<ucl::object, error> +{ + return ucl::string(m_storage); +} + +auto string::set_ucl(ucl::object const &uclobj) -> std::expected<void, error> +{ + auto obj = co_await object_cast<ucl::string>(uclobj) + .transform_error([&] (ucl::type_mismatch const &m) { + return error(std::format( + "'{}': expected string, not {}", + name(), str(m.actual_type()))); + }); + + m_storage = obj.value(); + is_default(false); + co_return {}; +} + +} // namespace nihil::config diff --git a/nihil.config/string.ccm b/nihil.config/string.ccm new file mode 100644 index 0000000..668bbc0 --- /dev/null +++ b/nihil.config/string.ccm @@ -0,0 +1,56 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <expected> +#include <format> +#include <string> + +export module nihil.config:string; + +import nihil.ucl; +import :option; + +namespace nihil::config { + +/* + * A string option. The backing type is std::string. + */ +export struct string final : option +{ + string(std::string &storage, + std::string_view name, + std::string_view description) noexcept; + + ~string(); + + /* + * Get this option as a string; simply returns the storage. + */ + [[nodiscard]] auto get_string() const -> std::string override; + + /* + * Set this option to a string value; assigns to the storage. + */ + [[nodiscard]] auto set_string(std::string_view new_value) + -> std::expected<void, error> override; + + /* + * Convert this option to a UCL object. + */ + [[nodiscard]] auto get_ucl() const + -> std::expected<ucl::object, error> override; + + /* + * Set this option from a UCL object. + */ + [[nodiscard]] auto set_ucl(ucl::object const &uclobj) + -> std::expected<void, error> override; + +private: + std::string &m_storage; +}; + +} // namespace nihil::config diff --git a/nihil.config/tests/CMakeLists.txt b/nihil.config/tests/CMakeLists.txt new file mode 100644 index 0000000..1805f7f --- /dev/null +++ b/nihil.config/tests/CMakeLists.txt @@ -0,0 +1,15 @@ +# This source code is released into the public domain. + +add_executable(nihil.config.test + string.cc +) + +target_link_libraries(nihil.config.test PRIVATE + nihil.config + Catch2::Catch2WithMain) + +find_package(Catch2 REQUIRED) + +include(CTest) +include(Catch) +catch_discover_tests(nihil.config.test) diff --git a/nihil.config/tests/string.cc b/nihil.config/tests/string.cc new file mode 100644 index 0000000..aeb1ef8 --- /dev/null +++ b/nihil.config/tests/string.cc @@ -0,0 +1,36 @@ +/* + * This source code is released into the public domain. + */ + +#include <string> + +#include <catch2/catch_test_macros.hpp> + +import nihil.config; + +TEST_CASE("nihil.config: string option", "[nihil][nihil.config]") +{ + std::string storage; + + auto opt = nihil::config::get_option("test_option"); + REQUIRE(!opt); + + { + auto string_option = nihil::config::string( + storage, "test_option", "This is a test option"); + + auto opt = nihil::config::get_option("test_option"); + REQUIRE(opt); + + REQUIRE((*opt)->name() == "test_option"); + REQUIRE((*opt)->description() == "This is a test option"); + REQUIRE((*opt)->is_default() == true); + REQUIRE((*opt)->string() == ""); + + REQUIRE((*opt)->string("testing")); + REQUIRE(storage == "testing"); + } + + opt = nihil::config::get_option("test_option"); + REQUIRE(!opt); +} diff --git a/nihil.config/write.cc b/nihil.config/write.cc new file mode 100644 index 0000000..80125a8 --- /dev/null +++ b/nihil.config/write.cc @@ -0,0 +1,41 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <coroutine> +#include <expected> +#include <filesystem> +#include <format> +#include <utility> + +module nihil.config; + +import nihil.error; +import nihil.monad; +import nihil.posix; +import nihil.ucl; + +namespace nihil::config { + +auto write_to(std::filesystem::path const &filename) + -> std::expected<void, error> +{ + auto uclconfig = ucl::map<ucl::object>(); + + // Add all the options to the UCL object. + for (auto const &option : store::get().all()) { + if (option->is_default()) + continue; + + auto uobj = co_await option->ucl(); + uclconfig.insert({option->name(), uobj}); + } + + auto ucl_text = std::format("{:c}", uclconfig); + co_await safe_write_file(filename, ucl_text); + co_return {}; +} + +}; diff --git a/nihil.config/write.ccm b/nihil.config/write.ccm new file mode 100644 index 0000000..564bb20 --- /dev/null +++ b/nihil.config/write.ccm @@ -0,0 +1,22 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <expected> +#include <filesystem> + +export module nihil.config:write; + +import nihil.error; + +namespace nihil::config { + +/* + * Write all config values (except defaults) to disk. + */ +export [[nodiscard]] auto write_to(std::filesystem::path const &filename) -> + std::expected<void, error>; + +}; diff --git a/nihil.core/CMakeLists.txt b/nihil.core/CMakeLists.txt new file mode 100644 index 0000000..2a7b3e2 --- /dev/null +++ b/nihil.core/CMakeLists.txt @@ -0,0 +1,12 @@ +# This source code is released into the public domain. + +add_library(nihil.core STATIC) +target_include_directories(nihil.core PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +target_sources(nihil.core + PUBLIC FILE_SET modules TYPE CXX_MODULES FILES + nihil.core.ccm + errc.ccm + + PRIVATE + errc.cc +) diff --git a/nihil.core/errc.cc b/nihil.core/errc.cc new file mode 100644 index 0000000..35c9d8f --- /dev/null +++ b/nihil.core/errc.cc @@ -0,0 +1,51 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <string> +#include <system_error> + +module nihil.core; + +namespace nihil { + +struct nihil_error_category final : std::error_category { + [[nodiscard]] auto name() const noexcept -> char const * override; + [[nodiscard]] auto message(int err) const -> std::string override; +}; + +[[nodiscard]] auto nihil_category() noexcept -> std::error_category & +{ + static auto category = nihil_error_category(); + return category; +} + +auto make_error_condition(errc ec) -> std::error_condition +{ + return {static_cast<int>(ec), nihil_category()}; +} + +auto nihil_error_category::name() const noexcept -> char const * +{ + return "nihil"; +} + +auto nihil_error_category::message(int err) const -> std::string +{ + switch (static_cast<errc>(err)) { + case errc::no_error: + return "No error"; + case errc::incomplete_command: + return "Incomplete command"; + case errc::empty_string: + return "Empty string is not permitted"; + case errc::invalid_unit: + return "Invalid unit specifier"; + default: + return "Undefined error"; + } +} + +} // namespace nihil diff --git a/nihil.core/errc.ccm b/nihil.core/errc.ccm new file mode 100644 index 0000000..c597faf --- /dev/null +++ b/nihil.core/errc.ccm @@ -0,0 +1,44 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <string> +#include <system_error> + +export module nihil.core:errc; + +namespace nihil { + +export enum struct errc { + no_error = 0, + + /* + * nihil.command + */ + + incomplete_command, + + /* + * nihil.util + */ + + // Empty string is not allowed. + empty_string, + + // Invalid unit, e.g. in parse_size() + invalid_unit, +}; + +export [[nodiscard]] auto nihil_category() noexcept -> std::error_category &; +export [[nodiscard]] auto make_error_condition(errc ec) -> std::error_condition; + +} // namespace nihil + +namespace std { + +export template<> +struct is_error_condition_enum<nihil::errc> : true_type {}; + +} // namespace std diff --git a/nihil.core/nihil.core.ccm b/nihil.core/nihil.core.ccm new file mode 100644 index 0000000..a7a4100 --- /dev/null +++ b/nihil.core/nihil.core.ccm @@ -0,0 +1,9 @@ +/* + * This source code is released into the public domain. + */ + +module; + +export module nihil.core; + +export import :errc; diff --git a/nihil.core/nihil.hh b/nihil.core/nihil.hh new file mode 100644 index 0000000..cd7e789 --- /dev/null +++ b/nihil.core/nihil.hh @@ -0,0 +1,26 @@ +/* +* This source code is released into the public domain. + */ + +#ifndef NIHIL_HH_INCLUDED +#define NIHIL_HH_INCLUDED + +#if __has_include(<sys/param.h>) +# include <sys/param.h> +#endif + +#if defined(__FreeBSD_version) + +/* fexecve() added in FreeBSD 8.0 */ +#if (__FreeBSD_version >= 800000) +# define NIHIL_HAVE_FEXECVE +#endif + +/* getenv_r() added in FreeBSD 15.0 */ +#if (__FreeBSD_version >= 1500000) +# define NIHIL_HAVE_GETENV_R +#endif + +#endif // defined(__FreeBSD_version) + +#endif // !NIHIL_HH_INCLUDED diff --git a/nihil.error/CMakeLists.txt b/nihil.error/CMakeLists.txt new file mode 100644 index 0000000..1316b71 --- /dev/null +++ b/nihil.error/CMakeLists.txt @@ -0,0 +1,29 @@ +# This source code is released into the public domain. + +add_library(nihil.error STATIC) +target_link_libraries(nihil.error PRIVATE nihil.match) +target_sources(nihil.error + PUBLIC FILE_SET modules TYPE CXX_MODULES FILES + error.ccm + + PRIVATE + error.cc +) + +if(NIHIL_TESTS) + enable_testing() + + add_executable(nihil.error.test + test.cc) + + target_link_libraries(nihil.error.test PRIVATE + nihil.error + Catch2::Catch2WithMain + ) + + find_package(Catch2 REQUIRED) + + include(CTest) + include(Catch) + catch_discover_tests(nihil.error.test) +endif() diff --git a/nihil.error/error.cc b/nihil.error/error.cc new file mode 100644 index 0000000..e4023f9 --- /dev/null +++ b/nihil.error/error.cc @@ -0,0 +1,160 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <iostream> +#include <memory> +#include <optional> +#include <string> +#include <system_error> +#include <variant> + +module nihil.error; + +import nihil.match; + +namespace nihil { + +auto to_string(error const &self) -> std::string +{ + auto ret = self.str(); + + auto cause = self.cause(); + while (cause) { + ret += ": " + cause->str(); + cause = cause->cause(); + } + + return ret; +} + +error::error() +{ +} + +error::~error() = default; + +error::error(std::string_view what, error cause) + : m_error(std::string(what)) + , m_cause(std::make_shared<error>(std::move(cause))) +{ +} +error::error(std::string_view what) + : m_error(std::string(what)) +{ +} + +error::error(std::error_condition what, error cause) + : m_error(what) + , m_cause(std::make_shared<error>(std::move(cause))) +{ +} + +error::error(std::error_condition what) + : m_error(what) +{ +} + +error::error(std::error_code what, error cause) + : m_error(what) + , m_cause(std::make_shared<error>(std::move(cause))) +{ +} + +error::error(std::error_code what) + : m_error(what) +{ +} + +error::error(error const &) = default; +error::error(error &&) noexcept = default; +auto error::operator=(this error &, error const &) -> error & = default; +auto error::operator=(this error &, error &&) noexcept -> error & = default; + +auto error::cause(this error const &self) -> std::shared_ptr<error> +{ + if (self.m_cause) + return self.m_cause; + return {}; +} + +auto error::root_cause(this error const &self) -> error const & +{ + if (self.m_cause) + return self.m_cause->root_cause(); + + return self; //NOLINT(bugprone-return-const-ref-from-parameter) +} + +auto error::str(this error const &self) -> std::string +{ + return self.m_error | match { + [] (std::monostate) -> std::string { + return "No error"; + }, + [] (std::error_code const &m) { + return m.message(); + }, + [] (std::error_condition const &m) { + return m.message(); + }, + [] (std::string const &m) { + return m; + } + }; +} + +auto error::code(this error const &self) -> std::optional<std::error_code> +{ + auto const *code = std::get_if<std::error_code>(&self.m_error); + if (code) + return {*code}; + return {}; +} + +auto error::condition(this error const &self) + -> std::optional<std::error_condition> +{ + auto const *condition = std::get_if<std::error_condition>(&self.m_error); + if (condition) + return {*condition}; + return {}; +} + +auto error::what() const noexcept -> char const * +{ + if (!m_what) + m_what = to_string(*this); + + return m_what->c_str(); +} + +auto operator==(error const &lhs, error const &rhs) -> bool +{ + return lhs.m_error == rhs.m_error; +} + +auto operator<=>(error const &lhs, error const &rhs) -> std::strong_ordering +{ + return lhs.m_error <=> rhs.m_error; +} + +auto operator==(error const &lhs, std::error_code const &rhs) -> bool +{ + return lhs.code() == rhs; +} + +// Compare an error to an std::error_condition. +auto operator==(error const &lhs, std::error_condition const &rhs) -> bool +{ + return lhs.condition() == rhs; +} + +auto operator<<(std::ostream &strm, error const &e) -> std::ostream & +{ + return strm << to_string(e); +} + +} // namespace nihil diff --git a/nihil.error/error.ccm b/nihil.error/error.ccm new file mode 100644 index 0000000..12d47cc --- /dev/null +++ b/nihil.error/error.ccm @@ -0,0 +1,199 @@ +/* + * This source code is released into the public domain. + */ + +module; + +/* + * error: a type representing an error. + * + * An error consists of an immediate cause, which may be a string or + * std:error_code, and an optional proximate cause, which is another error + * object. Any number of error objects may be stacked. + * + * For example, a failure to open a file might be a stack of two errors: + * + * - string, "failed to open /etc/somefile", + * - std::error_code, "No such file or directory". + * + * Calling .str() will format the entire stack starting at that error, + * for example: "failed to open /etc/somefile: No such file or directory". + * + * Errors may be moved and (relatively) cheaply copied, since the cause + * chain is refcounted. + * + * error derives from std::exception, so it may be thrown and caught and + * provides a useful what(). When throwing errors, creating a derived + * error will make it easier to distinguish errors when catching them. + */ + +#include <iosfwd> +#include <format> +#include <memory> +#include <optional> +#include <string> +#include <system_error> +#include <utility> +#include <variant> + +export module nihil.error; + +namespace nihil { + +// Things which can be errors. +using error_t = std::variant< + std::monostate, + std::string, + std::error_code, + std::error_condition + >; + +export struct error : std::exception { + // Create an empty error, representing success. + error(); + + // Destroy an error. + ~error() override; + + // Create an error from a freeform string. + error(std::string_view what, error cause); + explicit error(std::string_view what); + + template<typename Cause> + requires(std::is_error_code_enum<Cause>::value || + std::is_error_condition_enum<Cause>::value) + error(std::string_view what, Cause &&cause) + : error(what, error(std::forward<Cause>(cause))) + {} + + // Create an error from an std::error_code. + error(std::error_condition what, error cause); + explicit error(std::error_condition what); + + // Create an error from an std::error_condition. + error(std::error_code what, error cause); + explicit error(std::error_code what); + + // Create an error from an std::error_code enum. + error(auto errc, error cause) + requires(std::is_error_code_enum<decltype(errc)>::value) + : error(make_error_code(errc), std::move(cause)) + {} + + explicit error(auto errc) + requires(std::is_error_code_enum<decltype(errc)>::value) + : error(make_error_code(errc)) + {} + + // Create an error from an std::error_condition enum. + error(auto errc, error cause) + requires(std::is_error_condition_enum<decltype(errc)>::value) + : error(make_error_condition(errc), std::move(cause)) + {} + + explicit error(auto errc) + requires(std::is_error_condition_enum<decltype(errc)>::value) + : error(make_error_condition(errc)) + {} + + error(error const &); + error(error &&) noexcept; + + auto operator=(this error &, error const &) -> error &; + auto operator=(this error &, error &&) noexcept -> error &; + + // Return the cause of this error. + [[nodiscard]] auto cause(this error const &) -> std::shared_ptr<error>; + + // Return the root cause of this error, which may be this object. + // For errors caused by an OS error, this will typically be the + // error_code error. + [[nodiscard]] auto root_cause(this error const &) -> error const &; + + // Format this error as a string. + [[nodiscard]] auto str(this error const &) -> std::string; + + // Return this error's error_code, if any. + [[nodiscard]] auto code(this error const &) + -> std::optional<std::error_code>; + + // Return this error's error_condition, if any. + [[nodiscard]] auto condition(this error const &) + -> std::optional<std::error_condition>; + + [[nodiscard]] auto what() const noexcept -> char const * final; + +private: + friend auto operator==(error const &, error const &) -> bool; + friend auto operator<=>(error const &, error const &) + -> std::strong_ordering; + + // This error. + error_t m_error = make_error_code(std::errc()); + + // The cause of this error, if any. + std::shared_ptr<error> m_cause; + + // For std::exception::what(), we need to keep the string valid + // until we're destroyed. + mutable std::optional<std::string> m_what; +}; + +/* + * Format an error and its cause(s) as a string. + */ +export [[nodiscard]] auto to_string(error const &) -> std::string; + +// Compare an error to another error. This only compares the error itself, +// not any nested causes. +export [[nodiscard]] auto operator==(error const &, error const &) + -> bool; +export [[nodiscard]] auto operator<=>(error const &, error const &) + -> std::strong_ordering; + +// Compare an error to an std::error_code. +export [[nodiscard]] auto operator==(error const &, std::error_code const &) + -> bool; + +// Compare an error to an std::error_condition. +export [[nodiscard]] auto operator==(error const &, + std::error_condition const &) + -> bool; + +// Compare an error to an std::error_code enum. +export [[nodiscard]] auto operator==(error const &lhs, auto rhs) -> bool +requires(std::is_error_code_enum<decltype(rhs)>::value) +{ + return lhs.code() == rhs; +} + +// Compare an error to an std::error_condition enum. +export [[nodiscard]] auto operator==(error const &lhs, auto rhs) -> bool +requires(std::is_error_condition_enum<decltype(rhs)>::value) +{ + return lhs.condition() == rhs; +} + +// Print an error to an ostream. +export [[nodiscard]] auto operator<<(std::ostream &, error const &) + -> std::ostream &; + +} // namespace nihil + +// Make error formattable. +export template<> +struct std::formatter<nihil::error, char> +{ + template<typename ParseContext> + constexpr auto parse(ParseContext &ctx) -> ParseContext::iterator + { + return ctx.begin(); + } + + template<typename FormatContext> + auto format(nihil::error const &e, FormatContext &ctx) const + -> FormatContext::iterator + { + return std::ranges::copy(to_string(e), ctx.out()).out; + } +}; diff --git a/nihil.error/test.cc b/nihil.error/test.cc new file mode 100644 index 0000000..9b3eef1 --- /dev/null +++ b/nihil.error/test.cc @@ -0,0 +1,169 @@ +/* + * This source code is released into the public domain. + */ + +#include <cerrno> +#include <cstring> +#include <system_error> + +#include <catch2/catch_test_macros.hpp> + +import nihil.error; + +TEST_CASE("error: invariants", "[nihil]") +{ + static_assert(std::destructible<nihil::error>); + static_assert(std::default_initializable<nihil::error>); + static_assert(std::move_constructible<nihil::error>); + static_assert(std::copy_constructible<nihil::error>); + static_assert(std::equality_comparable<nihil::error>); + static_assert(std::totally_ordered<nihil::error>); + static_assert(std::swappable<nihil::error>); + static_assert(std::regular<nihil::error>); +} + +TEST_CASE("error: construct from string", "[nihil]") +{ + using namespace nihil; + + auto e = error("an error"); + REQUIRE(e.str() == to_string(e)); + REQUIRE(to_string(e) == "an error"); + REQUIRE(std::format("{}", e) == to_string(e)); +} + +TEST_CASE("error: construct from std::error_condition", "[nihil]") +{ + using namespace nihil; + + auto code = std::make_error_condition(std::errc::invalid_argument); + auto e = error(code); + + REQUIRE(!e.cause()); + REQUIRE(e.code().has_value() == false); + REQUIRE(e.condition().has_value() == true); + + REQUIRE(e == std::errc::invalid_argument); + REQUIRE(e != std::errc::no_such_file_or_directory); + + REQUIRE(e.str() == to_string(e)); + REQUIRE(to_string(e) == std::strerror(EINVAL)); + REQUIRE(std::format("{}", e) == to_string(e)); +} + +TEST_CASE("error: construct from std::errc", "[nihil]") +{ + using namespace nihil; + + auto e = error(std::errc::invalid_argument); + + REQUIRE(!e.cause()); + REQUIRE(e.code().has_value() == false); + REQUIRE(e.condition().has_value() == true); + + REQUIRE(e == std::errc::invalid_argument); + REQUIRE(e != std::errc::no_such_file_or_directory); + + REQUIRE(e.str() == to_string(e)); + REQUIRE(to_string(e) == std::strerror(EINVAL)); + REQUIRE(std::format("{}", e) == to_string(e)); +} + +TEST_CASE("error: compound error", "[nihil]") +{ + using namespace std::literals; + using namespace nihil; + + auto e = error("cannot open file", + error(std::errc::no_such_file_or_directory)); + + REQUIRE(e.cause()); + REQUIRE(e.code().has_value() == false); + REQUIRE(e.condition().has_value() == false); + + REQUIRE(*e.cause() == std::errc::no_such_file_or_directory); + REQUIRE(e.str() == "cannot open file"); + REQUIRE(to_string(e) == ("cannot open file: "s + + std::strerror(ENOENT))); + REQUIRE(std::format("{}", e) == to_string(e)); +} + +TEST_CASE("error: operator== with strings", "[nihil]") +{ + using namespace nihil; + + auto e1 = error("error"); + auto e2 = error("error"); + auto e3 = error("an error"); + + REQUIRE(e1 == e2); + REQUIRE(e1 != e3); +} + +TEST_CASE("error: operator< with strings", "[nihil]") +{ + using namespace nihil; + + auto e1 = error("aaa"); + auto e2 = error("zzz"); + + REQUIRE(e1 < e2); +} + +TEST_CASE("error: operator== with a cause", "[nihil]") +{ + using namespace nihil; + + auto e1 = error("error", error("cause 1")); + auto e2 = error("error", error("cause 2")); + + REQUIRE(e1 == e2); +} + +TEST_CASE("error: operator== with error_conditions", "[nihil]") +{ + using namespace nihil; + + auto e1 = error(std::errc::invalid_argument); + auto e2 = error(std::errc::invalid_argument); + auto e3 = error(std::errc::permission_denied); + + REQUIRE(e1 == e2); + REQUIRE(e1 != e3); +} + +TEST_CASE("error: std::format with string", "[nihil]") +{ + using namespace nihil; + + auto err = error("an error"); + REQUIRE(std::format("{}", err) == "an error"); +} + +TEST_CASE("error: std::format with std::errc", "[nihil]") +{ + using namespace nihil; + + auto err = error(std::errc::invalid_argument); + REQUIRE(std::format("{}", err) == std::strerror(EINVAL)); +} + +TEST_CASE("error: std::format with cause", "[nihil]") +{ + using namespace nihil; + + auto err = error("an error", std::errc::invalid_argument); + REQUIRE(std::format("{}", err) == "an error: Invalid argument"); +} + +TEST_CASE("error: throw and catch", "[nihil]") +{ + using namespace std::literals; + using namespace nihil; + + try { + throw error("oh no", error(std::errc::invalid_argument)); + } catch (std::exception const &exc) { + REQUIRE(exc.what() == "oh no: Invalid argument"s); + } +} diff --git a/nihil.flagset/CMakeLists.txt b/nihil.flagset/CMakeLists.txt new file mode 100644 index 0000000..be9b99b --- /dev/null +++ b/nihil.flagset/CMakeLists.txt @@ -0,0 +1,23 @@ +# This source code is released into the public domain. + +add_library(nihil.flagset STATIC) +target_sources(nihil.flagset + PUBLIC FILE_SET modules TYPE CXX_MODULES FILES + flagset.ccm +) + +if(NIHIL_TESTS) + enable_testing() + + add_executable(nihil.flagset.test test.cc) + target_link_libraries(nihil.flagset.test PRIVATE + nihil.flagset + Catch2::Catch2WithMain + ) + + find_package(Catch2 REQUIRED) + + include(CTest) + include(Catch) + catch_discover_tests(nihil.flagset.test) +endif() diff --git a/nihil.flagset/flagset.ccm b/nihil.flagset/flagset.ccm new file mode 100644 index 0000000..8369b75 --- /dev/null +++ b/nihil.flagset/flagset.ccm @@ -0,0 +1,228 @@ +/* + * This source code is released into the public domain. + */ + +module; + +/* + * flagset: a type-type flags type. + */ + +#include <concepts> +#include <cstdint> +#include <iostream> +#include <limits> +#include <print> + +export module nihil.flagset; + +namespace nihil { + +export template<std::integral base_type, typename Tag> +struct flagset final { + using underlying_type = base_type; + + /* + * Create an empty flags. + */ + flagset() noexcept = default; + + /* + * Copyable. + */ + flagset(flagset const &other) noexcept + : m_value(other.m_value) + {} + + /* + * Create flags from an integer mask. + */ + template<base_type flag> + [[nodiscard]] static constexpr auto mask() noexcept -> flagset + { + return flagset(flag); + } + + /* + * Create flags for a specific bit. + */ + template<unsigned bitnr> + [[nodiscard]] static constexpr auto bit() noexcept -> flagset + { + static_assert(bitnr < std::numeric_limits<base_type>::digits); + return flagset(static_cast<base_type>(1) << bitnr); + } + + /* + * Create flags from a runtime value. + */ + [[nodiscard]] static auto from_int(base_type value) noexcept + -> flagset + { + return flagset(value); + } + + /* + * Assign this flagset. + */ + auto operator=(this flagset &lhs, flagset rhs) noexcept + -> flagset & + { + if (&lhs != &rhs) + lhs.m_value = rhs.m_value; + return lhs; + } + + /* + * The integer value of this flagset. + */ + [[nodiscard]] constexpr auto value(this flagset self) noexcept + -> base_type + { + return self.m_value; + } + + /* + * True if this flagset has any bits set. + */ + [[nodiscard]] explicit constexpr operator bool(this flagset self) + noexcept + { + return self.m_value != 0; + } + + /* + * Set bits. + */ + constexpr auto operator|= (this flagset &lhs, flagset rhs) noexcept + -> flagset & + { + lhs.m_value |= rhs.value(); + return lhs; + } + + /* + * Mask bits. + */ + constexpr auto operator&= (this flagset &lhs, flagset rhs) noexcept + -> flagset & + { + lhs.m_value &= rhs.value(); + return lhs; + } + + /* + * Invert bits. + */ + [[nodiscard]] constexpr auto operator~ (this flagset self) noexcept + -> flagset + { + return flagset(~self.m_value); + } + + /* + * xor bits. + */ + constexpr auto operator^= (this flagset &lhs, flagset rhs) + noexcept -> flagset + { + lhs.m_value ^= rhs.value(); + return lhs; + } + +private: + base_type m_value = 0; + + explicit constexpr flagset(base_type mask) noexcept + : m_value(mask) + {} +}; + +export template<std::integral base_type, typename Tag> +[[nodiscard]] auto operator| (flagset<base_type, Tag> lhs, + flagset<base_type, Tag> rhs) noexcept + -> flagset<base_type, Tag> +{ + return (lhs |= rhs); +} + +export template<std::integral base_type, typename Tag> +[[nodiscard]] auto operator& (flagset<base_type, Tag> lhs, + flagset<base_type, Tag> rhs) noexcept + -> flagset<base_type, Tag> +{ + return (lhs &= rhs); +} + +export template<std::integral base_type, typename Tag> +[[nodiscard]] auto operator^ (flagset<base_type, Tag> lhs, + flagset<base_type, Tag> rhs) noexcept + -> flagset<base_type, Tag> +{ + return (lhs ^= rhs); +} + +export template<std::integral base_type, typename Tag> +[[nodiscard]] auto operator== (flagset<base_type, Tag> lhs, + flagset<base_type, Tag> rhs) noexcept + -> bool +{ + return lhs.value() == rhs.value(); +} + +export template<std::integral base_type, typename Tag> +auto operator<<(std::ostream &strm, flagset<base_type, Tag> flags) + -> std::ostream & +{ + std::print(strm, "{}", flags); + return strm; +} + +} // namespace nihil + +/* + * Formatting for flagset. + */ +export template<std::integral base_type, typename Tag, typename Char> +struct std::formatter<nihil::flagset<base_type, Tag>, Char> +{ + using flags_t = nihil::flagset<base_type, Tag>; + + template<typename ParseContext> + constexpr auto parse(ParseContext &ctx) -> ParseContext::iterator + { + return ctx.begin(); + } + + template<typename FmtContext> + auto format(flags_t flags, FmtContext& ctx) const + -> FmtContext::iterator + { + auto constexpr digits = std::numeric_limits<base_type>::digits; + auto value = flags.value(); + auto it = ctx.out(); + *it++ = Char{'<'}; + + auto printed_any = false; + + for (unsigned i = 0; i < digits; ++i) { + unsigned bit = (digits - 1) - i; + + auto this_bit = static_cast<base_type>(1) << bit; + if ((value & this_bit) == 0) + continue; + + if (printed_any) + *it++ = Char{','}; + + if (bit > 10) + *it++ = Char{'0'} + (bit / 10); + *it++ = Char{'0'} + (bit % 10); + + printed_any = true; + } + + *it++ = Char{'>'}; + return it; + } +}; diff --git a/nihil.flagset/test.cc b/nihil.flagset/test.cc new file mode 100644 index 0000000..c3ebd35 --- /dev/null +++ b/nihil.flagset/test.cc @@ -0,0 +1,149 @@ +/* + * This source code is released into the public domain. + */ + +#include <concepts> +#include <format> +#include <sstream> + +#include <catch2/catch_test_macros.hpp> + +import nihil.flagset; + +namespace { + +struct test_tag{}; +using testflags = nihil::flagset<unsigned, test_tag>; + +constexpr auto zero = testflags::bit<0>(); +constexpr auto one = testflags::bit<1>(); +constexpr auto two = testflags::bit<2>(); +constexpr auto twelve = testflags::bit<12>(); + +} + +TEST_CASE("flagset: invariant", "[nihil]") +{ + static_assert(std::regular<testflags>); + static_assert(sizeof(testflags) == sizeof(testflags::underlying_type)); +} + +TEST_CASE("flagset: bit<>", "[nihil]") +{ + REQUIRE(zero.value() == 0x1); + REQUIRE(one.value() == 0x2); +} + +TEST_CASE("flagset: mask<>", "[nihil]") +{ + auto zero_ = testflags::mask<0x1>(); + auto one_ = testflags::mask<0x2>(); + + REQUIRE(zero_ == zero); + REQUIRE(one_ == one); +} + +TEST_CASE("flagset: constructor", "[nihil]") +{ + SECTION("default construct") { + auto flags = testflags(); + REQUIRE(flags.value() == 0); + } + + SECTION("construct from int") { + auto flags = testflags::from_int(one.value() | zero.value()); + REQUIRE(flags == (one | zero)); + } + + SECTION("copy construct") { + auto flags = one; + auto flags2(flags); + REQUIRE(flags == flags2); + } +} + +TEST_CASE("flagset: operators", "[nihil]") +{ + SECTION("operator|") { + REQUIRE((zero | one).value() == 0x3); + } + + SECTION("operator|=") { + auto flags = zero; + flags |= one; + REQUIRE(flags.value() == 0x3); + } + + SECTION("operator&") { + auto flags = zero | one; + + REQUIRE((flags & zero) == zero); + } + + SECTION("operator&=") { + auto flags = zero | one | two; + REQUIRE(flags.value() == 0x7); + flags &= (zero | one); + REQUIRE(flags.value() == 0x3); + } + + SECTION("operator^") { + auto flags = zero | one; + REQUIRE((flags ^ (one | two)) == (zero | two)); + } + + SECTION("operator^=") { + auto flags = zero | one; + flags ^= (one | two); + REQUIRE(flags == (zero | two)); + } + + SECTION("operator~") { + auto flags = ~zero; + REQUIRE(flags.value() == ~static_cast<unsigned>(1)); + } + + SECTION("operator==") { + auto flags = zero; + REQUIRE(flags == zero); + REQUIRE(flags != one); + } +} + +TEST_CASE("flagset: assignment", "[nihil]") +{ + auto flags = zero; + REQUIRE(flags == zero); + + flags = one; + REQUIRE(flags == one); + REQUIRE(flags != zero); +} + +TEST_CASE("flagset: format", "[nihil]") +{ + REQUIRE(std::format("{}", testflags()) == "<>"); + REQUIRE(std::format("{}", zero) == "<0>"); + REQUIRE(std::format("{}", one) == "<1>"); + REQUIRE(std::format("{}", zero | one) == "<1,0>"); + + REQUIRE(std::format("{}", twelve) == "<12>"); + REQUIRE(std::format("{}", twelve | one) == "<12,1>"); +} + +TEST_CASE("flagset: ostream operator<<", "[nihil]") +{ + auto write = [] (testflags flags) -> std::string { + auto strm = std::ostringstream(); + strm << flags; + return strm.str(); + }; + + REQUIRE(write(testflags()) == "<>"); + REQUIRE(write(zero) == "<0>"); + REQUIRE(write(one) == "<1>"); + REQUIRE(write(zero | one) == "<1,0>"); + + REQUIRE(write(twelve) == "<12>"); + REQUIRE(write(twelve | one) == "<12,1>"); +} diff --git a/nihil.generator/CMakeLists.txt b/nihil.generator/CMakeLists.txt new file mode 100644 index 0000000..56afdac --- /dev/null +++ b/nihil.generator/CMakeLists.txt @@ -0,0 +1,29 @@ +# This source code is released into the public domain. + +add_library(nihil.generator STATIC) +target_sources(nihil.generator + PUBLIC FILE_SET modules TYPE CXX_MODULES FILES + nihil.generator.ccm + + generator.ccm + elements_of.ccm + manual_lifetime.ccm + promise_base_alloc.ccm + util.ccm +) + +if(NIHIL_TESTS) + enable_testing() + + add_executable(nihil.generator.test test.cc) + target_link_libraries(nihil.generator.test PRIVATE + nihil.generator + Catch2::Catch2WithMain + ) + + find_package(Catch2 REQUIRED) + + include(CTest) + include(Catch) + catch_discover_tests(nihil.generator.test) +endif() diff --git a/nihil.generator/elements_of.ccm b/nihil.generator/elements_of.ccm new file mode 100644 index 0000000..0e34eb9 --- /dev/null +++ b/nihil.generator/elements_of.ccm @@ -0,0 +1,69 @@ +/////////////////////////////////////////////////////////////////////////////// +// Reference implementation of std::generator proposal P2168. +// +// See https://wg21.link/P2168 for details. +// +/////////////////////////////////////////////////////////////////////////////// +// Copyright Lewis Baker, Corentin Jabot +// +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. +// (See accompanying file LICENSE or http://www.boost.org/LICENSE_1_0.txt) +/////////////////////////////////////////////////////////////////////////////// + +module; + +#include <concepts> + +export module nihil.generator:elements_of; + +import :util; + +namespace nihil { + +export template <typename Range, typename Allocator = use_allocator_arg> +struct elements_of { + explicit constexpr elements_of(Range &&range) noexcept + requires std::is_default_constructible_v<Allocator> + : m_range(static_cast<Range &&>(range)) + { + } + + constexpr elements_of(Range &&range, Allocator &&alloc) noexcept + : m_range(static_cast<Range &&>(range)) + , m_alloc(static_cast<Allocator &&>(alloc)) + {} + + constexpr elements_of(elements_of &&) noexcept = default; + + constexpr elements_of(const elements_of &) = delete; + + constexpr auto operator=(this elements_of &, const elements_of &) + -> elements_of & = delete; + constexpr auto operator=(this elements_of &, elements_of &&) noexcept + -> elements_of & = delete; + + [[nodiscard]] constexpr auto + get(this elements_of const &self) noexcept -> Range && + { + return static_cast<Range &&>(self.m_range); + } + + [[nodiscard]] constexpr auto + get_allocator(this elements_of const &self) noexcept -> Allocator + { + return self.m_alloc; + } + +private: + [[no_unique_address]] Allocator m_alloc; + Range &&m_range; +}; + +export template <typename Range> +elements_of(Range &&) -> elements_of<Range>; + +export template <typename Range, typename Allocator> +elements_of(Range &&, Allocator &&) -> elements_of<Range, Allocator>; + +} // namespace nihil diff --git a/nihil.generator/generator.ccm b/nihil.generator/generator.ccm new file mode 100644 index 0000000..27e8103 --- /dev/null +++ b/nihil.generator/generator.ccm @@ -0,0 +1,507 @@ +/////////////////////////////////////////////////////////////////////////////// +// Reference implementation of std::generator proposal P2168. +// +// See https://wg21.link/P2168 for details. +// +/////////////////////////////////////////////////////////////////////////////// +// Copyright Lewis Baker, Corentin Jabot +// +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. +// (See accompanying file LICENSE or http://www.boost.org/LICENSE_1_0.txt) +/////////////////////////////////////////////////////////////////////////////// + +module; + +#include <cassert> +#include <coroutine> +#include <exception> +#include <memory> +#include <ranges> +#include <type_traits> +#include <utility> + +export module nihil.generator:generator; + +import :elements_of; +import :manual_lifetime; +import :promise_base_alloc; +import :util; + +namespace nihil { + +export template <typename _Ref, + typename _Value = std::remove_cvref_t<_Ref>, + typename _Allocator = use_allocator_arg> +class generator; + + +template<typename _Ref> +struct __generator_promise_base +{ + template <typename _Ref2, typename _Value, typename _Alloc> + friend class generator; + + __generator_promise_base* __root_; + std::coroutine_handle<> __parentOrLeaf_; + // Note: Using manual_lifetime here to avoid extra calls to exception_ptr + // constructor/destructor in cases where it is not needed (i.e. where this + // generator coroutine is not used as a nested coroutine). + // This member is lazily constructed by the __yield_sequence_awaiter::await_suspend() + // method if this generator is used as a nested generator. + manual_lifetime<std::exception_ptr> __exception_; + manual_lifetime<_Ref> __value_; + + explicit __generator_promise_base(std::coroutine_handle<> thisCoro) noexcept + : __root_(this) + , __parentOrLeaf_(thisCoro) + {} + + ~__generator_promise_base() { + if (__root_ != this) { + // This coroutine was used as a nested generator and so will + // have constructed its __exception_ member which needs to be + // destroyed here. + __exception_.destruct(); + } + } + + std::suspend_always initial_suspend() noexcept { + return {}; + } + + void return_void() noexcept {} + + void unhandled_exception() { + if (__root_ != this) { + __exception_.get() = std::current_exception(); + } else { + throw; + } + } + + // Transfers control back to the parent of a nested coroutine + struct __final_awaiter { + bool await_ready() noexcept { + return false; + } + + template <typename _Promise> + std::coroutine_handle<> + await_suspend(std::coroutine_handle<_Promise> __h) noexcept { + _Promise& __promise = __h.promise(); + __generator_promise_base& __root = *__promise.__root_; + if (&__root != &__promise) { + auto __parent = __promise.__parentOrLeaf_; + __root.__parentOrLeaf_ = __parent; + return __parent; + } + return std::noop_coroutine(); + } + + void await_resume() noexcept {} + }; + + __final_awaiter final_suspend() noexcept { + return {}; + } + + std::suspend_always yield_value(_Ref&& __x) + noexcept(std::is_nothrow_move_constructible_v<_Ref>) { + __root_->__value_.construct((_Ref&&)__x); + return {}; + } + + template <typename _T> + requires + (!std::is_reference_v<_Ref>) && + std::is_convertible_v<_T, _Ref> + std::suspend_always yield_value(_T&& __x) + noexcept(std::is_nothrow_constructible_v<_Ref, _T>) { + __root_->__value_.construct((_T&&)__x); + return {}; + } + + template <typename _Gen> + struct __yield_sequence_awaiter { + _Gen __gen_; + + __yield_sequence_awaiter(_Gen&& __g) noexcept + // Taking ownership of the generator ensures frame are destroyed + // in the reverse order of their execution. + : __gen_((_Gen&&)__g) { + } + + bool await_ready() noexcept { + return false; + } + + // set the parent, root and exceptions pointer and + // resume the nested + template<typename _Promise> + std::coroutine_handle<> + await_suspend(std::coroutine_handle<_Promise> __h) noexcept { + __generator_promise_base& __current = __h.promise(); + __generator_promise_base& __nested = *__gen_.__get_promise(); + __generator_promise_base& __root = *__current.__root_; + + __nested.__root_ = __current.__root_; + __nested.__parentOrLeaf_ = __h; + + // Lazily construct the __exception_ member here now that we + // know it will be used as a nested generator. This will be + // destroyed by the promise destructor. + __nested.__exception_.construct(); + __root.__parentOrLeaf_ = __gen_.__get_coro(); + + // Immediately resume the nested coroutine (nested generator) + return __gen_.__get_coro(); + } + + void await_resume() { + __generator_promise_base& __nestedPromise = *__gen_.__get_promise(); + if (__nestedPromise.__exception_.get()) { + std::rethrow_exception(std::move(__nestedPromise.__exception_.get())); + } + } + }; + + template <typename _OValue, typename _OAlloc> + __yield_sequence_awaiter<generator<_Ref, _OValue, _OAlloc>> + yield_value(nihil::elements_of<generator<_Ref, _OValue, _OAlloc>> __g) noexcept { + return std::move(__g).get(); + } + + template <std::ranges::range _Rng, typename _Allocator> + __yield_sequence_awaiter<generator<_Ref, std::remove_cvref_t<_Ref>, _Allocator>> + yield_value(nihil::elements_of<_Rng, _Allocator> && __x) { + return [](std::allocator_arg_t, _Allocator, auto && __rng) -> generator<_Ref, std::remove_cvref_t<_Ref>, _Allocator> { + for(auto && e: __rng) + co_yield static_cast<decltype(e)>(e); + }(std::allocator_arg, __x.get_allocator(), std::forward<_Rng>(__x.get())); + } + + void resume() { + __parentOrLeaf_.resume(); + } + + // Disable use of co_await within this coroutine. + void await_transform() = delete; +}; + +template<typename _Generator, typename _ByteAllocator, bool _ExplicitAllocator = false> +struct __generator_promise; + +template<typename _Ref, typename _Value, typename _Alloc, typename _ByteAllocator, bool _ExplicitAllocator> +struct __generator_promise<generator<_Ref, _Value, _Alloc>, _ByteAllocator, _ExplicitAllocator> final + : public __generator_promise_base<_Ref> + , public promise_base_alloc<_ByteAllocator> { + __generator_promise() noexcept + : __generator_promise_base<_Ref>(std::coroutine_handle<__generator_promise>::from_promise(*this)) + {} + + generator<_Ref, _Value, _Alloc> get_return_object() noexcept { + return generator<_Ref, _Value, _Alloc>{ + std::coroutine_handle<__generator_promise>::from_promise(*this) + }; + } + + using __generator_promise_base<_Ref>::yield_value; + + template <std::ranges::range _Rng> + typename __generator_promise_base<_Ref>::template __yield_sequence_awaiter<generator<_Ref, _Value, _Alloc>> + yield_value(nihil::elements_of<_Rng> && __x) { + static_assert (!_ExplicitAllocator, + "This coroutine has an explicit allocator specified with std::allocator_arg so an allocator needs to be passed " + "explicitely to std::elements_of"); + return [](auto && __rng) -> generator<_Ref, _Value, _Alloc> { + for(auto && e: __rng) + co_yield static_cast<decltype(e)>(e); + }(std::forward<_Rng>(__x.get())); + } +}; + +template<typename _Alloc> +using __byte_allocator_t = typename std::allocator_traits<std::remove_cvref_t<_Alloc>>::template rebind_alloc<std::byte>; + +} // namespace nihil + +namespace std { + +// Type-erased allocator with default allocator behaviour. +export template<typename _Ref, typename _Value, typename... _Args> +struct coroutine_traits<nihil::generator<_Ref, _Value>, _Args...> { + using promise_type = nihil::__generator_promise<nihil::generator<_Ref, _Value>, std::allocator<std::byte>>; +}; + +// Type-erased allocator with std::allocator_arg parameter +export template<typename _Ref, typename _Value, typename _Alloc, typename... _Args> +struct coroutine_traits<nihil::generator<_Ref, _Value>, allocator_arg_t, _Alloc, _Args...> { +private: + using __byte_allocator = nihil::__byte_allocator_t<_Alloc>; +public: + using promise_type = nihil::__generator_promise<nihil::generator<_Ref, _Value>, __byte_allocator, true /*explicit Allocator*/>; +}; + +// Type-erased allocator with std::allocator_arg parameter (non-static member functions) +export template<typename _Ref, typename _Value, typename _This, typename _Alloc, typename... _Args> +struct coroutine_traits<nihil::generator<_Ref, _Value>, _This, allocator_arg_t, _Alloc, _Args...> { +private: + using __byte_allocator = nihil::__byte_allocator_t<_Alloc>; +public: + using promise_type = nihil::__generator_promise<nihil::generator<_Ref, _Value>, __byte_allocator, true /*explicit Allocator*/>; +}; + +// Generator with specified allocator type +export template<typename _Ref, typename _Value, typename _Alloc, typename... _Args> +struct coroutine_traits<nihil::generator<_Ref, _Value, _Alloc>, _Args...> { + using __byte_allocator = nihil::__byte_allocator_t<_Alloc>; +public: + using promise_type = nihil::__generator_promise<nihil::generator<_Ref, _Value, _Alloc>, __byte_allocator>; +}; + +} // namespace std + +namespace nihil { + +// TODO : make layout compatible promise casts possible +export template <typename _Ref, typename _Value, typename _Alloc> +class generator { + using __byte_allocator = __byte_allocator_t<_Alloc>; +public: + using promise_type = __generator_promise<generator<_Ref, _Value, _Alloc>, __byte_allocator>; + friend promise_type; +private: + using __coroutine_handle = std::coroutine_handle<promise_type>; +public: + + generator() noexcept = default; + + generator(generator&& __other) noexcept + : __coro_(std::exchange(__other.__coro_, {})) + , __started_(std::exchange(__other.__started_, false)) { + } + + ~generator() noexcept { + if (__coro_) { + if (__started_ && !__coro_.done()) { + __coro_.promise().__value_.destruct(); + } + __coro_.destroy(); + } + } + + generator& operator=(generator && g) noexcept { + swap(g); + return *this; + } + + void swap(generator& __other) noexcept { + std::swap(__coro_, __other.__coro_); + std::swap(__started_, __other.__started_); + } + + struct sentinel {}; + + class iterator { + public: + using iterator_category = std::input_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = _Value; + using reference = _Ref; + using pointer = std::add_pointer_t<_Ref>; + + iterator() noexcept = default; + iterator(const iterator &) = delete; + + iterator(iterator&& __other) noexcept + : __coro_(std::exchange(__other.__coro_, {})) { + } + + iterator& operator=(iterator&& __other) { + std::swap(__coro_, __other.__coro_); + return *this; + } + + ~iterator() { + } + + friend bool operator==(const iterator &it, sentinel) noexcept { + return it.__coro_.done(); + } + + iterator &operator++() { + __coro_.promise().__value_.destruct(); + __coro_.promise().resume(); + return *this; + } + void operator++(int) { + (void)operator++(); + } + + reference operator*() const noexcept { + return static_cast<reference>(__coro_.promise().__value_.get()); + } + + private: + friend generator; + + explicit iterator(__coroutine_handle __coro) noexcept + : __coro_(__coro) {} + + __coroutine_handle __coro_; + }; + + iterator begin() { + assert(__coro_); + assert(!__started_); + __started_ = true; + __coro_.resume(); + return iterator{__coro_}; + } + + sentinel end() noexcept { + return {}; + } + +private: + explicit generator(__coroutine_handle __coro) noexcept + : __coro_(__coro) { + } + +public: // to get around access restrictions for __yield_sequence_awaitable + std::coroutine_handle<> __get_coro() noexcept { return __coro_; } + promise_type* __get_promise() noexcept { return std::addressof(__coro_.promise()); } + +private: + __coroutine_handle __coro_; + bool __started_ = false; +}; + +// Specialisation for type-erased allocator implementation. +export template <typename _Ref, typename _Value> +class generator<_Ref, _Value, use_allocator_arg> { + using __promise_base = __generator_promise_base<_Ref>; +public: + + generator() noexcept + : __promise_(nullptr) + , __coro_() + , __started_(false) + {} + + generator(generator&& __other) noexcept + : __promise_(std::exchange(__other.__promise_, nullptr)) + , __coro_(std::exchange(__other.__coro_, {})) + , __started_(std::exchange(__other.__started_, false)) { + } + + ~generator() noexcept { + if (__coro_) { + if (__started_ && !__coro_.done()) { + __promise_->__value_.destruct(); + } + __coro_.destroy(); + } + } + + generator& operator=(generator g) noexcept { + swap(g); + return *this; + } + + void swap(generator& __other) noexcept { + std::swap(__promise_, __other.__promise_); + std::swap(__coro_, __other.__coro_); + std::swap(__started_, __other.__started_); + } + + struct sentinel {}; + + class iterator { + public: + using iterator_category = std::input_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = _Value; + using reference = _Ref; + using pointer = std::add_pointer_t<_Ref>; + + iterator() noexcept = default; + iterator(const iterator &) = delete; + + iterator(iterator&& __other) noexcept + : __promise_(std::exchange(__other.__promise_, nullptr)) + , __coro_(std::exchange(__other.__coro_, {})) + {} + + iterator& operator=(iterator&& __other) { + __promise_ = std::exchange(__other.__promise_, nullptr); + __coro_ = std::exchange(__other.__coro_, {}); + return *this; + } + + ~iterator() = default; + + friend bool operator==(const iterator &it, sentinel) noexcept { + return it.__coro_.done(); + } + + iterator& operator++() { + __promise_->__value_.destruct(); + __promise_->resume(); + return *this; + } + + void operator++(int) { + (void)operator++(); + } + + reference operator*() const noexcept { + return static_cast<reference>(__promise_->__value_.get()); + } + + private: + friend generator; + + explicit iterator(__promise_base* __promise, std::coroutine_handle<> __coro) noexcept + : __promise_(__promise) + , __coro_(__coro) + {} + + __promise_base* __promise_; + std::coroutine_handle<> __coro_; + }; + + iterator begin() { + assert(__coro_); + assert(!__started_); + __started_ = true; + __coro_.resume(); + return iterator{__promise_, __coro_}; + } + + sentinel end() noexcept { + return {}; + } + +private: + template<typename _Generator, typename _ByteAllocator, bool _ExplicitAllocator> + friend struct __generator_promise; + + template<typename _Promise> + explicit generator(std::coroutine_handle<_Promise> __coro) noexcept + : __promise_(std::addressof(__coro.promise())) + , __coro_(__coro) + {} + +public: // to get around access restrictions for __yield_sequence_awaitable + std::coroutine_handle<> __get_coro() noexcept { return __coro_; } + __promise_base* __get_promise() noexcept { return __promise_; } + +private: + __promise_base* __promise_; + std::coroutine_handle<> __coro_; + bool __started_ = false; +}; + +} // namespace nihil diff --git a/nihil.generator/manual_lifetime.ccm b/nihil.generator/manual_lifetime.ccm new file mode 100644 index 0000000..d249e99 --- /dev/null +++ b/nihil.generator/manual_lifetime.ccm @@ -0,0 +1,117 @@ +/////////////////////////////////////////////////////////////////////////////// +// Reference implementation of std::generator proposal P2168. +// +// See https://wg21.link/P2168 for details. +// +/////////////////////////////////////////////////////////////////////////////// +// Copyright Lewis Baker, Corentin Jabot +// +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. +// (See accompanying file LICENSE or http://www.boost.org/LICENSE_1_0.txt) +/////////////////////////////////////////////////////////////////////////////// + +module; + +#include <concepts> +#include <memory> + +export module nihil.generator:manual_lifetime; + +namespace nihil { + +template <typename T> +struct manual_lifetime { + manual_lifetime() noexcept {} + ~manual_lifetime() {} + + template <typename ...Args> + auto construct(this manual_lifetime &self, Args && ...args) + noexcept(std::is_nothrow_constructible_v<T, Args...>) + -> T & + { + return *::new (static_cast<void*>(std::addressof(self.m_value))) + T(static_cast<Args &&>(args)...); + } + + void destruct(this manual_lifetime &self) + noexcept(std::is_nothrow_destructible_v<T>) + { + self.m_value.~T(); + } + + auto get(this manual_lifetime &self) noexcept -> T & + { + return self.m_value; + } + + auto get(this manual_lifetime &&self) noexcept -> T && + { + return static_cast<T&&>(self.m_value); + } + + auto get(this manual_lifetime const &self) noexcept -> T const & + { + return self.m_value; + } + + auto get(this manual_lifetime const &&self) noexcept -> T const && + { + return static_cast<T const &&>(self.m_value); + } + +private: + union { + std::remove_const_t<T> m_value; + }; +}; + +template <typename T> +class manual_lifetime<T &> { + manual_lifetime() noexcept {} + ~manual_lifetime() {} + + auto construct(this manual_lifetime &self, T &value) noexcept -> T & + { + self.m_value = std::addressof(value); + return self.m_value; + } + + auto destruct(this manual_lifetime &) noexcept -> void + { + } + + auto get(this manual_lifetime const &self) noexcept -> T & + { + return *self.m_value; + } + +private: + T *m_value = nullptr; +}; + +template <typename T> +class manual_lifetime<T &&> { + manual_lifetime() noexcept {} + ~manual_lifetime() {} + + auto construct(this manual_lifetime &self, T &&value) noexcept -> T && + { + self.m_value = std::addressof(value); + return static_cast<T &&>(value); + } + + void destruct(this manual_lifetime &) noexcept + { + } + + auto get(this manual_lifetime const &self) noexcept -> T && + { + return static_cast<T &&>(*self.m_value); + } + +private: + T* m_value = nullptr; +}; + +} diff --git a/nihil.generator/nihil.generator.ccm b/nihil.generator/nihil.generator.ccm new file mode 100644 index 0000000..9eec5b4 --- /dev/null +++ b/nihil.generator/nihil.generator.ccm @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////// +// Reference implementation of std::generator proposal P2168. +// +// See https://wg21.link/P2168 for details. +// +/////////////////////////////////////////////////////////////////////////////// +// Copyright Lewis Baker, Corentin Jabot +// +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. +// (See accompanying file LICENSE or http://www.boost.org/LICENSE_1_0.txt) +/////////////////////////////////////////////////////////////////////////////// + +module; + +#include <ranges> + +export module nihil.generator; + +export import :elements_of; +export import :generator; +export import :manual_lifetime; +export import :promise_base_alloc; +export import :util; + +export namespace std::ranges { + +template <typename T, typename U, typename Alloc> +constexpr inline bool enable_view<nihil::generator<T, U, Alloc>> = true; + +} // namespace std::ranges + diff --git a/nihil.generator/promise_base_alloc.ccm b/nihil.generator/promise_base_alloc.ccm new file mode 100644 index 0000000..e59fc57 --- /dev/null +++ b/nihil.generator/promise_base_alloc.ccm @@ -0,0 +1,94 @@ +/////////////////////////////////////////////////////////////////////////////// +// Reference implementation of std::generator proposal P2168. +// +// See https://wg21.link/P2168 for details. +// +/////////////////////////////////////////////////////////////////////////////// +// Copyright Lewis Baker, Corentin Jabot +// +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. +// (See accompanying file LICENSE or http://www.boost.org/LICENSE_1_0.txt) +/////////////////////////////////////////////////////////////////////////////// + +module; + +#include <cstdlib> +#include <memory> + +export module nihil.generator:promise_base_alloc; + +import :util; + +namespace nihil { + +template<typename Alloc> +struct promise_base_alloc +{ + template<typename... Args> + static void* operator new(std::size_t frame_size, std::allocator_arg_t, Alloc alloc, Args &...) + { + void* frame = alloc.allocate(padded_frame_size(frame_size)); + + // Store allocator at end of the coroutine frame. Assuming the + // allocator's move constructor is non-throwing (a requirement + // for allocators) + auto *alloc_address = static_cast<void*>(std::addressof(get_allocator(frame, frame_size))); + ::new (alloc_address) Alloc(std::move(alloc)); + + return frame; + } + + template<typename This, typename... Args> + static void* operator new(std::size_t frame_size, This &, std::allocator_arg_t, Alloc alloc, Args &...) + { + return promise_base_alloc::operator new(frame_size, std::allocator_arg, std::move(alloc)); + } + + static void operator delete(void* ptr, std::size_t frame_size) noexcept + { + auto &alloc = get_allocator(ptr, frame_size); + auto local_alloc = Alloc(std::move(alloc)); + + alloc.~Alloc(); + + local_alloc.deallocate(static_cast<std::byte*>(ptr), padded_frame_size(frame_size)); + } + +private: + [[nodiscard]] static constexpr auto offset_of_allocator(std::size_t frame_size) noexcept -> std::size_t + { + return aligned_allocation_size(frame_size, alignof(Alloc)); + } + + [[nodiscard]] static constexpr auto padded_frame_size(std::size_t frame_size) noexcept -> std::size_t + { + return offset_of_allocator(frame_size) + sizeof(Alloc); + } + + [[nodiscard]] static auto get_allocator(void* frame, std::size_t frame_size) noexcept -> Alloc & + { + return *reinterpret_cast<Alloc*>( + static_cast<char*>(frame) + offset_of_allocator(frame_size)); + } + +}; + +template<typename Alloc> +requires (!allocator_needs_to_be_stored<Alloc>) +struct promise_base_alloc<Alloc> +{ + static void* operator new(std::size_t size) + { + auto alloc = Alloc(); + return alloc.allocate(size); + } + + static void operator delete(void *ptr, std::size_t size) noexcept + { + auto alloc = Alloc(); + alloc.deallocate(static_cast<std::byte *>(ptr), size); + } +}; + +} // namespace nihil diff --git a/nihil.generator/test.cc b/nihil.generator/test.cc new file mode 100644 index 0000000..49272b4 --- /dev/null +++ b/nihil.generator/test.cc @@ -0,0 +1,56 @@ +/* + * This source code is released into the public domain. + */ + +#include <coroutine> +#include <ranges> +#include <vector> + +#include <catch2/catch_test_macros.hpp> + +import nihil.generator; + +TEST_CASE("generator: basic", "[generator]") +{ + auto fn = [] () -> nihil::generator<int> { + co_yield 1; + co_yield 2; + co_yield 3; + }; + + auto values = std::vector<int>(); + std::ranges::copy(fn(), std::back_inserter(values)); + + REQUIRE(values == std::vector{1, 2, 3}); +} + +TEST_CASE("generator: exceptions", "[generator]") +{ + auto fn = [] () -> nihil::generator<int> { + co_yield 1; + throw std::runtime_error("test"); + }; + + auto range = fn(); + auto it = std::ranges::begin(range); + REQUIRE(*it == 1); + REQUIRE_THROWS_AS(it++, std::runtime_error); +} + +TEST_CASE("generator: elements_of", "[generator]") +{ + auto fn1 = [] -> nihil::generator<int> { + co_yield 1; + co_yield 2; + co_yield 3; + }; + + auto fn2 = [&fn1] -> nihil::generator<int> { + co_yield nihil::elements_of(fn1()); + }; + + auto values = std::vector<int>(); + std::ranges::copy(fn2(), std::back_inserter(values)); + + REQUIRE(values == std::vector{1, 2, 3}); +} diff --git a/nihil.generator/util.ccm b/nihil.generator/util.ccm new file mode 100644 index 0000000..4d732b9 --- /dev/null +++ b/nihil.generator/util.ccm @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////// +// Reference implementation of std::generator proposal P2168. +// +// See https://wg21.link/P2168 for details. +// +/////////////////////////////////////////////////////////////////////////////// +// Copyright Lewis Baker, Corentin Jabot +// +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. +// (See accompanying file LICENSE or http://www.boost.org/LICENSE_1_0.txt) +/////////////////////////////////////////////////////////////////////////////// + +module; + +#include <concepts> +#include <memory> + +export module nihil.generator:util; + +namespace nihil { + +export struct use_allocator_arg {}; + +template <typename Alloc> +constexpr bool allocator_needs_to_be_stored = + !std::allocator_traits<Alloc>::is_always_equal::value || + !std::is_default_constructible_v<Alloc>; + +// Round s up to next multiple of a. +[[nodiscard]] constexpr auto +aligned_allocation_size(std::size_t s, std::size_t a) -> std::size_t +{ + return (s + a - 1) & ~(a - 1); +} + +} // namespace nihil diff --git a/nihil.guard/CMakeLists.txt b/nihil.guard/CMakeLists.txt new file mode 100644 index 0000000..bba4284 --- /dev/null +++ b/nihil.guard/CMakeLists.txt @@ -0,0 +1,23 @@ +# This source code is released into the public domain. + +add_library(nihil.guard STATIC) +target_sources(nihil.guard + PUBLIC FILE_SET modules TYPE CXX_MODULES FILES + guard.ccm +) + +if(NIHIL_TESTS) + enable_testing() + + add_executable(nihil.guard.test test.cc) + target_link_libraries(nihil.guard.test PRIVATE + nihil.guard + Catch2::Catch2WithMain + ) + + find_package(Catch2 REQUIRED) + + include(CTest) + include(Catch) + catch_discover_tests(nihil.guard.test) +endif() diff --git a/nihil.guard/guard.ccm b/nihil.guard/guard.ccm new file mode 100644 index 0000000..7b6cf66 --- /dev/null +++ b/nihil.guard/guard.ccm @@ -0,0 +1,53 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <concepts> +#include <functional> +#include <optional> +#include <utility> + +export module nihil.guard; + +namespace nihil { + +/* + * guard: invoke a callable when this object is destroyed; this is similar to + * scope_exit from the library fundamentals TS, which LLVM doesn't implement. + */ +export template<std::invocable F> +struct guard final { + // Initialise the guard with a callable we will invoke later. + guard(F func) : m_func(std::move(func)) {} + + /* + * We are being destroyed, so call the callable. + * If the callable throws, std::terminate() will be called. + */ + ~guard() + { + if (m_func) + std::invoke(*m_func); + } + + // Release the guard. This turns the destructor into a no-op. + auto release(this guard &self) noexcept -> void + { + self.m_func.reset(); + } + + // Not default-constructible, movable or copyable. + guard() = delete; + guard(guard const &) = delete; + guard(guard &&) noexcept = delete; + auto operator=(this guard &, guard const &) -> guard & = delete; + auto operator=(this guard &, guard &&) noexcept -> guard & = delete; + +private: + // The callable to be invoked when we are destroyed. + std::optional<F> m_func; +}; + +} // namespace nihil diff --git a/nihil.guard/test.cc b/nihil.guard/test.cc new file mode 100644 index 0000000..11f7d37 --- /dev/null +++ b/nihil.guard/test.cc @@ -0,0 +1,20 @@ +/* + * This source code is released into the public domain. + */ + +#include <catch2/catch_test_macros.hpp> + +import nihil.guard; + +using namespace std::literals; + +TEST_CASE("guard: basic", "[guard]") { + int n = 0; + + { + auto guard = nihil::guard([&] { n = 1; }); + REQUIRE(n == 0); + } + + REQUIRE(n == 1); +} diff --git a/nihil.match/CMakeLists.txt b/nihil.match/CMakeLists.txt new file mode 100644 index 0000000..d7c5875 --- /dev/null +++ b/nihil.match/CMakeLists.txt @@ -0,0 +1,23 @@ +# This source code is released into the public domain. + +add_library(nihil.match STATIC) +target_sources(nihil.match + PUBLIC FILE_SET modules TYPE CXX_MODULES FILES + match.ccm +) + +if(NIHIL_TESTS) + enable_testing() + + add_executable(nihil.match.test test.cc) + target_link_libraries(nihil.match.test PRIVATE + nihil.match + Catch2::Catch2WithMain + ) + + find_package(Catch2 REQUIRED) + + include(CTest) + include(Catch) + catch_discover_tests(nihil.match.test) +endif() diff --git a/nihil.match/match.ccm b/nihil.match/match.ccm new file mode 100644 index 0000000..d67bd0b --- /dev/null +++ b/nihil.match/match.ccm @@ -0,0 +1,23 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <variant> + +export module nihil.match; + +namespace nihil { + +export template<class... Ts> +struct match : Ts... { using Ts::operator()...; }; + +export template<typename... Ts, typename... Fs> +[[nodiscard]] constexpr decltype(auto) operator| + (std::variant<Ts...> const &v, match<Fs...> const &match) +{ + return std::visit(match, v); +} + +} // namespace nihil diff --git a/nihil.match/test.cc b/nihil.match/test.cc new file mode 100644 index 0000000..7dd1c34 --- /dev/null +++ b/nihil.match/test.cc @@ -0,0 +1,34 @@ +/* + * This source code is released into the public domain. + */ + +#include <string> +#include <variant> + +#include <catch2/catch_test_macros.hpp> + +import nihil.match; + +TEST_CASE("match", "[nihil]") +{ + using namespace nihil; + using namespace std::literals; + + auto v = std::variant<int, std::string>(42); + + auto s = v | match { + [](int) { return "int"s; }, + [](std::string const &) { return "string"s; } + }; + + REQUIRE(s == "int"); + + v = "test"s; + + s = v | match { + [](int) { return "int"s; }, + [](std::string const &) { return "string"s; } + }; + + REQUIRE(s == "string"); +} diff --git a/nihil.monad/CMakeLists.txt b/nihil.monad/CMakeLists.txt new file mode 100644 index 0000000..b0fa095 --- /dev/null +++ b/nihil.monad/CMakeLists.txt @@ -0,0 +1,24 @@ +# This source code is released into the public domain. + +add_library(nihil.monad STATIC) +target_link_libraries(nihil.monad PRIVATE nihil.error) +target_sources(nihil.monad + PUBLIC FILE_SET modules TYPE CXX_MODULES FILES + monad.ccm +) + +if(NIHIL_TESTS) + enable_testing() + + add_executable(nihil.monad.test test.cc) + target_link_libraries(nihil.monad.test PRIVATE + nihil.monad + Catch2::Catch2WithMain + ) + + find_package(Catch2 REQUIRED) + + include(CTest) + include(Catch) + catch_discover_tests(nihil.monad.test) +endif() diff --git a/nihil.monad/monad.ccm b/nihil.monad/monad.ccm new file mode 100644 index 0000000..b462e4c --- /dev/null +++ b/nihil.monad/monad.ccm @@ -0,0 +1,289 @@ +/* + * From https://github.com/toby-allsopp/coroutine_monad + * + * Copyright (c) 2017 Toby Allsopp + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +module; + +#include <coroutine> +#include <exception> +#include <expected> +#include <optional> +#include <utility> + +export module nihil.monad; + +namespace nihil { + +/********************************************************************** + * return_object_holder + */ + +// An object that starts out unitialized. Initialized by a call to emplace. +template <typename T> +using deferred = std::optional<T>; + +template <typename T> +struct return_object_holder { + // The staging object that is returned (by copy/move) to the caller of + // the coroutine. + deferred<T> stage; + return_object_holder*& p; + + // When constructed, we assign a pointer to ourselves to the supplied + // reference to pointer. + return_object_holder(return_object_holder*& p) + : stage{} + , p(p) + { + p = this; + } + + // Copying doesn't make any sense (which copy should the pointer refer + // to?). + return_object_holder(return_object_holder const&) = delete; + + // To move, we just update the pointer to point at the new object. + return_object_holder(return_object_holder&& other) + : stage(std::move(other.stage)) + , p(other.p) + { + p = this; + } + + // Assignment doesn't make sense. + void operator=(return_object_holder const&) = delete; + void operator=(return_object_holder&&) = delete; + + // A non-trivial destructor is required until + // https://bugs.llvm.org//show_bug.cgi?id=28593 is fixed. + ~return_object_holder() {} + + // Construct the staging value; arguments are perfect forwarded to T's + // constructor. + template <typename... Args> + void emplace(Args&&... args) + { + stage.emplace(std::forward<Args>(args)...); + } + + // We assume that we will be converted only once, so we can move from + // the staging object. We also assume that `emplace` has been called + // at least once. + operator T() + { + return std::move(*stage); + } +}; + +template <typename T> +auto make_return_object_holder(return_object_holder<T>*& p) +{ + return return_object_holder<T>{p}; +} + +/********************************************************************** + * std::optional + */ + +template <typename T> +struct optional_promise { + return_object_holder<std::optional<T>>* data; + + auto get_return_object() + { + return make_return_object_holder(data); + } + + auto initial_suspend() noexcept -> std::suspend_never + { + return {}; + } + + auto final_suspend() noexcept -> std::suspend_never + { + return {}; + } + + void return_value(T x) + { + data->emplace(std::move(x)); + } + + void unhandled_exception() + { + std::rethrow_exception(std::current_exception()); + } +}; + +} // namespace nihil + +export template <typename T, typename... Args> +struct std::coroutine_traits<std::optional<T>, Args...> { + using promise_type = nihil::optional_promise<T>; +}; + +namespace nihil { + +template <typename T> +struct optional_awaitable { + std::optional<T> o; + + auto await_ready() + { + return o.has_value(); + } + + auto await_resume() + { + return *o; + } + + template <typename U> + void await_suspend(std::coroutine_handle<optional_promise<U>> h) + { + h.promise().data->emplace(std::nullopt); + h.destroy(); + } +}; + +} // namespace nihil + +namespace std { + +export template <typename T> +auto operator co_await(std::optional<T> o) { + return nihil::optional_awaitable<T>{std::move(o)}; +} + +} // namespace std + +/********************************************************************** + * std::expected + */ + +namespace nihil { + +export template <typename T, typename E> +struct expected_promise_base { + return_object_holder<std::expected<T, E>>* data; + + auto get_return_object() + { + return make_return_object_holder(data); + } + + auto initial_suspend() noexcept -> std::suspend_never + { + return {}; + } + + auto final_suspend() noexcept -> std::suspend_never + { + return {}; + } + + void unhandled_exception() + { + std::rethrow_exception(std::current_exception()); + } +}; + +export template <typename T, typename E> +struct expected_promise : expected_promise_base<T, E> { + void return_value(this expected_promise &self, std::unexpected<E> err) + { + self.data->emplace(std::move(err)); + } + + void return_value(this expected_promise &self, T o) + { + self.data->emplace(std::move(o)); + } +}; + +export template <typename E> +struct expected_promise<void, E> : expected_promise_base<void, E> { + void return_value(this expected_promise &self, std::unexpected<E> err) + { + self.data->emplace(std::move(err)); + } + + void return_value(this expected_promise &self, + std::expected<void, E> o) + { + self.data->emplace(std::move(o)); + } +}; + +} // namespace nihil + +export template <typename T, typename E, typename... Args> +struct std::coroutine_traits<std::expected<T, E>, Args...> { + using promise_type = nihil::expected_promise<T, E>; +}; + +namespace nihil { + +export template<typename T, typename E> +struct expected_awaitable_base { + std::expected<T, E> o; + + auto await_ready() + { + return o.has_value(); + } + + template <typename P> + void await_suspend(std::coroutine_handle<P> h) + { + h.promise().data->emplace(std::unexpected(o.error())); + h.destroy(); + } +}; + +export template <typename T, typename E> +struct expected_awaitable : expected_awaitable_base<T, E> { + auto await_resume(this expected_awaitable &self) + { + return std::move(*self.o); + } +}; + +export template <typename E> +struct expected_awaitable<void, E> : expected_awaitable_base<void, E> { + auto await_resume(this expected_awaitable &) + { + return std::expected<void, E>(); + } +}; + +} // namespace nihil + +namespace std { + +export template <typename T, typename E> +auto operator co_await(std::expected<T, E> o) { + return nihil::expected_awaitable<T, E>{std::move(o)}; +} + +} // namespace std diff --git a/nihil.monad/test.cc b/nihil.monad/test.cc new file mode 100644 index 0000000..347acdb --- /dev/null +++ b/nihil.monad/test.cc @@ -0,0 +1,69 @@ +/* + * This source code is released into the public domain. + */ + +#include <coroutine> +#include <expected> +#include <optional> + +#include <catch2/catch_test_macros.hpp> + +import nihil.error; +import nihil.monad; + +TEST_CASE("monad: co_await std::optional<> with value", "[nihil]") +{ + auto get_value = [] -> std::optional<int> { + return 42; + }; + + auto try_get_value = [&get_value] -> std::optional<int> { + co_return co_await get_value(); + }; + + auto o = try_get_value(); + REQUIRE(o == 42); +} + +TEST_CASE("monad: co_await std::optional<> without value", "[nihil]") +{ + auto get_value = [] -> std::optional<int> { + return {}; + }; + + auto try_get_value = [&get_value] -> std::optional<int> { + co_return co_await get_value(); + }; + + auto o = try_get_value(); + REQUIRE(!o.has_value()); +} + +TEST_CASE("monad: co_await std::expected<> with value", "[nihil]") +{ + auto get_value = [] -> std::expected<int, std::string> { + return 42; + }; + + auto try_get_value = [&get_value] -> std::expected<int, std::string> { + co_return co_await get_value(); + }; + + auto o = try_get_value(); + REQUIRE(o == 42); +} + +TEST_CASE("monad: co_await std::expected<> with error", "[nihil]") +{ + auto get_value = [] -> std::expected<int, std::string> { + return std::unexpected("error"); + }; + + auto try_get_value = [&get_value] -> std::expected<int, std::string> { + co_return co_await get_value(); + }; + + auto o = try_get_value(); + REQUIRE(!o); + REQUIRE(o.error() == "error"); +} diff --git a/nihil.posix/CMakeLists.txt b/nihil.posix/CMakeLists.txt new file mode 100644 index 0000000..62f6aaf --- /dev/null +++ b/nihil.posix/CMakeLists.txt @@ -0,0 +1,61 @@ +# This source code is released into the public domain. + +add_library(nihil.posix STATIC) +target_link_libraries(nihil.posix PRIVATE + nihil.core nihil.error nihil.flagset nihil.guard nihil.monad) + +target_sources(nihil.posix + PUBLIC FILE_SET modules TYPE CXX_MODULES FILES + posix.ccm + + argv.ccm + ensure_dir.ccm + exec.ccm + execv.ccm + executor.ccm + fd.ccm + find_in_path.ccm + getenv.ccm + open.ccm + process.ccm + read_file.ccm + rename.ccm + spawn.ccm + tempfile.ccm + write_file.ccm + + PRIVATE + argv.cc + ensure_dir.cc + exec.cc + execv.cc + getenv.cc + fd.cc + find_in_path.cc + open.cc + process.cc + rename.cc + tempfile.cc +) + +if(NIHIL_TESTS) + enable_testing() + + add_executable(nihil.posix.test + test.fd.cc + test.getenv.cc + test.spawn.cc + test.tempfile.cc + ) + + target_link_libraries(nihil.posix.test PRIVATE + nihil.posix + Catch2::Catch2WithMain + ) + + find_package(Catch2 REQUIRED) + + include(CTest) + include(Catch) + catch_discover_tests(nihil.posix.test) +endif() diff --git a/nihil.posix/argv.cc b/nihil.posix/argv.cc new file mode 100644 index 0000000..e6b1389 --- /dev/null +++ b/nihil.posix/argv.cc @@ -0,0 +1,65 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <memory> +#include <ranges> +#include <string> +#include <vector> + +module nihil.posix; + +namespace nihil { + +argv::argv() = default; +argv::argv(argv &&) noexcept = default; +auto argv::operator=(this argv &, argv &&) -> argv & = default; + +argv::~argv() +{ + for (auto *arg : m_args) + delete[] arg; +} + +auto argv::data(this argv const &self) -> char const * const * +{ + return self.m_args.data(); +} + +auto argv::data(this argv &self) -> char * const * +{ + return self.m_args.data(); +} + +auto argv::size(this argv const &self) +{ + return self.m_args.size(); +} + +auto argv::begin(this argv const &self) +{ + return self.m_args.begin(); +} + +auto argv::end(this argv const &self) +{ + return self.m_args.end(); +} + + +auto argv::add_arg(this argv &self, std::string_view arg) -> void +{ + // Create a nul-terminated C string. + auto ptr = std::make_unique<char[]>(arg.size() + 1); + std::ranges::copy(arg, ptr.get()); + ptr[arg.size()] = '\0'; + + // Ensure we won't throw when emplacing the pointer. + self.m_args.reserve(self.m_args.size() + 1); + self.m_args.emplace_back(ptr.release()); +} + +} // namespace nihil + diff --git a/nihil.posix/argv.ccm b/nihil.posix/argv.ccm new file mode 100644 index 0000000..6f60f4b --- /dev/null +++ b/nihil.posix/argv.ccm @@ -0,0 +1,78 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <memory> +#include <ranges> +#include <string> +#include <vector> + +export module nihil.posix:argv; + +namespace nihil { + +/* + * argv: stores a null-terminated array of nul-terminated C strings. + * argv::data() is suitable for passing to ::execv(). + * + * Create an argv using argv::from_range(), which takes a range of + * string-like objects. + */ + +export struct argv { + /* + * Create a new argv from a range. + */ + argv(std::from_range_t, std::ranges::range auto &&args) + { + for (auto &&arg : args) + add_arg(std::string_view(arg)); + + m_args.push_back(nullptr); + } + + /* + * Create an argv from an initializer list. + */ + template<typename T> + explicit argv(std::initializer_list<T> &&args) + : argv(std::from_range, std::forward<decltype(args)>(args)) + { + } + + // Movable. + argv(argv &&) noexcept; + auto operator=(this argv &, argv &&other) -> argv &; + + // Not copyable. TODO: for completeness, it probably should be. + argv(argv const &) = delete; + auto operator=(this argv &, argv const &other) -> argv& = delete; + + ~argv(); + + // Access the stored arguments. + [[nodiscard]] auto data(this argv const &self) -> char const * const *; + [[nodiscard]] auto data(this argv &self) -> char * const *; + [[nodiscard]] auto size(this argv const &self); + + // Range access + [[nodiscard]] auto begin(this argv const &self); + [[nodiscard]] auto end(this argv const &self); + +private: + // Use the from_range() factory method to create new instances. + argv(); + + // The argument pointers, including the null terminator. + // This can't be a vector<unique_ptr> because we need an array of + // char pointers to pass to exec. + std::vector<char *> m_args; + + // Add a new argument to the array. + auto add_arg(this argv &self, std::string_view arg) -> void; +}; + +} // namespace nihil + diff --git a/nihil.posix/ensure_dir.cc b/nihil.posix/ensure_dir.cc new file mode 100644 index 0000000..88e8898 --- /dev/null +++ b/nihil.posix/ensure_dir.cc @@ -0,0 +1,30 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <expected> +#include <filesystem> +#include <format> +#include <system_error> + +module nihil.posix; + +import nihil.error; + +namespace nihil { + +auto ensure_dir(std::filesystem::path const &dir) -> std::expected<void, error> +{ + auto err = std::error_code(); + + std::filesystem::create_directories(dir, err); + + if (err) + return std::unexpected(error(err)); + + return {}; +} + +} // namespace nihil diff --git a/nihil.posix/ensure_dir.ccm b/nihil.posix/ensure_dir.ccm new file mode 100644 index 0000000..fa92a90 --- /dev/null +++ b/nihil.posix/ensure_dir.ccm @@ -0,0 +1,23 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <expected> +#include <filesystem> + +export module nihil.posix:ensure_dir; + +import nihil.error; + +namespace nihil { + +/* + * Create the given directory and any parent directories. + */ +export [[nodiscard]] auto ensure_dir(std::filesystem::path const &dir) + -> std::expected<void, error>; + +} // namespace nihil + diff --git a/nihil.posix/exec.cc b/nihil.posix/exec.cc new file mode 100644 index 0000000..5bdcbf7 --- /dev/null +++ b/nihil.posix/exec.cc @@ -0,0 +1,71 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <coroutine> +#include <expected> +#include <format> +#include <string> +#include <utility> + +#include <err.h> +#include <fcntl.h> +#include <unistd.h> + +extern char **environ; + +module nihil.posix; + +import nihil.error; +import nihil.monad; + +namespace nihil { + +fexecv::fexecv(fd &&execfd, argv &&args) noexcept + : m_execfd(std::move(execfd)) + , m_args(std::move(args)) +{ +} + +auto fexecv::exec(this fexecv &self) + -> std::expected<void, error> +{ + ::fexecve(self.m_execfd.get(), self.m_args.data(), environ); + return std::unexpected(error("fexecve failed", + error(std::errc(errno)))); +} + +fexecv::fexecv(fexecv &&) noexcept = default; +auto fexecv::operator=(this fexecv &, fexecv &&) noexcept -> fexecv& = default; + +auto execv(std::string_view path, argv &&argv) + -> std::expected<fexecv, error> +{ + auto file = co_await open(path, O_EXEC) + .transform_error([&] (error cause) { + return error(std::format("could not open {}", path), + std::move(cause)); + }); + + co_return fexecv(std::move(file), std::move(argv)); +} + +auto execvp(std::string_view file, argv &&argv) + -> std::expected<fexecv, error> +{ + auto execfd = find_in_path(file); + if (!execfd) + return std::unexpected(error( + std::format("executable not found in path: {}", file))); + return fexecv(std::move(*execfd), std::move(argv)); +} + +auto shell(std::string_view const &command) + -> std::expected<fexecv, error> +{ + return execl("/bin/sh", "sh", "-c", command); +} + +} // namespace nihil diff --git a/nihil.posix/exec.ccm b/nihil.posix/exec.ccm new file mode 100644 index 0000000..6098318 --- /dev/null +++ b/nihil.posix/exec.ccm @@ -0,0 +1,105 @@ +/* + * This source code is released into the public domain. + */ + +module; + +/* + * Exec providers, mostly used for spawn(). + */ + +#include <expected> +#include <string> + +export module nihil.posix:exec; + +import nihil.error; +import :argv; +import :fd; + +namespace nihil { + +/* + * A concept to mark spawn executors. + */ + +export struct exec_tag{}; + +export template<typename T> +concept executor = + requires (T e) { + std::same_as<exec_tag, typename std::remove_cvref_t<T>::tag>; + { e.exec() }; + }; + +/* + * fexecv: use a file descriptor and an argument vector to call ::fexecve(). + * This is the lowest-level executor which all others are implemented + * in terms of. + * + * TODO: Should have a way to pass the environment (envp). + */ +export struct fexecv final { + using tag = exec_tag; + + fexecv(fd &&execfd, argv &&args) noexcept; + + [[nodiscard]] auto exec(this fexecv &self) + -> std::expected<void, error>; + + // Movable + fexecv(fexecv &&) noexcept; + auto operator=(this fexecv &, fexecv &&) noexcept -> fexecv&; + + // Not copyable (because we hold the open fd object) + fexecv(fexecv const &) = delete; + auto operator=(this fexecv &, fexecv const &) -> fexecv& = delete; + +private: + fd m_execfd; + argv m_args; +}; + +/* + * execv: equivalent to fexecv(), except the command is passed as + * a pathname instead of a file descriptor. Does not search $PATH. + */ +export [[nodiscard]] auto execv(std::string_view path, argv &&argv) + -> std::expected<fexecv, error>; + +/* + * execvp: equivalent to fexecv(), except the command is passed as + * a filename instead of a file descriptor. If the filename is not + * an absolute path, it will be searched for in $PATH. + */ +export [[nodiscard]] auto execvp(std::string_view file, argv &&argv) + -> std::expected<fexecv, error>; + +/* + * execl: equivalent to execv, except the arguments are passed as a + * variadic pack of string-like objects. + */ +export [[nodiscard]] auto execl(std::string_view path, auto &&...args) + -> std::expected<fexecv, error> +{ + return execv(path, argv({std::string_view(args)...})); +} + +/* + * execlp: equivalent to execvp, except the arguments are passed as a + * variadic pack of string-like objects. + */ +export [[nodiscard]] auto execlp(std::string_view file, auto &&...args) + -> std::expected<fexecv, error> +{ + return execvp(file, argv({std::string_view(args)...})); +} + +/* + * shell: run the process by invoking /bin/sh -c with the single argument, + * equivalent to system(3). + */ +export [[nodiscard]] auto shell(std::string_view const &command) + -> std::expected<fexecv, error>; + +} // namespace nihil diff --git a/nihil.posix/executor.ccm b/nihil.posix/executor.ccm new file mode 100644 index 0000000..f348dc8 --- /dev/null +++ b/nihil.posix/executor.ccm @@ -0,0 +1,27 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <concepts> +#include <type_traits> + +export module nihil.posix:executor; + +namespace nihil { + +/* + * A concept to mark spawn executors. + */ + +export struct exec_tag{}; + +export template<typename T> +concept executor = + requires (T e) { + std::same_as<exec_tag, typename std::remove_cvref_t<T>::tag>; + { e.exec() }; + }; + +} // namespace nihil diff --git a/nihil.posix/execv.cc b/nihil.posix/execv.cc new file mode 100644 index 0000000..63f9698 --- /dev/null +++ b/nihil.posix/execv.cc @@ -0,0 +1,43 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <coroutine> +#include <expected> +#include <format> +#include <string> +#include <utility> + +#include <err.h> +#include <fcntl.h> +#include <unistd.h> + +extern char **environ; + +module nihil.posix; + +import nihil.error; +import nihil.monad; + +namespace nihil { + +execv::execv(std::filesystem::path path, argv &&args) noexcept + : m_path(std::move(path)) + , m_args(std::move(args)) +{ +} + +auto execv::exec(this execv &self) -> std::expected<void, error> +{ + ::execve(self.m_path.string().c_str(), self.m_args.data(), environ); + return std::unexpected(error("execve failed", error(std::errc(errno)))); +} + +execv::execv(execv &&) noexcept = default; +execv::execv(execv const &) = default; +auto execv::operator=(this execv &, execv &&) -> execv & = default; +auto execv::operator=(this execv &, execv const &) -> execv & = default; + +} // namespace nihil diff --git a/nihil.posix/execv.ccm b/nihil.posix/execv.ccm new file mode 100644 index 0000000..ca036a1 --- /dev/null +++ b/nihil.posix/execv.ccm @@ -0,0 +1,47 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <expected> +#include <filesystem> +#include <string> + +export module nihil.posix:execv; + +import nihil.error; +import :argv; +import :executor; + +namespace nihil { + +/* + * execv: use a filename and an argument vector to call ::execve(). + * This is the lowest-level executor which all others are implemented + * in terms of, if fexecve is not available. + * + * TODO: Should have a way to pass the environment (envp). + */ +export struct execv final +{ + using tag = exec_tag; + + execv(std::filesystem::path, argv &&) noexcept; + + [[nodiscard]] auto exec(this execv &) -> std::expected<void, error>; + + // Movable + execv(execv &&) noexcept; + auto operator=(this execv &, execv &&) noexcept -> execv &; + + // Copyable. + execv(execv const &); + auto operator=(this execv &, execv const &) -> execv &; + +private: + std::filesystem::path m_path; + argv m_args; +}; + +} // namespace nihil diff --git a/nihil.posix/fd.cc b/nihil.posix/fd.cc new file mode 100644 index 0000000..6d5e54f --- /dev/null +++ b/nihil.posix/fd.cc @@ -0,0 +1,220 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <fcntl.h> +#include <unistd.h> + +#include <coroutine> +#include <expected> +#include <format> +#include <stdexcept> +#include <system_error> + +module nihil.posix; + +import nihil.error; +import nihil.monad; + +namespace nihil { + +fd::fd() noexcept = default; + +fd::fd(int fileno) noexcept + : m_fileno(fileno) +{ +} + +fd::~fd() +{ + if (*this) + std::ignore = this->close(); +} + +fd::fd(fd &&other) noexcept + : m_fileno(std::exchange(other.m_fileno, invalid_fileno)) +{ +} + +auto fd::operator=(this fd &self, fd &&other) noexcept -> fd & +{ + if (&self != &other) + self.m_fileno = std::exchange(other.m_fileno, invalid_fileno); + return self; +} + +fd::operator bool(this fd const &self) noexcept +{ + return self.m_fileno != invalid_fileno; +} + +auto fd::close(this fd &self) -> std::expected<void, error> +{ + auto const ret = ::close(self.get()); + self.m_fileno = invalid_fileno; + + if (ret == 0) + return {}; + + return std::unexpected(error(std::errc(errno))); +} + +auto fd::get(this fd const &self) -> int +{ + if (self) + return self.m_fileno; + throw std::logic_error("Attempt to call get() on invalid fd"); +} + +auto fd::release(this fd &&self) -> int +{ + if (self) + return std::exchange(self.m_fileno, invalid_fileno); + throw std::logic_error("Attempt to release an invalid fd"); +} + +auto dup(fd const &self) -> std::expected<fd, error> +{ + auto const newfd = ::dup(self.get()); + if (newfd != -1) + return newfd; + + return std::unexpected(error(std::errc(errno))); +} + +auto dup(fd const &self, int newfd) -> std::expected<fd, error> +{ + auto const ret = ::dup2(self.get(), newfd); + if (ret != -1) + return newfd; + + return std::unexpected(error(std::errc(errno))); +} + +auto raw_dup(fd const &self) -> std::expected<int, error> +{ + auto const newfd = ::dup(self.get()); + if (newfd != -1) + return newfd; + + return std::unexpected(error(std::errc(errno))); +} + +auto raw_dup(fd const &self, int newfd) -> std::expected<int, error> +{ + auto const ret = ::dup2(self.get(), newfd); + if (ret != -1) + return newfd; + + return std::unexpected(error(std::errc(errno))); +} + +auto getflags(fd const &self) -> std::expected<int, error> +{ + auto const flags = ::fcntl(self.get(), F_GETFL); + if (flags != -1) + return flags; + + return std::unexpected(error(std::errc(errno))); +} + +auto replaceflags(fd &self, int newflags) -> std::expected<void, error> +{ + auto const ret = ::fcntl(self.get(), F_SETFL, newflags); + if (ret == 0) + return {}; + + return std::unexpected(error(std::errc(errno))); +} + +auto setflags(fd &self, int newflags) -> std::expected<int, error> +{ + auto flags = co_await getflags(self); + + flags |= newflags; + co_await replaceflags(self, flags); + + co_return flags; +} + +auto clearflags(fd &self, int clrflags) -> std::expected<int, error> +{ + auto flags = co_await getflags(self); + + flags &= ~clrflags; + co_await replaceflags(self, flags); + + co_return flags; +} + +auto getfdflags(fd const &self) -> std::expected<int, error> +{ + auto const flags = ::fcntl(self.get(), F_GETFD); + if (flags != -1) + return flags; + + return std::unexpected(error(std::errc(errno))); +} + +auto replacefdflags(fd &self, int newflags) -> std::expected<void, error> +{ + auto const ret = ::fcntl(self.get(), F_SETFD, newflags); + if (ret != -1) + return {}; + + return std::unexpected(error(std::errc(errno))); +} + +auto setfdflags(fd &self, int newflags) -> std::expected<int, error> +{ + auto flags = co_await getfdflags(self); + + flags |= newflags; + co_await replacefdflags(self, flags); + + co_return flags; +} + +auto clearfdflags(fd &self, int clrflags) -> std::expected<int, error> +{ + auto flags = co_await getfdflags(self); + + flags &= ~clrflags; + co_await replacefdflags(self, flags); + + co_return flags; +} + +auto pipe() -> std::expected<std::pair<fd, fd>, error> +{ + auto fds = std::array<int, 2>{}; + + if (auto const ret = ::pipe(fds.data()); ret != 0) + return std::unexpected(error(std::errc(errno))); + + return {{fd(fds[0]), fd(fds[1])}}; +} + +auto fd::write(this fd &self, std::span<std::byte const> buffer) + -> std::expected<std::size_t, error> +{ + auto const ret = ::write(self.get(), buffer.data(), buffer.size()); + if (ret >= 0) + return ret; + + return std::unexpected(error(std::errc(errno))); +} + +auto fd::read(this fd &self, std::span<std::byte> buffer) + -> std::expected<std::span<std::byte>, error> +{ + auto const ret = ::read(self.get(), buffer.data(), buffer.size()); + if (ret >= 0) + return buffer.subspan(0, ret); + + return std::unexpected(error(std::errc(errno))); +} + +} // namespace nihil diff --git a/nihil.posix/fd.ccm b/nihil.posix/fd.ccm new file mode 100644 index 0000000..b937f46 --- /dev/null +++ b/nihil.posix/fd.ccm @@ -0,0 +1,157 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <coroutine> +#include <expected> +#include <ranges> +#include <span> +#include <stdexcept> +#include <system_error> + +export module nihil.posix:fd; + +import nihil.error; +import nihil.monad; + +namespace nihil { + +/* + * fd: a file descriptor. + */ + +export struct fd final { + // Construct an empty (invalid) fd. + fd() noexcept; + + // Construct an fd from an exising file destrictor, taking ownership. + fd(int fd_) noexcept; + + // Destructor. Close the fd, discarding any errors. + ~fd(); + + // Move from another fd, leaving the moved-from fd in an invalid state. + fd(fd &&other) noexcept; + auto operator=(this fd &, fd &&other) noexcept -> fd &; + + // Not copyable. + fd(fd const &) = delete; + fd& operator=(this fd &, fd const &) = delete; + + // Return true if this fd is valid (open). + [[nodiscard]] explicit operator bool(this fd const &self) noexcept; + + // Close the wrapped fd. + [[nodiscard]] auto close(this fd &self) -> std::expected<void, error>; + + // Return the stored fd. + [[nodiscard]] auto get(this fd const &self) -> int; + + // Release the stored fd and return it. The caller must close it. + [[nodiscard]] auto release(this fd &&self) -> int; + + // Write data from the provided buffer to the fd. Returns the + // number of bytes written. + [[nodiscard]] auto write(this fd &self, std::span<std::byte const>) + -> std::expected<std::size_t, error>; + + // Read data from the fd to the provided buffer. Returns a + // subspan containing the data which was read. + [[nodiscard]] auto read(this fd &self, std::span<std::byte>) + -> std::expected<std::span<std::byte>, error>; + +private: + static constexpr int invalid_fileno = -1; + + int m_fileno = invalid_fileno; +}; + +// Create a copy of this fd by calling dup(). +export [[nodiscard]] auto dup(fd const &self) -> std::expected<fd, error>; + +// Create a copy of this fd by calling dup2(). Note that because this results +// in the existing fd and the new fd both being managed by an fd instance, +// there are two potential cases that can cause problems: +// +// - dup()ing an fd to itself (a no-op) +// - dup()ing an fd to an fd which is already managed by an fd instance +// +// In both of these cases, either use raw_dup() instead, or immediately call +// release() on the returned fd to prevent the fd instance from closing it. +export [[nodiscard]] auto dup(fd const &self, int newfd) + -> std::expected<fd, error>; + +// Create a copy of this fd by calling dup(). +export [[nodiscard]] auto raw_dup(fd const &self) + -> std::expected<int, error>; + +// Create a copy of this fd by calling dup2(). +export [[nodiscard]] auto raw_dup(fd const &self, int newfd) + -> std::expected<int, error>; + +// Return the fnctl flags for this fd. +export [[nodiscard]] auto getflags(fd const &self) + -> std::expected<int, error>; + +// Replace the fnctl flags for this fd. +export [[nodiscard]] auto replaceflags(fd &self, int newflags) + -> std::expected<void, error>; + +// Add bits to the fcntl flags for this fd. Returns the new flags. +export [[nodiscard]] auto setflags(fd &self, int newflags) + -> std::expected<int, error>; + +// Remove bits from the fcntl flags for this fd. Returns the new flags. +export [[nodiscard]] auto clearflags(fd &self, int clrflags) + -> std::expected<int, error>; + +// Return the fd flags for this fd. +export [[nodiscard]] auto getfdflags(fd const &self) + -> std::expected<int, error>; + +// Replace the fd flags for this fd. +export [[nodiscard]] auto replacefdflags(fd &self, int newflags) + -> std::expected<void, error>; + +// Add bits to the fd flags for this fd. Returns the new flags. +export [[nodiscard]] auto setfdflags(fd &self, int newflags) + -> std::expected<int, error>; + +// Remove bits from the fd flags for this fd. Returns the new flags. +export [[nodiscard]] auto clearfdflags(fd &self, int clrflags) + -> std::expected<int, error>; + +// Create two fds by calling pipe() and return them. +export [[nodiscard]] auto pipe() -> std::expected<std::pair<fd, fd>, error>; + +/* + * Write data to a file descriptor from the provided range. Returns the + * number of bytes written. + */ +export [[nodiscard]] auto write(fd &file, + std::ranges::contiguous_range auto &&range) + -> std::expected<std::size_t, error> +requires(sizeof(std::ranges::range_value_t<decltype(range)>) == 1) +{ + return file.write(as_bytes(std::span(range))); +} + +/* + * Read data from a file descriptor into the provided buffer. Returns a + * span containing the data that was read. + */ +export [[nodiscard]] auto read(fd &file, + std::ranges::contiguous_range auto &&range) + -> std::expected< + std::span<std::ranges::range_value_t<decltype(range)>>, + error> +requires(sizeof(std::ranges::range_value_t<decltype(range)>) == 1) +{ + auto bspan = as_writable_bytes(std::span(range)); + auto rspan = co_await file.read(bspan); + co_return std::span(range).subspan(0, rspan.size()); +} + +} // namespace nihil diff --git a/nihil.posix/fexecv.ccm b/nihil.posix/fexecv.ccm new file mode 100644 index 0000000..5ad6c62 --- /dev/null +++ b/nihil.posix/fexecv.ccm @@ -0,0 +1,53 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <expected> +#include <string> + +#include "nihil.hh" + +#ifdef NIHIL_HAVE_FEXECVE + +export module nihil.posix:fexecv; + +import nihil.error; +import :argv; +import :executor; +import :fd; + +namespace nihil { + +/* + * fexecv: use a file descriptor and an argument vector to call ::fexecve(). + * This is the lowest-level executor which all others are implemented + * in terms of (if it's available). + * + * TODO: Should have a way to pass the environment (envp). + */ +export struct fexecv final { + using tag = exec_tag; + + fexecv(fd &&execfd, argv &&args) noexcept; + + [[nodiscard]] auto exec(this fexecv &self) + -> std::expected<void, error>; + + // Movable + fexecv(fexecv &&) noexcept; + auto operator=(this fexecv &, fexecv &&) noexcept -> fexecv&; + + // Not copyable (because we hold the open fd object) + fexecv(fexecv const &) = delete; + auto operator=(this fexecv &, fexecv const &) -> fexecv& = delete; + +private: + fd m_execfd; + argv m_args; +}; + +} // namespace nihil + +#endif // NIHIL_HAVE_FEXECVE
\ No newline at end of file diff --git a/nihil.posix/find_in_path.cc b/nihil.posix/find_in_path.cc new file mode 100644 index 0000000..7b03faa --- /dev/null +++ b/nihil.posix/find_in_path.cc @@ -0,0 +1,52 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <filesystem> +#include <optional> +#include <ranges> +#include <string> + +#include <fcntl.h> +#include <paths.h> +#include <unistd.h> + +module nihil.posix; + +namespace nihil { + +auto find_in_path(std::filesystem::path const &file) -> std::optional<std::filesystem::path> +{ + using namespace std::literals; + + auto try_return = [](std::filesystem::path file) + -> std::optional<std::filesystem::path> + { + auto ret = ::access(file.string().c_str(), X_OK); + if (ret == 0) + return {std::move(file)}; + return {}; + }; + + // Absolute pathname skips the search. + if (file.is_absolute()) + return try_return(file); + + auto path = getenv("PATH").value_or(_PATH_DEFPATH); + + for (auto &&dir : path | std::views::split(':')) { + // An empty $PATH element means cwd. + auto sdir = dir.empty() + ? std::filesystem::path(".") + : std::filesystem::path(std::string_view(dir)); + + if (auto ret = try_return(sdir / file); ret) + return ret; + } + + return {}; +} + +} // namespace nihil diff --git a/nihil.posix/find_in_path.ccm b/nihil.posix/find_in_path.ccm new file mode 100644 index 0000000..4988a12 --- /dev/null +++ b/nihil.posix/find_in_path.ccm @@ -0,0 +1,24 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <filesystem> +#include <optional> + +export module nihil.posix:find_in_path; + +import nihil.error; +import :fd; + +namespace nihil { + +/* + * Find an executable in $PATH and return the full path. If $PATH is not set, uses _PATH_DEFPATH. + * If the file can't be found or opened, returns std::nullopt. + */ +export [[nodiscard]] auto find_in_path(std::filesystem::path const &file) + -> std::optional<std::filesystem::path>; + +} // namespace nihil diff --git a/nihil.posix/getenv.cc b/nihil.posix/getenv.cc new file mode 100644 index 0000000..ad93305 --- /dev/null +++ b/nihil.posix/getenv.cc @@ -0,0 +1,54 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <cstdint> +#include <expected> +#include <string> +#include <system_error> +#include <vector> + +#include <unistd.h> + +#include "nihil.hh" + +module nihil.posix; + +import nihil.error; + +namespace nihil { + +auto getenv(std::string_view varname) -> std::expected<std::string, error> +{ + auto cvarname = std::string(varname); + +#ifdef NIHIL_HAVE_GETENV_R + // Start with a buffer of this size, and double it every iteration. + constexpr auto bufinc = std::size_t{1024}; + + auto buf = std::vector<char>(bufinc); + for (;;) { + auto const ret = ::getenv_r(cvarname.c_str(), + buf.data(), buf.size()); + + if (ret == 0) + return {std::string(buf.data())}; + + if (ret == -1 && errno == ERANGE) { + buf.resize(buf.size() * 2); + continue; + } + + return std::unexpected(error(std::errc(errno))); + } +#else // NIHIL_HAVE_GETENV_R + auto *v = ::getenv(cvarname.c_str()); + if (v == nullptr) + return std::unexpected(error(std::errc(errno))); + return {std::string(v)}; +#endif // NIHIL_HAVE_GETENV_R +} + +} // namespace nihil diff --git a/nihil.posix/getenv.ccm b/nihil.posix/getenv.ccm new file mode 100644 index 0000000..465f7e7 --- /dev/null +++ b/nihil.posix/getenv.ccm @@ -0,0 +1,23 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <expected> +#include <string> + +export module nihil.posix:getenv; + +import nihil.error; + +namespace nihil { + +/* + * Find a variable by the given name in the environment by calling getenv_r(). + */ + +export [[nodiscard]] auto getenv(std::string_view varname) + -> std::expected<std::string, error>; + +} // namespace nihil diff --git a/nihil.posix/open.cc b/nihil.posix/open.cc new file mode 100644 index 0000000..9ef6538 --- /dev/null +++ b/nihil.posix/open.cc @@ -0,0 +1,31 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <expected> +#include <filesystem> +#include <system_error> + +#include <fcntl.h> +#include <unistd.h> + +module nihil.posix; + +import nihil.error; +import :fd; + +namespace nihil { + +auto open(std::filesystem::path const &filename, int flags, int mode) + -> std::expected<fd, error> +{ + auto fdno = ::open(filename.c_str(), flags, mode); + if (fdno != -1) + return fd(fdno); + + return std::unexpected(error(std::errc(errno))); +} + +} // namespace nihil diff --git a/nihil.posix/open.ccm b/nihil.posix/open.ccm new file mode 100644 index 0000000..eaedacd --- /dev/null +++ b/nihil.posix/open.ccm @@ -0,0 +1,24 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <expected> +#include <filesystem> + +export module nihil.posix:open; + +import nihil.error; +import :fd; + +export namespace nihil { + +/* + * Open the given file and return an fd for it. + */ +[[nodiscard]] auto open(std::filesystem::path const &filename, + int flags, int mode = 0777) + -> std::expected<fd, error>; + +} // namespace nihil diff --git a/nihil.posix/open_in_path.cc b/nihil.posix/open_in_path.cc new file mode 100644 index 0000000..30021ca --- /dev/null +++ b/nihil.posix/open_in_path.cc @@ -0,0 +1,51 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <filesystem> +#include <optional> +#include <ranges> +#include <string> + +#include <fcntl.h> +#include <paths.h> + +module nihil.posix; + +namespace nihil { + +auto open_in_path(std::filesystem::path const &file) -> std::optional<fd> +{ + using namespace std::literals; + + auto try_open = + [](std::filesystem::path const &file) -> std::optional<fd> + { + auto ret = open(file, O_EXEC); + if (ret) + return {std::move(*ret)}; + return {}; + }; + + // Absolute pathname skips the search. + if (file.is_absolute()) + return try_open(file); + + auto path = getenv("PATH").value_or(_PATH_DEFPATH); + + for (auto &&dir : path | std::views::split(':')) { + // An empty $PATH element means cwd. + auto sdir = dir.empty() + ? std::filesystem::path(".") + : std::filesystem::path(std::string_view(dir)); + + if (auto ret = try_open(sdir / file); ret) + return ret; + } + + return {}; +} + +} // namespace nihil diff --git a/nihil.posix/open_in_path.ccm b/nihil.posix/open_in_path.ccm new file mode 100644 index 0000000..1fae56b --- /dev/null +++ b/nihil.posix/open_in_path.ccm @@ -0,0 +1,23 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <filesystem> +#include <optional> + +export module nihil.posix:find_in_path; + +import nihil.error; +import :fd; + +namespace nihil { + +/* + * Find an executable in $PATH and open it with O_EXEC. If $PATH is not set, uses _PATH_DEFPATH. + * If the file can't be found or opened, returns std::nullopt. + */ +export [[nodiscard]] auto open_in_path(std::filesystem::path const &file) -> std::optional<fd>; + +} // namespace nihil diff --git a/nihil.posix/posix.ccm b/nihil.posix/posix.ccm new file mode 100644 index 0000000..ea13f81 --- /dev/null +++ b/nihil.posix/posix.ccm @@ -0,0 +1,35 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <expected> +#include <filesystem> +#include <optional> +#include <string> + +#include "nihil.hh" + +export module nihil.posix; + +import nihil.error; + +export import :argv; +export import :ensure_dir; +export import :exec; +export import :execv; +export import :fd; +export import :find_in_path; +export import :getenv; +export import :open; +export import :process; +export import :read_file; +export import :rename; +export import :spawn; +export import :tempfile; +export import :write_file; + +#ifdef NIHIL_HAVE_FEXECVE +export import :fexecv; +#endif diff --git a/nihil.posix/process.cc b/nihil.posix/process.cc new file mode 100644 index 0000000..70e84b7 --- /dev/null +++ b/nihil.posix/process.cc @@ -0,0 +1,102 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <cerrno> +#include <cstring> +#include <expected> +#include <format> +#include <optional> +#include <system_error> +#include <utility> + +#include <sys/types.h> +#include <sys/wait.h> + +module nihil.posix; + +import nihil.error; + +namespace nihil { + +auto wait_result::okay(this wait_result const &self) -> bool +{ + return self.status() == 0; +} + +wait_result::operator bool(this wait_result const &self) +{ + return self.okay(); +} + +auto wait_result::status(this wait_result const &self) -> std::optional<int> +{ + if (WIFEXITED(self._status)) + return WEXITSTATUS(self._status); + return {}; +} + +auto wait_result::signal(this wait_result const &self) -> std::optional<int> +{ + if (WIFSIGNALED(self._status)) + return WTERMSIG(self._status); + return {}; +} + +wait_result::wait_result(int status) + : _status(status) +{} + +process::process(::pid_t pid) + : m_pid(pid) +{} + +process::~process() { + if (m_pid == -1) + return; + + auto status = int{}; + std::ignore = waitpid(m_pid, &status, WEXITED); +} + +process::process(process &&other) noexcept + : m_pid(std::exchange(other.m_pid, -1)) +{ +} + +auto process::operator=(this process &self, process &&other) noexcept + -> process & +{ + if (&self != &other) { + self.m_pid = std::exchange(other.m_pid, -1); + } + + return self; +} + +// Get the child's process id. +auto process::pid(this process const &self) noexcept -> ::pid_t +{ + return self.m_pid; +} + +auto process::wait(this process &&self) -> std::expected<wait_result, error> +{ + auto status = int{}; + auto ret = waitpid(self.m_pid, &status, WEXITED); + if (ret == -1) + return std::unexpected(error(std::errc(errno))); + + return wait_result(status); +} + +auto process::release(this process &&self) -> ::pid_t +{ + auto const ret = self.pid(); + self.m_pid = -1; + return ret; +} + +} // namespace nihil diff --git a/nihil.posix/process.ccm b/nihil.posix/process.ccm new file mode 100644 index 0000000..425deac --- /dev/null +++ b/nihil.posix/process.ccm @@ -0,0 +1,91 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <expected> +#include <optional> +#include <system_error> +#include <utility> + +#include <sys/types.h> + +export module nihil.posix:process; + +import nihil.error; + +namespace nihil { + +/* + * wait_result: the exit status of a process. + */ +export struct wait_result final { + // Return true if the process exited normally with an exit code of + // zero, otherwise false. + [[nodiscard]] auto okay(this wait_result const &self) -> bool; + [[nodiscard]] explicit operator bool(this wait_result const &self); + + // Return the exit status, if any. + [[nodiscard]] auto status(this wait_result const &self) + -> std::optional<int>; + + // Return the exit signal, if any. + [[nodiscard]] auto signal(this wait_result const &self) + -> std::optional<int>; + +private: + friend struct process; + + int _status; + + // Construct a new wait_result from the output of waitpid(). + wait_result(int status); +}; + +/* + * process: represents a process we created, which can be waited for. + */ +export struct process final { + process() = delete; + + /* + * Create a new process from a pid, which must be a child of the + * current process. + */ + process(::pid_t pid); + + // When destroyed, we automatically wait for the process to + // avoid creating zombie processes. + ~process(); + + // Movable. + process(process &&) noexcept; + auto operator=(this process &, process &&) noexcept -> process &; + + // Not copyable. + process(process const &) = delete; + auto operator=(this process &, process const &) -> process & = delete; + + // Get the child's process id. + [[nodiscard]] auto pid(this process const &self) noexcept -> ::pid_t; + + /* + * Wait for this process to exit (by calling waitpid()) and return + * its exit status. This destroys the process state, leaving this + * object in a moved-from state. + */ + [[nodiscard]] auto wait(this process &&self) + -> std::expected<wait_result, error>; + + /* + * Release this process so we won't try to wait for it when + * destroying this object. + */ + [[nodiscard]] auto release(this process &&self) -> ::pid_t; + +private: + ::pid_t m_pid; +}; + +} // namespace nihil diff --git a/nihil.posix/read_file.ccm b/nihil.posix/read_file.ccm new file mode 100644 index 0000000..be9e102 --- /dev/null +++ b/nihil.posix/read_file.ccm @@ -0,0 +1,49 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <algorithm> +#include <expected> +#include <filesystem> +#include <iterator> +#include <ranges> +#include <span> +#include <system_error> + +#include <fcntl.h> +#include <unistd.h> + +export module nihil.posix:read_file; + +import nihil.error; +import nihil.monad; +import :fd; +import :open; + +namespace nihil { + +/* + * Read the contents of a file into an output iterator. + */ +export [[nodiscard]] auto +read_file(std::filesystem::path const &filename, + std::output_iterator<char> auto &&iter) + -> std::expected<void, error> +{ + auto file = co_await open(filename, O_RDONLY); + + auto constexpr bufsize = std::size_t{1024}; + auto buffer = std::array<char, bufsize>{}; + + for (;;) { + auto read_buf = co_await(read(file, buffer)); + if (read_buf.empty()) + co_return {}; + + std::ranges::copy(read_buf, iter); + } +} + +} // namespace nihil diff --git a/nihil.posix/rename.cc b/nihil.posix/rename.cc new file mode 100644 index 0000000..9203d08 --- /dev/null +++ b/nihil.posix/rename.cc @@ -0,0 +1,34 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <expected> +#include <filesystem> + +module nihil.posix; + +import nihil.error; + +namespace nihil { + +/* + * Rename a file. + */ +auto rename_file(std::filesystem::path const &oldp, + std::filesystem::path const &newp) + -> std::expected<void, error> +{ + auto err = std::error_code(); + + std::filesystem::rename(oldp, newp, err); + + if (err) + return std::unexpected(error(err)); + + return {}; +} + + +} // namespace nihil diff --git a/nihil.posix/rename.ccm b/nihil.posix/rename.ccm new file mode 100644 index 0000000..796ec5b --- /dev/null +++ b/nihil.posix/rename.ccm @@ -0,0 +1,23 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <expected> +#include <filesystem> + +export module nihil.posix:rename; + +import nihil.error; + +namespace nihil { + +/* + * Rename a file (or directory). + */ +export [[nodiscard]] auto +rename(std::filesystem::path const &oldp, std::filesystem::path const &newp) + -> std::expected<void, error>; + +} // namespace nihil diff --git a/nihil.posix/spawn.ccm b/nihil.posix/spawn.ccm new file mode 100644 index 0000000..4cce334 --- /dev/null +++ b/nihil.posix/spawn.ccm @@ -0,0 +1,246 @@ +/* + * This source code is released into the public domain. + */ + +module; + +/* + * spawn(): fork and execute a child process. + */ + +#include <algorithm> +#include <cerrno> +#include <coroutine> +#include <expected> +#include <filesystem> +#include <format> +#include <iterator> +#include <print> +#include <string> +#include <utility> + +#include <sys/types.h> +#include <sys/wait.h> + +#include <fcntl.h> +#include <unistd.h> + +export module nihil.posix:spawn; + +import nihil.monad; +import :argv; +import :exec; +import :open; +import :process; + +namespace nihil { + +// Useful constants +export inline int constexpr stdin_fileno = STDIN_FILENO; +export inline int constexpr stdout_fileno = STDOUT_FILENO; +export inline int constexpr stderr_fileno = STDERR_FILENO; + +/* + * fd_pipe: create a pipe with one end in the child and the other in the + * parent. The child's side will be dup2()'d to the provided fd number. + * The parent side fd can be retrieved via parent_fd(); + */ +export struct fd_pipe final { + fd_pipe(int fdno, fd &&child_fd, fd &&parent_fd) + : m_fdno(fdno) + , m_child_fd(std::move(child_fd)) + , m_parent_fd(std::move(parent_fd)) + { + } + + auto run_in_child(this fd_pipe &self, process &) -> void + { + auto err = raw_dup(self.m_child_fd, self.m_fdno); + if (!err) { + std::print("dup: {}\n", err.error()); + _exit(1); + } + + /* + * We don't care about errors from close() since the fd + * is still closed. + */ + std::ignore = self.m_parent_fd.close(); + std::ignore = self.m_child_fd.close(); + } + + auto run_in_parent(this fd_pipe &self, process &) -> void + { + std::ignore = self.m_child_fd.close(); + } + + [[nodiscard]] auto parent_fd(this fd_pipe &self) -> fd & + { + return self.m_parent_fd; + } + +private: + int m_fdno; + fd m_child_fd; + fd m_parent_fd; +}; + +export [[nodiscard]] auto +make_fd_pipe(int fdno) -> std::expected<fd_pipe, error> +{ + auto fds = co_await pipe(); + co_return fd_pipe(fdno, std::move(fds.first), std::move(fds.second)); +} + +/* + * fd_file: open a file and provide it to the child as a file descriptor. + * open_flags and open_mode are as for ::open(). + */ +export struct fd_file final { + fd_file(int fdno, fd &&file_fd) + : m_fdno(fdno) + , m_file_fd(std::move(file_fd)) + { + } + + auto run_in_parent(this fd_file &self, process &) -> void + { + std::ignore = self.m_file_fd.close(); + } + + auto run_in_child(this fd_file &self, process &) -> void + { + auto err = raw_dup(self.m_file_fd, self.m_fdno); + if (!err) { + std::print("dup: {}\n", err.error()); + _exit(1); + } + + std::ignore = self.m_file_fd.close(); + } + +private: + int m_fdno; + fd m_file_fd; +}; + +export [[nodiscard]] auto +make_fd_file(int fdno, std::filesystem::path const &file, + int flags, int mode = 0777) + -> std::expected<fd_file, error> +{ + auto fd = co_await open(file, flags, mode); + co_return fd_file(fdno, std::move(fd)); +} + +/* + * Shorthand for fd_file with /dev/null as the file. + */ + +export [[nodiscard]] inline auto +stdin_devnull() -> std::expected<fd_file, error> +{ + return make_fd_file(stdin_fileno, "/dev/null", O_RDONLY); +} + +export [[nodiscard]] inline auto +stdout_devnull() -> std::expected<fd_file, error> +{ + return make_fd_file(stdout_fileno, "/dev/null", O_WRONLY); +} + +export [[nodiscard]] inline auto +stderr_devnull() -> std::expected<fd_file, error> +{ + return make_fd_file(stderr_fileno, "/dev/null", O_WRONLY); +} + +/* + * Capture the output of a pipe in the parent and read it into an + * output iterator. + */ +export template<std::output_iterator<char> Iterator> +struct fd_capture final { + fd_capture(fd_pipe &&pipe, Iterator it) + : m_pipe(std::move(pipe)) + , m_iterator(std::move(it)) + { + } + + auto run_in_child(this fd_capture &self, process &p) -> void + { + self.m_pipe.run_in_child(p); + } + + auto run_in_parent(this fd_capture &self, process &p) -> void + { + self.m_pipe.run_in_parent(p); + + auto constexpr bufsize = std::size_t{1024}; + auto buffer = std::array<char, bufsize>(); + auto &fd = self.m_pipe.parent_fd(); + for (;;) { + auto ret = read(fd, buffer); + if (!ret || ret->size() == 0) + break; + + std::ranges::copy(*ret, self.m_iterator); + } + + // We probably want to handle errors here somehow, + // but it's not clear what would be useful behaviour. + } + +private: + fd_pipe m_pipe; + Iterator m_iterator; +}; + +export [[nodiscard]] auto +make_capture(int fdno, std::output_iterator<char> auto &&it) + -> std::expected<fd_capture<decltype(it)>, error> +{ + auto pipe = co_await make_fd_pipe(fdno); + co_return fd_capture(std::move(pipe), + std::forward<decltype(it)>(it)); +} + +export [[nodiscard]] auto +make_capture(int fdno, std::string &str) + -> std::expected<fd_capture<decltype(std::back_inserter(str))>, error> +{ + auto pipe = co_await make_fd_pipe(fdno); + co_return fd_capture(std::move(pipe), std::back_inserter(str)); +} + +/* + * Spawn a new process with the given arguments and return a struct process. + * Throws exec_error() on failure. + */ +export [[nodiscard]] auto +spawn(executor auto &&executor, auto &&...actions) + -> std::expected<process, error> +{ + auto const pid = ::fork(); + if (pid == -1) + return std::unexpected(error("fork failed", + error(std::errc(errno)))); + + auto proc = process(pid); + + if (pid == 0) { + // We are in the child. + (actions.run_in_child(proc), ...); + std::ignore = std::move(proc).release(); + + auto err = executor.exec(); + std::print("{}\n", err.error()); + _exit(1); + } + + (actions.run_in_parent(proc), ...); + + return proc; +} + +} // namespace nihil diff --git a/nihil.posix/tempfile.cc b/nihil.posix/tempfile.cc new file mode 100644 index 0000000..b1d3dee --- /dev/null +++ b/nihil.posix/tempfile.cc @@ -0,0 +1,128 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <algorithm> +#include <coroutine> +#include <expected> +#include <filesystem> +#include <random> +#include <string> + +#include <fcntl.h> +#include <unistd.h> + +module nihil.posix; + +import nihil.flagset; +import :getenv; +import :open; + +namespace nihil { + +temporary_file::temporary_file(nihil::fd &&fd, + std::filesystem::path path) noexcept + : m_fd(std::move(fd)) + , m_path(std::move(path)) +{ +} + +temporary_file::temporary_file(nihil::fd &&fd) noexcept + : m_fd(std::move(fd)) +{ +} + +temporary_file::temporary_file(temporary_file &&other) noexcept + : m_fd(std::move(other.m_fd)) + , m_path(std::move(other.m_path)) +{ +} + +temporary_file::~temporary_file() //NOLINT(bugprone-exception-escape) +{ + if (m_fd) + release(); +} + +auto temporary_file::release(this temporary_file &self) -> void +{ + if (!self.m_fd) + throw std::logic_error( + "release() called on already-released tempfile"); + + if (!self.m_path.empty()) { + auto ec = std::error_code(); // ignore errors + remove(self.path(), ec); + + self.m_path.clear(); + } + + std::ignore = self.m_fd.close(); +} + +auto temporary_file::path(this temporary_file const &self) + -> std::filesystem::path const & +{ + if (self.m_path.empty()) + throw std::logic_error( + "path() called on unlinked temporary_file"); + + return self.m_path; +} + +auto temporary_file::fd(this temporary_file &self) -> nihil::fd & +{ + if (!self.m_fd) + throw std::logic_error("fd() called on empty temporary_file"); + + return self.m_fd; +} + +auto tempfile(tempfile_flags_t flags) -> std::expected<temporary_file, error> +{ + auto rng = std::default_random_engine(std::random_device{}()); + + auto random_name = [&] -> std::string { + auto constexpr length = std::size_t{12}; + auto constexpr randchars = std::string_view( + "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789"); + + auto dist = std::uniform_int_distribution<>( + 0, randchars.size() - 1); + auto ret = std::string(length, 0); + std::ranges::generate_n(ret.begin(), length, + [&] -> char { + return randchars[dist(rng)]; + }); + return ret; + }; + + auto dir = std::filesystem::path(getenv("TMPDIR").value_or("/tmp")); + + // Keep trying until we don't get EEXIST. + for (;;) { + auto filename = dir / (random_name() + ".tmp"); + auto fd = nihil::open(filename, O_RDWR | O_CREAT | O_EXCL, + 0600); + if (!fd) { + if (fd.error() == std::errc::file_exists) + continue; + return std::unexpected(fd.error()); + } + + if (flags & tempfile_unlink) { + auto ec = std::error_code(); + remove(filename, ec); + return temporary_file(std::move(*fd)); + } else { + return temporary_file(std::move(*fd), + std::move(filename)); + } + } +} + +} // namespace nihil diff --git a/nihil.posix/tempfile.ccm b/nihil.posix/tempfile.ccm new file mode 100644 index 0000000..82f3be4 --- /dev/null +++ b/nihil.posix/tempfile.ccm @@ -0,0 +1,87 @@ +/* + * This source code is released into the public domain. + */ + +module; + +/* + * tempfile: create a temporary file. + */ + +#include <cstdint> +#include <expected> +#include <filesystem> +#include <string> + +export module nihil.posix:tempfile; + +import nihil.error; +import nihil.flagset; +import :fd; + +namespace nihil { + +struct tempfile_flags_tag {}; +export using tempfile_flags_t = flagset<std::uint8_t, tempfile_flags_tag>; + +// No flags. +export inline constexpr auto tempfile_none = tempfile_flags_t(); + +// Unlink the tempfile immediately after creating it +export inline constexpr auto tempfile_unlink = tempfile_flags_t::bit<0>(); + +export struct temporary_file final { + /* + * Fetch the file's fd. + */ + [[nodiscard]] auto fd(this temporary_file &) -> nihil::fd &; + + /* + * Fetch the name of this file. If tempfile_unlink was specified, + * throws std::logic_error. + */ + [[nodiscard]] auto path(this temporary_file const &) + -> std::filesystem::path const &; + + /* + * Release this temporary file, causing it to be deleted immediately. + * Throws std::logic_error if the file has already been released. + */ + auto release(this temporary_file &) -> void; + + /* + * Destructor; unlink the file if we didn't already. + */ + ~temporary_file(); + + // Not copyable. + temporary_file(temporary_file const &) = delete; + + // Movable. + temporary_file(temporary_file &&other) noexcept; + + // Not assignable. + auto operator=(this temporary_file &, temporary_file const &) + -> temporary_file & = delete; + auto operator=(this temporary_file &, temporary_file &&) noexcept + -> temporary_file & = delete; + +private: + // The file descriptor for the file. + nihil::fd m_fd; + std::filesystem::path m_path; + + temporary_file(nihil::fd &&fd, std::filesystem::path) noexcept; + temporary_file(nihil::fd &&fd) noexcept; + + friend auto tempfile(tempfile_flags_t flags) + -> std::expected<temporary_file, error>; +}; + +/* + * Create a temporary file and return it. + */ +export [[nodiscard]] auto tempfile(tempfile_flags_t flags = tempfile_none) + -> std::expected<temporary_file, error>; + +} // namespace nihil diff --git a/nihil.posix/test.fd.cc b/nihil.posix/test.fd.cc new file mode 100644 index 0000000..6b6394b --- /dev/null +++ b/nihil.posix/test.fd.cc @@ -0,0 +1,199 @@ +/* + * This source code is released into the public domain. + */ + +#include <span> +#include <stdexcept> + +#include <stdio.h> +#include <fcntl.h> + +#include <catch2/catch_test_macros.hpp> + +import nihil.error; +import nihil.posix; + +using namespace std::literals; + +namespace { + +// Test if an fd is open. +auto fd_is_open(int fd) -> bool { + auto const ret = ::fcntl(fd, F_GETFL); + return ret == 0; +} + +} // anonymous namespace + +TEST_CASE("fd: construct empty", "[fd]") { + nihil::fd fd; + + REQUIRE(!fd); + REQUIRE_THROWS_AS(fd.get(), std::logic_error); +} + +TEST_CASE("fd: construct from fd", "[fd]") { + auto file = ::open("/dev/null", O_RDONLY); + REQUIRE(file > 0); + + { + auto fd = nihil::fd(file); + REQUIRE(fd_is_open(fd.get())); + } + + REQUIRE(!fd_is_open(file)); +} + +TEST_CASE("fd: close", "[fd]") { + auto file = ::open("/dev/null", O_RDONLY); + REQUIRE(file > 0); + + auto fd = nihil::fd(file); + REQUIRE(fd); + + auto const ret = fd.close(); + REQUIRE(ret); + REQUIRE(!fd_is_open(file)); +} + +TEST_CASE("fd: move construct", "[fd]") { + auto file = ::open("/dev/null", O_RDONLY); + REQUIRE(file > 0); + + auto fd1 = nihil::fd(file); + REQUIRE(fd_is_open(fd1.get())); + + auto fd2(std::move(fd1)); + REQUIRE(!fd1); //NOLINT + REQUIRE(fd2); + REQUIRE(fd2.get() == file); + REQUIRE(fd_is_open(file)); +} + +TEST_CASE("fd: move assign", "[fd]") { + auto file = ::open("/dev/null", O_RDONLY); + REQUIRE(file > 0); + + auto fd1 = nihil::fd(file); + REQUIRE(fd_is_open(fd1.get())); + + auto fd2 = nihil::fd(); + REQUIRE(!fd2); + + fd2 = std::move(fd1); + + REQUIRE(!fd1); //NOLINT + REQUIRE(fd2); + REQUIRE(fd2.get() == file); + REQUIRE(fd_is_open(file)); +} + +TEST_CASE("fd: release", "[fd]") { + auto file = ::open("/dev/null", O_RDONLY); + REQUIRE(file > 0); + + auto fd = nihil::fd(file); + auto fdesc = std::move(fd).release(); + REQUIRE(!fd); //NOLINT + REQUIRE(fdesc == file); +} + +TEST_CASE("fd: dup", "[fd]") { + auto file = ::open("/dev/null", O_RDONLY); + REQUIRE(file > 0); + + auto fd = nihil::fd(file); + REQUIRE(fd); + + auto fd2 = dup(fd); + REQUIRE(fd2); + REQUIRE(fd.get() != fd2->get()); +} + +TEST_CASE("fd: dup2", "[fd]") { + auto file = ::open("/dev/null", O_RDONLY); + REQUIRE(file > 0); + + REQUIRE(!fd_is_open(666)); + + auto fd = nihil::fd(file); + auto fd2 = dup(fd, 666); + + REQUIRE(fd); + REQUIRE(fd2); + REQUIRE(fd2->get() == 666); +} + +TEST_CASE("fd: flags", "[fd]") { + auto file = ::open("/dev/null", O_RDONLY); + REQUIRE(file > 0); + + auto fd = nihil::fd(file); + + { + auto const ret = replaceflags(fd, 0); + REQUIRE(ret); + REQUIRE(getflags(fd) == 0); + } + + { + auto const ret = setflags(fd, O_NONBLOCK); + REQUIRE(ret == O_NONBLOCK); + REQUIRE(getflags(fd) == O_NONBLOCK); + } + + { + auto const ret = setflags(fd, O_SYNC); + REQUIRE(ret == (O_NONBLOCK|O_SYNC)); + REQUIRE(getflags(fd) == (O_NONBLOCK|O_SYNC)); + } + + { + auto const ret = clearflags(fd, O_NONBLOCK); + REQUIRE(ret == O_SYNC); + REQUIRE(getflags(fd) == O_SYNC); + } +} + +TEST_CASE("fd: fdflags", "[fd]") { + auto file = ::open("/dev/null", O_RDONLY); + REQUIRE(file > 0); + + auto fd = nihil::fd(file); + + { + auto const ret = replacefdflags(fd, 0); + REQUIRE(ret); + REQUIRE(getfdflags(fd) == 0); + } + + { + auto const ret = setfdflags(fd, FD_CLOEXEC); + REQUIRE(ret == FD_CLOEXEC); + REQUIRE(getfdflags(fd) == FD_CLOEXEC); + } + + { + auto const ret = clearfdflags(fd, FD_CLOEXEC); + REQUIRE(ret == 0); + REQUIRE(getfdflags(fd) == 0); + } +} + +TEST_CASE("fd: pipe, read, write", "[fd]") { + auto fds = nihil::pipe(); + REQUIRE(fds); + + auto [fd1, fd2] = std::move(*fds); + + auto constexpr test_string = "test string"sv; + + auto ret = write(fd1, test_string); + REQUIRE(ret); + REQUIRE(*ret == test_string.size()); + + auto readbuf = std::array<char, test_string.size() * 2>{}; + auto read_buf = read(fd2, readbuf); + REQUIRE(read_buf); + REQUIRE(std::string_view(*read_buf) == test_string); +} diff --git a/nihil.posix/test.getenv.cc b/nihil.posix/test.getenv.cc new file mode 100644 index 0000000..9e10c16 --- /dev/null +++ b/nihil.posix/test.getenv.cc @@ -0,0 +1,50 @@ +/* + * This source code is released into the public domain. + */ + +#include <ranges> +#include <string> +#include <system_error> + +#include <unistd.h> + +#include <catch2/catch_test_macros.hpp> + +import nihil.error; +import nihil.posix; + +TEST_CASE("getenv: existing value", "[getenv]") +{ + auto constexpr *name = "NIHIL_TEST_VAR"; + auto constexpr *value = "test is a test"; + + REQUIRE(::setenv(name, value, 1) == 0); + + auto const s = nihil::getenv(name); + REQUIRE(s); + REQUIRE(*s == value); +} + +TEST_CASE("getenv: non-existing value", "[getenv]") +{ + auto constexpr *name = "NIHIL_TEST_VAR"; + + REQUIRE(::unsetenv(name) == 0); + + auto const s = nihil::getenv(name); + REQUIRE(!s); + REQUIRE(s.error() == std::errc::no_such_file_or_directory); +} + +// Force the call to getenv_r() to reallocate. +TEST_CASE("getenv: long value") +{ + auto constexpr *name = "NIHIL_TEST_VAR"; + auto const value = std::string(4096, 'a'); + + REQUIRE(::setenv(name, value.c_str(), 1) == 0); + + auto const s = nihil::getenv(name); + REQUIRE(s); + REQUIRE(*s == value); +} diff --git a/nihil.posix/test.spawn.cc b/nihil.posix/test.spawn.cc new file mode 100644 index 0000000..da321ff --- /dev/null +++ b/nihil.posix/test.spawn.cc @@ -0,0 +1,117 @@ +/* + * This source code is released into the public domain. + */ + +#include <catch2/catch_test_macros.hpp> + +import nihil.posix; + +TEST_CASE("spawn: system", "[spawn]") +{ + using namespace nihil; + + auto exec = shell("x=1; echo $x"); + REQUIRE(exec); + + auto output = std::string(); + auto capture = make_capture(stdout_fileno, output); + REQUIRE(capture); + + auto proc = spawn(*exec, *capture); + REQUIRE(proc); + + auto status = std::move(*proc).wait(); + REQUIRE(status); + + REQUIRE(status->okay()); + REQUIRE(output == "1\n"); +} + +TEST_CASE("spawn: execv", "[spawn]") { + using namespace nihil; + + auto args = argv({"sh", "-c", "x=1; echo $x"}); + auto exec = execv("/bin/sh", std::move(args)); + REQUIRE(exec); + + auto output = std::string(); + auto capture = make_capture(stdout_fileno, output); + REQUIRE(capture); + + auto proc = spawn(*exec, *capture); + REQUIRE(proc); + + auto status = std::move(*proc).wait(); + REQUIRE(status); + + REQUIRE(status->okay()); + REQUIRE(output == "1\n"); +} + +TEST_CASE("spawn: execvp", "[spawn]") { + using namespace nihil; + + auto args = argv({"sh", "-c", "x=1; echo $x"}); + auto exec = execvp("sh", std::move(args)); + REQUIRE(exec); + + auto output = std::string(); + auto capture = make_capture(stdout_fileno, output); + REQUIRE(capture); + + auto proc = spawn(*exec, *capture); + REQUIRE(proc); + + auto status = std::move(*proc).wait(); + REQUIRE(status); + + REQUIRE(status->okay()); + REQUIRE(output == "1\n"); +} + +TEST_CASE("spawn: execl", "[spawn]") { + using namespace nihil; + + auto exec = execl("/bin/sh", "sh", "-c", "x=1; echo $x"); + REQUIRE(exec); + + auto output = std::string(); + auto capture = make_capture(stdout_fileno, output); + REQUIRE(capture); + + auto proc = spawn(*exec, *capture); + REQUIRE(proc); + + auto status = std::move(*proc).wait(); + REQUIRE(status); + + REQUIRE(status->okay()); + REQUIRE(output == "1\n"); +} + +TEST_CASE("spawn: execlp", "[spawn]") { + using namespace nihil; + + auto exec = execlp("sh", "sh", "-c", "x=1; echo $x"); + REQUIRE(exec); + + auto output = std::string(); + auto capture = make_capture(stdout_fileno, output); + REQUIRE(capture); + + auto proc = spawn(*exec, *capture); + REQUIRE(proc); + + auto status = std::move(*proc).wait(); + REQUIRE(status); + + REQUIRE(status->okay()); + REQUIRE(output == "1\n"); +} + +TEST_CASE("spawn: execlp failure", "[spawn]") { + using namespace nihil; + + auto exec = execlp("nihil_no_such_executable", "x"); + REQUIRE(!exec); +} diff --git a/nihil.posix/test.tempfile.cc b/nihil.posix/test.tempfile.cc new file mode 100644 index 0000000..b1c7604 --- /dev/null +++ b/nihil.posix/test.tempfile.cc @@ -0,0 +1,90 @@ +/* + * This source code is released into the public domain. + */ + +#include <filesystem> + +#include <catch2/catch_test_macros.hpp> + +import nihil.posix; + +TEST_CASE("posix.tempfile: create", "[nihil][nihil.posix]") +{ + auto file = nihil::tempfile(); + REQUIRE(file); + REQUIRE(file->fd()); + + auto path = file->path(); + REQUIRE(exists(path) == true); +} + +TEST_CASE("posix.tempfile: create and release", "[nihil][nihil.posix]") +{ + auto file = nihil::tempfile(); + REQUIRE(file); + REQUIRE(file->fd()); + + auto path = file->path(); + REQUIRE(exists(path) == true); + + file->release(); + REQUIRE(exists(path) == false); + + REQUIRE_THROWS_AS(file->fd(), std::logic_error); + REQUIRE_THROWS_AS(file->path(), std::logic_error); +} + +TEST_CASE("posix.tempfile: create and double release", "[nihil][nihil.posix]") +{ + auto file = nihil::tempfile(); + REQUIRE(file->fd()); + + auto path = file->path(); + REQUIRE(exists(path) == true); + + file->release(); + REQUIRE(exists(path) == false); + + REQUIRE_THROWS_AS(file->fd(), std::logic_error); + REQUIRE_THROWS_AS(file->release(), std::logic_error); + REQUIRE_THROWS_AS(file->path(), std::logic_error); +} + +TEST_CASE("posix.tempfile: create unlinked", "[nihil][nihil.posix]") +{ + auto file = nihil::tempfile(nihil::tempfile_unlink); + REQUIRE(file); + REQUIRE(file->fd()); + + REQUIRE_THROWS_AS(file->path(), std::logic_error); +} + +TEST_CASE("posix.tempfile: create unlinked and release", + "[nihil][nihil.posix]") +{ + auto file = nihil::tempfile(nihil::tempfile_unlink); + REQUIRE(file); + REQUIRE(file->fd()); + + REQUIRE_THROWS_AS(file->path(), std::logic_error); + + file->release(); + + REQUIRE_THROWS_AS(file->fd(), std::logic_error); + REQUIRE_THROWS_AS(file->path(), std::logic_error); +} + +TEST_CASE("posix.tempfile: create unlinked and double release", + "[nihil][nihil.posix]") +{ + auto file = nihil::tempfile(nihil::tempfile_unlink); + REQUIRE(file->fd()); + + REQUIRE_THROWS_AS(file->path(), std::logic_error); + + file->release(); + + REQUIRE_THROWS_AS(file->fd(), std::logic_error); + REQUIRE_THROWS_AS(file->release(), std::logic_error); + REQUIRE_THROWS_AS(file->path(), std::logic_error); +} diff --git a/nihil.posix/write_file.ccm b/nihil.posix/write_file.ccm new file mode 100644 index 0000000..867e0db --- /dev/null +++ b/nihil.posix/write_file.ccm @@ -0,0 +1,82 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <coroutine> +#include <expected> +#include <filesystem> +#include <ranges> +#include <system_error> +#include <vector> + +#include <fcntl.h> +#include <unistd.h> + +export module nihil.posix:write_file; + +import nihil.error; +import nihil.guard; +import nihil.monad; +import :fd; +import :open; +import :rename; + +namespace nihil { + +/* + * Write the contents of a range to a file. Returns the number of bytes + * written. + */ +export [[nodiscard]] +auto write_file(std::filesystem::path const &filename, + std::ranges::contiguous_range auto &&range, + int mode = 0777) + -> std::expected<std::size_t, error> +{ + auto file = co_await open(filename, O_CREAT|O_WRONLY, mode); + auto nbytes = co_await write(file, range); + co_return nbytes; +} + +/* + * Utility wrapper for non-contiguous ranges. + */ +export [[nodiscard]] +auto write_file(std::filesystem::path const &filename, + std::ranges::range auto &&range) + -> std::expected<std::size_t, error> +requires(!std::ranges::contiguous_range<decltype(range)>) +{ + return write_file(filename, std::vector(std::from_range, range)); +} + +/* + * Write the contents of a range to a file safely. The data will be written + * to "<filename>.tmp", and if the write succeeds, the temporary file will be + * renamed to the target filename. If an error occurs, the target file will + * not be modified. + */ +export [[nodiscard]] +auto safe_write_file(std::filesystem::path const &filename, + std::ranges::range auto &&range) + -> std::expected<void, error> +{ + auto tmpfile = filename; + tmpfile.remove_filename(); + tmpfile /= (filename.filename().native() + ".tmp"); + + auto tmpfile_guard = guard([&tmpfile] { + ::unlink(tmpfile.c_str()); + }); + + co_await write_file(tmpfile, range); + co_await nihil::rename(tmpfile, filename); + + tmpfile_guard.release(); + co_return {}; +} + + +} // namespace nihil diff --git a/nihil.ucl/CMakeLists.txt b/nihil.ucl/CMakeLists.txt new file mode 100644 index 0000000..9d8ab3a --- /dev/null +++ b/nihil.ucl/CMakeLists.txt @@ -0,0 +1,46 @@ +# This source code is released into the public domain. + +pkg_check_modules(LIBUCL REQUIRED libucl) + +add_library(nihil.ucl STATIC) +target_link_libraries(nihil.ucl PRIVATE nihil.error nihil.monad) + +target_sources(nihil.ucl + PUBLIC FILE_SET modules TYPE CXX_MODULES FILES + nihil.ucl.ccm + emit.ccm + errc.ccm + object.ccm + object_cast.ccm + parser.ccm + type.ccm + + array.ccm + boolean.ccm + integer.ccm + map.ccm + real.ccm + string.ccm + + PRIVATE + emit.cc + errc.cc + parser.cc + type.cc + + object.cc + boolean.cc + integer.cc + real.cc + string.cc +) + +target_compile_options(nihil.ucl PUBLIC ${LIBUCL_CFLAGS_OTHER}) +target_include_directories(nihil.ucl PUBLIC ${LIBUCL_INCLUDE_DIRS}) +target_link_libraries(nihil.ucl PUBLIC ${LIBUCL_LIBRARIES}) +target_link_directories(nihil.ucl PUBLIC ${LIBUCL_LIBRARY_DIRS}) + +if(NIHIL_TESTS) + add_subdirectory(tests) + enable_testing() +endif() diff --git a/nihil.ucl/array.ccm b/nihil.ucl/array.ccm new file mode 100644 index 0000000..e3730ab --- /dev/null +++ b/nihil.ucl/array.ccm @@ -0,0 +1,468 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <cassert> +#include <cerrno> +#include <cstdint> +#include <cstdlib> +#include <format> +#include <iostream> +#include <string> +#include <system_error> +#include <utility> + +#include <ucl.h> + +export module nihil.ucl:array; + +import :object; + +namespace nihil::ucl { + +export template<datatype T> +struct array; + +export template<datatype T> +struct array_iterator { + using difference_type = std::ptrdiff_t; + using value_type = T; + using reference = T&; + using pointer = T*; + + array_iterator() = default; + + [[nodiscard]] auto operator* (this array_iterator const &self) -> T + { + auto arr = self.get_array(); + if (self.m_idx >= ::ucl_array_size(arr)) + throw std::logic_error( + "nihil::ucl::array_iterator: " + "access past end of array"); + + auto uobj = ::ucl_array_find_index(arr, self.m_idx); + if (uobj == nullptr) + throw std::runtime_error( + "nihil::ucl::array_iterator: " + "failed to fetch UCL array index"); + + return T(nihil::ucl::ref, uobj); + } + + [[nodiscard]] auto operator[] (this array_iterator const &self, + difference_type idx) + -> T + { + return *(self + idx); + } + + auto operator++ (this array_iterator &self) -> array_iterator & + { + auto arr = self.get_array(); + if (self.m_idx == ::ucl_array_size(arr)) + throw std::logic_error( + "nihil::ucl::array_iterator: " + "iterating past end of array"); + + ++self.m_idx; + return self; + } + + auto operator++ (this array_iterator &self, int) -> array_iterator + { + auto copy = self; + ++self; + return copy; + } + + auto operator-- (this array_iterator &self) -> array_iterator& + { + if (self.m_idx == 0) + throw std::logic_error( + "nihil::ucl::array_iterator: " + "iterating before start of array"); + --self.m_idx; + return self; + } + + auto operator-- (this array_iterator &self, int) -> array_iterator + { + auto copy = self; + --self; + return copy; + } + + [[nodiscard]] auto operator== (this array_iterator const &lhs, + array_iterator const &rhs) + -> bool + { + // Empty iterators should compare equal. + if (lhs.m_array == nullptr && rhs.m_array == nullptr) + return true; + + if (lhs.get_array() != rhs.get_array()) + throw std::logic_error( + "nihil::ucl::array_iterator: " + "comparing iterators of different arrays"); + + return lhs.m_idx == rhs.m_idx; + } + + [[nodiscard]] auto operator<=> (this array_iterator const &lhs, + array_iterator const &rhs) + { + // Empty iterators should compare equal. + if (lhs.m_array == nullptr && rhs.m_array == nullptr) + return std::strong_ordering::equal; + + if (lhs.get_array() != rhs.get_array()) + throw std::logic_error( + "nihil::ucl::array_iterator: " + "comparing iterators of different arrays"); + + return lhs.m_idx <=> rhs.m_idx; + } + + auto operator+= (this array_iterator &lhs, difference_type rhs) + -> array_iterator & + { + auto arr = lhs.get_array(); + // m_idx cannot be greater than the array size + auto max_inc = ::ucl_array_size(arr) - lhs.m_idx; + + if (std::cmp_greater(rhs, max_inc)) + throw std::logic_error( + "nihil::ucl::array_iterator: " + "iterating past end of array"); + + lhs.m_idx += rhs; + return lhs; + } + + auto operator-= (this array_iterator &lhs, difference_type rhs) + -> array_iterator & + { + if (std::cmp_greater(rhs, lhs.m_idx)) + throw std::logic_error( + "nihil::ucl::array_iterator: " + "iterating before start of array"); + lhs.m_idx -= rhs; + return lhs; + } + + [[nodiscard]] auto operator- (this array_iterator const &lhs, + array_iterator const &rhs) + -> difference_type + { + if (lhs.get_array() != rhs.get_array()) + throw std::logic_error( + "nihil::ucl::array_iterator: " + "comparing iterators of different arrays"); + + return lhs.m_idx - rhs.m_idx; + } + +private: + friend struct array<T>; + + ::ucl_object_t const * m_array{}; + std::size_t m_idx{}; + + [[nodiscard]] auto get_array(this array_iterator const &self) + -> ::ucl_object_t const * + { + if (self.m_array == nullptr) + throw std::logic_error( + "nihil::ucl::array_iterator: " + "attempt to access an empty iterator"); + + return self.m_array; + } + + array_iterator(::ucl_object_t const *array, std::size_t idx) + : m_array(array) + , m_idx(idx) + {} +}; + +export template<datatype T> [[nodiscard]] +auto operator+(array_iterator<T> const &lhs, + typename array_iterator<T>::difference_type rhs) +-> array_iterator<T> +{ + auto copy = lhs; + copy += rhs; + return copy; +} + +export template<datatype T> [[nodiscard]] +auto operator+(typename array_iterator<T>::difference_type lhs, + array_iterator<T> const &rhs) + -> array_iterator<T> +{ + return rhs - lhs; +} + +export template<datatype T> [[nodiscard]] +auto operator-(array_iterator<T> const &lhs, + typename array_iterator<T>::difference_type rhs) + -> array_iterator<T> +{ + auto copy = lhs; + copy -= rhs; + return copy; +} + +export template<datatype T = object> +struct array final : object { + inline static constexpr object_type ucl_type = object_type::array; + + using value_type = T; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using iterator = array_iterator<T>; + + /* + * Create an empty array. Throws std::system_error on failure. + */ + array() : object(noref, [] { + auto *uobj = ::ucl_object_typed_new(UCL_ARRAY); + if (uobj == nullptr) + throw std::system_error( + std::make_error_code(std::errc(errno))); + return uobj; + }()) + { + } + + /* + * Create an array from a UCL object. Throws type_mismatch + * on failure. + * + * Unlike object_cast<>, this does not check the type of the contained + * elements, which means object access can throw type_mismatch. + */ + array(ref_t, ::ucl_object_t const *uobj) + : object(nihil::ucl::ref, [&] { + auto actual_type = static_cast<object_type>( + ::ucl_object_type(uobj)); + if (actual_type != array::ucl_type) + throw type_mismatch(array::ucl_type, + actual_type); + return uobj; + }()) + { + } + + array(noref_t, ::ucl_object_t *uobj) + : object(nihil::ucl::noref, [&] { + auto actual_type = static_cast<object_type>( + ::ucl_object_type(uobj)); + if (actual_type != array::ucl_type) + throw type_mismatch(array::ucl_type, + actual_type); + return uobj; + }()) + { + } + + /* + * Create an array from an iterator pair. + */ + template<std::input_iterator Iterator> + requires(std::convertible_to<std::iter_value_t<Iterator>, T>) + array(Iterator first, Iterator last) + : array() + { + // This is exception safe, because if we throw here the + // base class destructor will free the array. + while (first != last) { + push_back(*first); + ++first; + } + } + + /* + * Create an array from a range. + */ + template<std::ranges::range Range> + requires(std::convertible_to<std::ranges::range_value_t<Range>, T>) + array(std::from_range_t, Range &&range) + : array(std::ranges::begin(range), + std::ranges::end(range)) + { + } + + /* + * Create an array from an initializer_list. + */ + array(std::initializer_list<T> const &list) + : array(std::ranges::begin(list), + std::ranges::end(list)) + { + } + + /* + * Array iterator access. + */ + + [[nodiscard]] auto begin(this array const &self) -> iterator + { + return {self.get_ucl_object(), 0}; + } + + [[nodiscard]] auto end(this array const &self) -> iterator + { + return {self.get_ucl_object(), self.size()}; + } + + /* + * Return the size of this array. + */ + [[nodiscard]] auto size(this array const &self) -> size_type + { + return ::ucl_array_size(self.get_ucl_object()); + } + + /* + * Test if this array is empty. + */ + [[nodiscard]] auto empty(this array const &self) -> bool + { + return self.size() == 0; + } + + /* + * Reserve space for future insertions. + */ + auto reserve(this array &self, size_type nelems) -> void + { + ::ucl_object_reserve(self.get_ucl_object(), nelems); + } + + /* + * Append an element to the array. + */ + auto push_back(this array &self, value_type const &v) -> void + { + auto uobj = ::ucl_object_ref(v.get_ucl_object()); + ::ucl_array_append(self.get_ucl_object(), uobj); + } + + /* + * Prepend an element to the array. + */ + auto push_front(this array &self, value_type const &v) -> void + { + auto uobj = ::ucl_object_ref(v.get_ucl_object()); + ::ucl_array_prepend(self.get_ucl_object(), uobj); + } + + /* + * Access an array element by index. + */ + [[nodiscard]] auto at(this array const &self, size_type idx) -> T + { + if (idx >= self.size()) + throw std::out_of_range("UCL array index out of range"); + + auto uobj = ::ucl_array_find_index(self.get_ucl_object(), idx); + if (uobj == nullptr) + throw std::runtime_error( + "failed to fetch UCL array index"); + + return T(nihil::ucl::ref, uobj); + } + + [[nodiscard]] auto operator[] (this array const &self, size_type idx) -> T + { + return self.at(idx); + } + + /* + * Return the first element. + */ + [[nodiscard]] auto front(this array const &self) -> T + { + return self.at(0); + } + + /* + * Return the last element. + */ + [[nodiscard]] auto back(this array const &self) -> T + { + if (self.empty()) + throw std::out_of_range("attempt to access back() on " + "empty UCL array"); + return self.at(self.size() - 1); + } +}; + +/* + * Comparison operators. + */ + +export template<datatype T> [[nodiscard]] +auto operator==(array<T> const &a, array<T> const &b) -> bool +{ + if (a.size() != b.size()) + return false; + + for (typename array<T>::size_type i = 0; i < a.size(); ++i) + if (a.at(i) != b.at(i)) + return false; + + return true; +} + +/* + * Print an array to an ostream; uses the same format as std::format(). + */ +export template<datatype T> +auto operator<<(std::ostream &strm, array<T> const &a) -> std::ostream & +{ + return strm << std::format("{}", a); +} + +} // namespace nihil::ucl + +/* + * std::formatter for an array. The output format is a list of values + * on a single line: [1, 2, 3]. + */ +export template<typename T> +struct std::formatter<nihil::ucl::array<T>, char> +{ + template<class ParseContext> + constexpr ParseContext::iterator parse(ParseContext& ctx) + { + return ctx.begin(); + } + + template<class FmtContext> + FmtContext::iterator format(nihil::ucl::array<T> const &o, + FmtContext& ctx) const + { + auto it = ctx.out(); + bool first = true; + + *it++ = '['; + + for (auto &&elm : o) { + if (first) + first = false; + else { + *it++ = ','; + *it++ = ' '; + } + + it = std::format_to(it, "{}", elm); + } + + *it++ = ']'; + return it; + } +}; diff --git a/nihil.ucl/boolean.cc b/nihil.ucl/boolean.cc new file mode 100644 index 0000000..91f2b17 --- /dev/null +++ b/nihil.ucl/boolean.cc @@ -0,0 +1,106 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <compare> +#include <cstdlib> +#include <expected> +#include <system_error> + +#include <ucl.h> + +module nihil.ucl; + +import nihil.error; + +namespace nihil::ucl { + +auto make_boolean(boolean::contained_type value) + -> std::expected<boolean, error> +{ + auto *uobj = ::ucl_object_frombool(value); + if (uobj == nullptr) + return std::unexpected(error( + errc::failed_to_create_object, + error(std::errc(errno)))); + + return boolean(noref, uobj); +} + +boolean::boolean() + : boolean(false) +{ +} + +boolean::boolean(contained_type value) + : object(noref, [&] { + auto *uobj = ::ucl_object_frombool(value); + if (uobj == nullptr) + throw std::system_error( + std::make_error_code(std::errc(errno))); + return uobj; + }()) +{ +} + +boolean::boolean(ref_t, ::ucl_object_t const *uobj) + : object(nihil::ucl::ref, [&] { + auto actual_type = static_cast<object_type>( + ::ucl_object_type(uobj)); + if (actual_type != boolean::ucl_type) + throw type_mismatch(boolean::ucl_type, actual_type); + return uobj; + }()) +{ +} + +boolean::boolean(noref_t, ::ucl_object_t *uobj) + : object(nihil::ucl::noref, [&] { + auto actual_type = static_cast<object_type>( + ::ucl_object_type(uobj)); + if (actual_type != boolean::ucl_type) + throw type_mismatch(boolean::ucl_type, actual_type); + return uobj; + }()) +{ +} + +auto boolean::value(this boolean const &self) + -> contained_type +{ + auto v = contained_type{}; + auto const *uobj = self.get_ucl_object(); + + if (::ucl_object_toboolean_safe(uobj, &v)) + return v; + + std::abort(); +} + +auto operator== (boolean const &a, boolean const &b) + -> bool +{ + return a.value() == b.value(); +} + +auto operator<=> (boolean const &a, boolean const &b) + -> std::strong_ordering +{ + return a.value() <=> b.value(); +} + +auto operator== (boolean const &a, boolean::contained_type b) + -> bool +{ + return a.value() == b; +} + +auto operator<=> (boolean const &a, boolean::contained_type b) + -> std::strong_ordering +{ + return a.value() <=> b; +} + +} // namespace nihil::ucl diff --git a/nihil.ucl/boolean.ccm b/nihil.ucl/boolean.ccm new file mode 100644 index 0000000..068dfdd --- /dev/null +++ b/nihil.ucl/boolean.ccm @@ -0,0 +1,91 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <cassert> +#include <cstdint> +#include <cstdlib> +#include <expected> +#include <format> +#include <string> + +#include <ucl.h> + +export module nihil.ucl:boolean; + +import :object; + +namespace nihil::ucl { + +export struct boolean final : object { + using contained_type = bool; + + inline static constexpr object_type ucl_type = object_type::boolean; + + /* + * Create a boolean holding the value false. Throws std::system_error + * on failure. + */ + boolean(); + + /* + * Create a boolean holding a specific value. Throws std::system_error + * on failure. + */ + explicit boolean(bool); + + /* + * Create a new boolean from a UCL object. Throws type_mismatch + * on failure. + */ + boolean(ref_t, ::ucl_object_t const *uobj); + boolean(noref_t, ::ucl_object_t *uobj); + + // Return this object's value. + auto value(this boolean const &self) -> contained_type; +}; + +/* + * Boolean constructors. These return an error instead of throwing. + */ + +export [[nodiscard]] auto +make_boolean(boolean::contained_type = false) -> std::expected<boolean, error>; + +/* + * Comparison operators. + */ + +export auto operator== (boolean const &a, boolean const &b) -> bool; +export auto operator== (boolean const &a, boolean::contained_type b) -> bool; +export auto operator<=> (boolean const &a, boolean const &b) + -> std::strong_ordering; +export auto operator<=> (boolean const &a, boolean::contained_type b) + -> std::strong_ordering; + +} // namespace nihil::ucl + +/* + * std::formatter for a boolean. This provides the same format operations + * as std::formatter<bool>. + */ +export template<> +struct std::formatter<nihil::ucl::boolean, char> +{ + std::formatter<bool> base_formatter; + + template<class ParseContext> + constexpr ParseContext::iterator parse(ParseContext& ctx) + { + return base_formatter.parse(ctx); + } + + template<class FmtContext> + FmtContext::iterator format(nihil::ucl::boolean const &o, + FmtContext& ctx) const + { + return base_formatter.format(o.value(), ctx); + } +}; diff --git a/nihil.ucl/emit.cc b/nihil.ucl/emit.cc new file mode 100644 index 0000000..480ddd8 --- /dev/null +++ b/nihil.ucl/emit.cc @@ -0,0 +1,21 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <iostream> +#include <iterator> + +module nihil.ucl; + +namespace nihil::ucl { + +auto operator<<(std::ostream &stream, object const &o) +-> std::ostream & +{ + emit(o, emitter::json, std::ostream_iterator<char>(stream)); + return stream; +} + +} // namespace nihil::ucl diff --git a/nihil.ucl/emit.ccm b/nihil.ucl/emit.ccm new file mode 100644 index 0000000..b88f8e7 --- /dev/null +++ b/nihil.ucl/emit.ccm @@ -0,0 +1,209 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <array> +#include <charconv> +#include <cstdlib> +#include <format> +#include <iterator> +#include <iosfwd> +#include <span> +#include <string> +#include <utility> + +#include <ucl.h> + +export module nihil.ucl:emit; + +import :object; + +namespace nihil::ucl { + +export enum struct emitter { + configuration = UCL_EMIT_CONFIG, + compact_json = UCL_EMIT_JSON_COMPACT, + json = UCL_EMIT_JSON, + yaml = UCL_EMIT_YAML, +}; + +/* + * Wrap ucl_emitter_functions for a particular output iterator type. + * + * We can't throw exceptions here since we're called from C code. The emit + * functions return an integer value, but it's not really clear what this is + * for and the C API seems to mostly ignore it. So, we just eat errors and + * keep going. + */ +template<std::output_iterator<char> Iterator> +struct emit_wrapper { + emit_wrapper(Iterator iterator_) + : iterator(std::move(iterator_)) + {} + + static auto append_character(unsigned char c, std::size_t nchars, + void *ud) + noexcept -> int + try { + auto *self = static_cast<emit_wrapper *>(ud); + + while (nchars--) + *self->iterator++ = static_cast<char>(c); + + return 0; + } catch (...) { + return 0; + } + + static auto append_len(unsigned char const *str, std::size_t len, + void *ud) + noexcept -> int + try { + auto *self = static_cast<emit_wrapper *>(ud); + + for (auto c : std::span(str, len)) + *self->iterator++ = static_cast<char>(c); + + return 0; + } catch (...) { + return 0; + } + + static auto append_int(std::int64_t value, void *ud) + noexcept -> int + try { + auto constexpr bufsize = + std::numeric_limits<std::int64_t>::digits10; + auto buf = std::array<char, bufsize>(); + + auto *self = static_cast<emit_wrapper *>(ud); + auto result = std::to_chars(buf.data(), buf.data() + buf.size(), + value, 10); + + if (result.ec == std::errc()) + for (auto c : std::span(buf.data(), result.ptr)) + *self->iterator++ = c; + + return 0; + } catch (...) { + return 0; + } + + static auto append_double(double value, void *ud) + noexcept -> int + try { + auto constexpr bufsize = + std::numeric_limits<double>::digits10; + auto buf = std::array<char, bufsize>(); + + auto *self = static_cast<emit_wrapper *>(ud); + auto result = std::to_chars(buf.data(), buf.data() + buf.size(), + value); + + if (result.ec == std::errc()) + for (auto c : std::span(buf.data(), result.ptr)) + *self->iterator++ = c; + + return 0; + } catch (...) { + return 0; + } + + auto get_functions(this emit_wrapper &self) -> ucl_emitter_functions + { + auto ret = ucl_emitter_functions{}; + + ret.ucl_emitter_append_character = &emit_wrapper::append_character; + ret.ucl_emitter_append_len = &emit_wrapper::append_len; + ret.ucl_emitter_append_int = &emit_wrapper::append_int; + ret.ucl_emitter_append_double = &emit_wrapper::append_double; + ret.ud = &self; + + return ret; + } + +private: + Iterator iterator{}; +}; + +export auto emit(object const &object, emitter format, + std::output_iterator<char> auto &&it) + -> void +{ + auto ucl_format = static_cast<ucl_emitter>(format); + auto wrapper = emit_wrapper(it); + auto functions = wrapper.get_functions(); + + ::ucl_object_emit_full(object.get_ucl_object(), ucl_format, + &functions, nullptr); +} + +/* + * Basic ostream printer for UCL; default to JSON since it's probably what + * most people expect. + */ +export auto operator<<(std::ostream &, object const &) -> std::ostream &; + +} // namespace nihil::ucl + +/* + * Specialisation of std::formatter<> for object. + */ +template<std::derived_from<nihil::ucl::object> T> +struct std::formatter<T, char> +{ + nihil::ucl::emitter emitter = nihil::ucl::emitter::json; + + template<class ParseContext> + constexpr ParseContext::iterator parse(ParseContext& ctx) + { + auto it = ctx.begin(); + auto end = ctx.end(); + + while (it != end) { + switch (*it) { + case 'j': + emitter = nihil::ucl::emitter::json; + break; + case 'J': + emitter = nihil::ucl::emitter::compact_json; + break; + case 'c': + emitter = nihil::ucl::emitter::configuration; + break; + case 'y': + emitter = nihil::ucl::emitter::yaml; + break; + case '}': + return it; + default: + throw std::format_error("Invalid format string " + "for UCL object"); + } + + ++it; + } + + return it; + } + + template<class FmtContext> + FmtContext::iterator format(nihil::ucl::object const &o, + FmtContext& ctx) const + { + // We can't use emit() here since the context iterator is not + // an std::output_iterator. + + auto out = ctx.out(); + + auto ucl_format = static_cast<::ucl_emitter>(emitter); + auto wrapper = nihil::ucl::emit_wrapper(out); + auto functions = wrapper.get_functions(); + + ::ucl_object_emit_full(o.get_ucl_object(), ucl_format, + &functions, nullptr); + return out; + } +}; diff --git a/nihil.ucl/errc.cc b/nihil.ucl/errc.cc new file mode 100644 index 0000000..0b65b86 --- /dev/null +++ b/nihil.ucl/errc.cc @@ -0,0 +1,49 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <string> +#include <system_error> + +module nihil.ucl; + +namespace nihil::ucl { + +struct ucl_error_category final : std::error_category { + auto name() const noexcept -> char const * override; + auto message(int err) const -> std::string override; +}; + +auto ucl_category() noexcept -> std::error_category & +{ + static auto category = ucl_error_category(); + return category; +} + +auto make_error_condition(errc ec) -> std::error_condition +{ + return {static_cast<int>(ec), ucl_category()}; +} + +auto ucl_error_category::name() const noexcept -> char const * +{ + return "nihil.ucl"; +} + +auto ucl_error_category::message(int err) const -> std::string +{ + switch (static_cast<errc>(err)) { + case errc::no_error: + return "No error"; + case errc::failed_to_create_object: + return "Failed to create UCL object"; + case errc::type_mismatch: + return "UCL type does not match expected type"; + default: + return "Undefined error"; + } +} + +} // namespace nihil::ucl diff --git a/nihil.ucl/errc.ccm b/nihil.ucl/errc.ccm new file mode 100644 index 0000000..8f0444d --- /dev/null +++ b/nihil.ucl/errc.ccm @@ -0,0 +1,33 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <string> +#include <system_error> + +export module nihil.ucl:errc; + +namespace nihil::ucl { + +export enum struct errc { + no_error = 0, + + // ucl_object_new() or similar failed, e.g. out of memory + failed_to_create_object, + // Trying to create an object from a UCL object of the wrong type + type_mismatch, +}; + +export auto ucl_category() noexcept -> std::error_category &; +export auto make_error_condition(errc ec) -> std::error_condition; + +} // namespace nihil::ucl + +namespace std { + +export template<> +struct is_error_condition_enum<nihil::ucl::errc> : true_type {}; + +} // namespace std diff --git a/nihil.ucl/integer.cc b/nihil.ucl/integer.cc new file mode 100644 index 0000000..825d8f6 --- /dev/null +++ b/nihil.ucl/integer.cc @@ -0,0 +1,102 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <compare> +#include <cstdlib> +#include <expected> +#include <system_error> + +#include <ucl.h> + +module nihil.ucl; + +import nihil.error; + +namespace nihil::ucl { + +integer::integer() + : integer(0) +{ +} + +integer::integer(contained_type value) + : integer(noref, [&] { + auto *uobj = ::ucl_object_fromint(value); + if (uobj == nullptr) + throw std::system_error( + std::make_error_code(std::errc(errno))); + return uobj; + }()) +{ +} + +integer::integer(ref_t, ::ucl_object_t const *uobj) + : object(nihil::ucl::ref, [&] { + auto actual_type = static_cast<object_type>( + ::ucl_object_type(uobj)); + if (actual_type != integer::ucl_type) + throw type_mismatch(integer::ucl_type, actual_type); + return uobj; + }()) +{ +} + +integer::integer(noref_t, ::ucl_object_t *uobj) + : object(nihil::ucl::noref, [&] { + auto actual_type = static_cast<object_type>( + ::ucl_object_type(uobj)); + if (actual_type != integer::ucl_type) + throw type_mismatch(integer::ucl_type, actual_type); + return uobj; + }()) +{ +} + +auto make_integer(integer::contained_type value) + -> std::expected<integer, error> +{ + auto *uobj = ::ucl_object_fromint(value); + if (uobj == nullptr) + return std::unexpected(error( + errc::failed_to_create_object, + error(std::errc(errno)))); + + return integer(noref, uobj); +} + +auto integer::value(this integer const &self) -> contained_type +{ + auto v = contained_type{}; + auto const *uobj = self.get_ucl_object(); + + if (::ucl_object_toint_safe(uobj, &v)) + return v; + + std::abort(); +} + +auto operator== (integer const &a, integer const &b) -> bool +{ + return a.value() == b.value(); +} + +auto operator<=> (integer const &a, integer const &b) -> std::strong_ordering +{ + return a.value() <=> b.value(); +} + +auto operator== (integer const &a, integer::contained_type b) -> bool +{ + return a.value() == b; +} + +auto operator<=> (integer const &a, integer::contained_type b) + -> std::strong_ordering +{ + return a.value() <=> b; +} + +} // namespace nihil::ucl diff --git a/nihil.ucl/integer.ccm b/nihil.ucl/integer.ccm new file mode 100644 index 0000000..e35a471 --- /dev/null +++ b/nihil.ucl/integer.ccm @@ -0,0 +1,115 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <compare> +#include <cstdint> +#include <cstdlib> +#include <expected> +#include <format> +#include <utility> + +#include <ucl.h> + +export module nihil.ucl:integer; + +import :object; +import :type; + +namespace nihil::ucl { + +export struct integer final : object { + using contained_type = std::int64_t; + inline static constexpr object_type ucl_type = object_type::integer; + + /* + * Create an integer holding the value 0. Throws std::system_error + * on failure. + */ + integer(); + + /* + * Create an integer holding a specific value. Throws std::system_error + * on failure. + */ + explicit integer(contained_type value); + + /* + * Create a new integer from a UCL object. Throws type_mismatch + * on failure. + */ + integer(ref_t, ::ucl_object_t const *uobj); + integer(noref_t, ::ucl_object_t *uobj); + + // Return the value of this object. + [[nodiscard]] auto value(this integer const &self) -> contained_type; +}; + +/* + * Integer constructors. These return an error instead of throwing. + */ + +export [[nodiscard]] auto +make_integer(integer::contained_type = 0) -> std::expected<integer, error>; + +/* + * Comparison operators. + */ + +export [[nodiscard]] auto operator== (integer const &a, + integer const &b) -> bool; + +export [[nodiscard]] auto operator== (integer const &a, + integer::contained_type b) -> bool; + +export [[nodiscard]] auto operator<=> (integer const &a, + integer const &b) + -> std::strong_ordering; + +export [[nodiscard]] auto operator<=> (integer const &a, + integer::contained_type b) + -> std::strong_ordering; + +/* + * Literal operator. + */ +inline namespace literals { +export constexpr auto operator""_ucl (unsigned long long i) -> integer +{ + if (std::cmp_greater(i, std::numeric_limits<std::int64_t>::max())) + throw std::out_of_range("literal out of range"); + + return integer(static_cast<std::int64_t>(i)); +} +} // namespace nihil::ucl::literals + +} // namespace nihil::ucl + +namespace nihil { inline namespace literals { + export using namespace ::nihil::ucl::literals; +}} // namespace nihil::literals + +/* + * std::formatter for an integer. This provides the same format operations + * as std::formatter<std::int64_t>. + */ +export template<> +struct std::formatter<nihil::ucl::integer, char> +{ + std::formatter<std::int64_t> base_formatter; + + template<class ParseContext> + constexpr ParseContext::iterator parse(ParseContext& ctx) + { + return base_formatter.parse(ctx); + } + + template<class FmtContext> + FmtContext::iterator format(nihil::ucl::integer const &o, + FmtContext& ctx) const + { + return base_formatter.format(o.value(), ctx); + } +}; diff --git a/nihil.ucl/map.ccm b/nihil.ucl/map.ccm new file mode 100644 index 0000000..fa77601 --- /dev/null +++ b/nihil.ucl/map.ccm @@ -0,0 +1,293 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <cassert> +#include <cstdint> +#include <cstdlib> +#include <format> +#include <memory> +#include <optional> +#include <string> +#include <system_error> + +#include <ucl.h> + +export module nihil.ucl:map; + +import :object; + +namespace nihil::ucl { + +// Exception thrown when map::operator[] does not find the key. +export struct key_not_found : error { + key_not_found(std::string_view key) + : error(std::format("key '{}' not found in map", key)) + , m_key(key) + {} + + auto key(this key_not_found const &self) -> std::string_view + { + return self.m_key; + } + +private: + std::string m_key; +}; + +export template<datatype T> +struct map; + +template<datatype T> +struct map_iterator { + using difference_type = std::ptrdiff_t; + using value_type = std::pair<std::string_view, T>; + using reference = value_type &; + using const_reference = value_type const &; + using pointer = value_type *; + using const_pointer = value_type const *; + + struct sentinel{}; + + [[nodiscard]] auto operator==(this map_iterator const &self, sentinel) + -> bool + { + return (self.m_state->cur == nullptr); + } + + auto operator++(this map_iterator &self) -> map_iterator & + { + self.m_state->next(); + return self; + } + + auto operator++(this map_iterator &self, int) -> map_iterator & + { + self.m_state->next(); + return self; + } + + [[nodiscard]] auto operator*(this map_iterator const &self) + -> value_type + { + auto obj = T(ref, self.m_state->cur); + return {obj.key(), std::move(obj)}; + } + +private: + friend struct map<T>; + + map_iterator(::ucl_object_t const *obj) + : m_state(std::make_shared<state>(obj)) + { + ++(*this); + } + + struct state { + state(::ucl_object_t const *obj) + { + iter = ::ucl_object_iterate_new(obj); + if (iter == nullptr) + throw std::system_error(make_error_code( + std::errc(errno))); + } + + state(state const &) = delete; + auto operator=(this state &, state const &) -> state& = delete; + + ~state() + { + if (iter != nullptr) + ::ucl_object_iterate_free(iter); + } + + auto next() -> void + { + cur = ::ucl_object_iterate_safe(iter, true); + } + + ucl_object_iter_t iter = nullptr; + ucl_object_t const *cur = nullptr; + }; + + std::shared_ptr<state> m_state; +}; + +export template<datatype T = object> +struct map final : object { + inline static constexpr object_type ucl_type = object_type::object; + + using value_type = std::pair<std::string_view, T>; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using iterator = map_iterator<T>; + + /* + * Create an empty map. Throws std::system_error on failure. + */ + map() : object(noref, [] { + auto *uobj = ::ucl_object_typed_new(UCL_OBJECT); + if (uobj == nullptr) + throw std::system_error( + std::make_error_code(std::errc(errno))); + return uobj; + }()) + { + } + + /* + * Create a map from a UCL object. Throws type_mismatch on failure. + * + * Unlike object_cast<>, this does not check the type of the contained + * elements, which means object access can throw type_mismatch. + */ + map(ref_t, ::ucl_object_t const *uobj) + : object(nihil::ucl::ref, [&] { + auto actual_type = static_cast<object_type>( + ::ucl_object_type(uobj)); + if (actual_type != map::ucl_type) + throw type_mismatch(map::ucl_type, + actual_type); + return uobj; + }()) + { + if (type() != ucl_type) + throw type_mismatch(ucl_type, type()); + } + + map(noref_t, ::ucl_object_t *uobj) + : object(nihil::ucl::noref, [&] { + auto actual_type = static_cast<object_type>( + ::ucl_object_type(uobj)); + if (actual_type != map::ucl_type) + throw type_mismatch(map::ucl_type, + actual_type); + return uobj; + }()) + { + } + + /* + * Create a map from an iterator pair. + */ + template<std::input_iterator Iterator> + requires(std::convertible_to<std::iter_value_t<Iterator>, value_type>) + map(Iterator first, Iterator last) + : map() + { + // This is exception safe, because if we throw here the + // base class destructor will free the map. + while (first != last) { + insert(*first); + ++first; + } + } + + /* + * Create a map from a range. + */ + template<std::ranges::range Range> + requires(std::convertible_to<std::ranges::range_value_t<Range>, + value_type>) + map(std::from_range_t, Range &&range) + : map(std::ranges::begin(range), + std::ranges::end(range)) + { + } + + /* + * Create a map from an initializer_list. + */ + map(std::initializer_list<value_type> const &list) + : map(std::ranges::begin(list), std::ranges::end(list)) + { + } + + /* + * Map iterator access. + */ + + [[nodiscard]] auto begin(this map const &self) -> iterator + { + return {self.get_ucl_object()}; + } + + [[nodiscard]] auto end(this map const &) -> iterator::sentinel + { + return {}; + } + + /* + * Reserve space for future insertions. + */ + auto reserve(this map &self, size_type nelems) -> void + { + ::ucl_object_reserve(self.get_ucl_object(), nelems); + } + + /* + * Add an element to the map. + */ + auto insert(this map &self, value_type const &v) -> void + { + auto uobj = ::ucl_object_ref(v.second.get_ucl_object()); + + ::ucl_object_insert_key(self.get_ucl_object(), uobj, + v.first.data(), v.first.size(), true); + } + + /* + * Access a map element by key. + */ + [[nodiscard]] auto find(this map const &self, std::string_view key) + -> std::optional<T> + { + auto const *obj = ::ucl_object_lookup_len( + self.get_ucl_object(), + key.data(), key.size()); + if (obj == nullptr) + return {}; + + return {T(nihil::ucl::ref, obj)}; + } + + /* + * Remove an object from the map. + */ + auto remove(this map &self, std::string_view key) -> bool + { + return ::ucl_object_delete_keyl(self.get_ucl_object(), + key.data(), key.size()); + } + + /* + * Remove an object from the map and return it. + */ + auto pop(this map &self, std::string_view key) + -> std::optional<T> + { + auto *uobj = ::ucl_object_pop_keyl(self.get_ucl_object(), + key.data(), key.size()); + if (uobj) + return T(noref, uobj); + return {}; + } + + /* + * Equivalent to find(), except it throws key_not_found if the key + * doesn't exist in the map. + */ + [[nodiscard]] auto operator[] (this map const &self, + std::string_view key) + -> T + { + auto obj = self.find(key); + if (obj) + return *obj; + throw key_not_found(key); + } +}; + +} // namespace nihil::ucl diff --git a/nihil.ucl/nihil.ucl.ccm b/nihil.ucl/nihil.ucl.ccm new file mode 100644 index 0000000..b16eb3d --- /dev/null +++ b/nihil.ucl/nihil.ucl.ccm @@ -0,0 +1,21 @@ +/* + * This source code is released into the public domain. + */ + +module; + +export module nihil.ucl; + +export import :emit; +export import :errc; +export import :object; +export import :object_cast; +export import :parser; +export import :type; + +export import :array; +export import :boolean; +export import :integer; +export import :map; +export import :real; +export import :string; diff --git a/nihil.ucl/object.cc b/nihil.ucl/object.cc new file mode 100644 index 0000000..53fc4c7 --- /dev/null +++ b/nihil.ucl/object.cc @@ -0,0 +1,114 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <cstdlib> +#include <string> +#include <utility> + +#include <ucl.h> + +module nihil.ucl; + +namespace nihil::ucl { + +object::object(ref_t, ::ucl_object_t const *object) + : m_object(::ucl_object_ref(object)) +{ +} + +object::object(noref_t, ::ucl_object_t *object) + : m_object(object) +{ +} + +object::~object() { + if (m_object != nullptr) + ::ucl_object_unref(m_object); +} + +object::object(object &&other) noexcept + : m_object(std::exchange(other.m_object, nullptr)) +{} + +object::object(object const &other) + : m_object(nullptr) +{ + m_object = ::ucl_object_copy(other.get_ucl_object()); + if (m_object == nullptr) + throw std::runtime_error("failed to copy UCL object"); +} + +auto object::operator=(this object &self, object &&other) noexcept + -> object & +{ + if (&self != &other) + self.m_object = std::exchange(other.m_object, nullptr); + return self; +} + +auto object::operator=(this object &self, object const &other) -> object & +{ + return self = object(other); +} + +auto object::ref(this object const &self) -> object +{ + return object(nihil::ucl::ref, self.get_ucl_object()); +} + +auto object::type(this object const &self) -> object_type +{ + auto utype = ::ucl_object_type(self.get_ucl_object()); + return static_cast<object_type>(utype); +} + +auto object::get_ucl_object(this object &self) -> ::ucl_object_t * +{ + if (self.m_object == nullptr) + throw std::logic_error("attempt to access empty UCL object"); + return self.m_object; +} + +auto object::get_ucl_object(this object const &self) -> ::ucl_object_t const * +{ + if (self.m_object == nullptr) + throw std::logic_error("attempt to access empty UCL object"); + return self.m_object; +} + +// Return the key of this object. +auto object::key(this object const &self) -> std::string_view +{ + auto dlen = std::size_t{}; + auto const *dptr = ::ucl_object_keyl(self.get_ucl_object(), + &dlen); + return {dptr, dlen}; +} + +auto swap(object &a, object &b) -> void +{ + std::swap(a.m_object, b.m_object); +} + +auto operator<=>(object const &lhs, object const &rhs) -> std::strong_ordering +{ + auto cmp = ::ucl_object_compare(lhs.get_ucl_object(), + rhs.get_ucl_object()); + + if (cmp < 0) + return std::strong_ordering::less; + else if (cmp > 0) + return std::strong_ordering::greater; + else + return std::strong_ordering::equal; +} + +auto operator==(object const &lhs, object const &rhs) -> bool +{ + return (lhs <=> rhs) == std::strong_ordering::equal; +} + +} // namespace nihil::ucl diff --git a/nihil.ucl/object.ccm b/nihil.ucl/object.ccm new file mode 100644 index 0000000..9a7eaf7 --- /dev/null +++ b/nihil.ucl/object.ccm @@ -0,0 +1,88 @@ +/* + * This source code is released into the public domain. + */ + +module; + +/* + * A UCL object. The object is immutable and internally refcounted, so it + * may be copied as needed. + * + */ + +#include <compare> +#include <cstddef> +#include <string> + +#include <ucl.h> + +export module nihil.ucl:object; + +import :type; + +namespace nihil::ucl { + +/*********************************************************************** + * The basic object type. + */ + +// Ref the UCL object when creating an object. +export inline constexpr struct ref_t {} ref; +// Don't ref the UCL object. +export inline constexpr struct noref_t {} noref; + +export struct object { + inline static constexpr object_type ucl_type = object_type::object; + + // Create an object from an existing ucl_object_t. The first argument + // determines whether we ref the object or not. + + object(ref_t, ::ucl_object_t const *object); + object(noref_t, ::ucl_object_t *object); + + // Free our object on destruction. + virtual ~object(); + + // Movable. + object(object &&other) noexcept; + auto operator=(this object &self, object &&other) noexcept -> object&; + + // Copyable. + // Note that this copies the entire UCL object. + object(object const &other); + auto operator=(this object &self, object const &other) -> object &; + + // Increase the refcount of this object. + [[nodiscard]] auto ref(this object const &self) -> object; + + // Return the type of this object. + [[nodiscard]] auto type(this object const &self) -> object_type; + + // Return the underlying object. + [[nodiscard]] auto get_ucl_object(this object &self) + -> ::ucl_object_t *; + + [[nodiscard]] auto get_ucl_object(this object const &self) + -> ::ucl_object_t const *; + + // Return the key of this object. + [[nodiscard]] auto key(this object const &self) -> std::string_view; + +protected: + // The object we're wrapping. + ::ucl_object_t *m_object = nullptr; + + friend auto swap(object &a, object &b) -> void; +}; + +/*********************************************************************** + * Object comparison. + */ + +export [[nodiscard]] auto operator==(object const &lhs, object const &rhs) + -> bool; + +export [[nodiscard]] auto operator<=>(object const &lhs, object const &rhs) + -> std::strong_ordering; + +} // namespace nihil::ucl diff --git a/nihil.ucl/object_cast.ccm b/nihil.ucl/object_cast.ccm new file mode 100644 index 0000000..3fa9eba --- /dev/null +++ b/nihil.ucl/object_cast.ccm @@ -0,0 +1,89 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <coroutine> +#include <cstdlib> +#include <expected> + +#include <ucl.h> + +export module nihil.ucl:object_cast; + +import nihil.monad; +import :type; +import :object; +import :array; + +namespace nihil::ucl { + +/* + * Ensure a UCL object is convertible to another type. Throws type_mismatch + * if not. + */ + +// Implementation for basic types. +template<datatype To> +struct convert_check +{ + [[nodiscard]] auto check(::ucl_object_t const *from) + -> std::expected<void, type_mismatch> + { + auto from_type = static_cast<object_type>(::ucl_object_type(from)); + auto to_type = To::ucl_type; + + // Converting from anything to object is permitted. + if (to_type == object_type::object) + return {}; + + // Converting between two equal types is permitted. + if (from_type == to_type) + return {}; + + // Otherwise, this is an error. + return std::unexpected(type_mismatch(to_type, from_type)); + } +}; + +// Implementation for array. +template<typename T> +struct convert_check<array<T>> +{ + [[nodiscard]] auto check(::ucl_object_t const *from) + -> std::expected<void, type_mismatch> + { + using To = array<T>; + auto from_type = static_cast<object_type>(::ucl_object_type(from)); + auto to_type = To::ucl_type; + + // If the source type is not an array, this is an error. + if (from_type != object_type::array) + co_return std::unexpected( + type_mismatch(to_type, from_type)); + + for (std::size_t i = 0, size = ::ucl_array_size(from); + i < size; ++i) { + auto const *arr_obj = ::ucl_array_find_index(from, i); + co_await convert_check<typename To::value_type>{} + .check(arr_obj); + } + + co_return {}; + } +}; + +/* + * Convert a UCL object to another type. + */ +export template<datatype To> +auto object_cast(object const &from) -> std::expected<To, type_mismatch> +{ + auto uobj = from.get_ucl_object(); + + co_await convert_check<To>{}.check(uobj); + co_return To(nihil::ucl::ref, uobj); +} + +} // namespace nihil::ucl diff --git a/nihil.ucl/parser.cc b/nihil.ucl/parser.cc new file mode 100644 index 0000000..0a08670 --- /dev/null +++ b/nihil.ucl/parser.cc @@ -0,0 +1,102 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <expected> +#include <functional> +#include <string> + +#include <ucl.h> + +module nihil.ucl; + +import nihil.error; + +namespace nihil::ucl { + +auto make_parser(int flags) -> std::expected<parser, error> +{ + auto *p = ::ucl_parser_new(flags); + if (p != nullptr) + return p; + + // TODO: Is there a way to get the actual error here? + return std::unexpected(error("failed to create parser")); +} + +auto macro_handler::handle(unsigned char const *data, + std::size_t len, void *ud) + -> bool +{ + auto handler = static_cast<macro_handler *>(ud); + auto string = std::string_view( + reinterpret_cast<char const *>(data), + len); + return handler->callback(string); +} + +parser::parser(::ucl_parser *uclp) + : m_parser(uclp) +{ +} + +parser::~parser() +{ + if (m_parser) + ::ucl_parser_free(m_parser); +} + +parser::parser(parser &&other) noexcept + : m_parser(std::exchange(other.m_parser, nullptr)) + , m_macros(std::move(other.m_macros)) +{ +} + +auto parser::operator=(this parser &self, parser &&other) noexcept + -> parser & +{ + if (&self != &other) { + if (self.m_parser) + ::ucl_parser_free(self.m_parser); + + self.m_parser = std::exchange(other.m_parser, nullptr); + self.m_macros = std::move(other.m_macros); + } + + return self; +} + +auto parser::register_value( + this parser &self, + std::string_view variable, + std::string_view value) + -> void +{ + ::ucl_parser_register_variable( + self.get_parser(), + std::string(variable).c_str(), + std::string(value).c_str()); +} + +auto parser::top(this parser &self) -> map<object> +{ + auto obj = ::ucl_parser_get_object(self.get_parser()); + if (obj != nullptr) + // ucl_parser_get_object() refs the object for us. + return {noref, obj}; + + throw std::logic_error( + "attempt to call top() on an invalid ucl::parser"); +} + +auto parser::get_parser(this parser &self) -> ::ucl_parser * +{ + if (self.m_parser == nullptr) + throw std::logic_error("attempt to fetch a null ucl::parser"); + + return self.m_parser; +} + +} // namespace nihil::ucl diff --git a/nihil.ucl/parser.ccm b/nihil.ucl/parser.ccm new file mode 100644 index 0000000..5fa3495 --- /dev/null +++ b/nihil.ucl/parser.ccm @@ -0,0 +1,160 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <coroutine> +#include <expected> +#include <format> +#include <functional> +#include <memory> +#include <string> +#include <vector> + +#include <ucl.h> + +export module nihil.ucl:parser; + +import nihil.monad; +import :object; +import :map; + +namespace nihil::ucl { + +// UCL parser flags. +export inline constexpr int parser_key_lower = UCL_PARSER_KEY_LOWERCASE; +export inline constexpr int parser_zerocopy = UCL_PARSER_ZEROCOPY; +export inline constexpr int parser_no_time = UCL_PARSER_NO_TIME; + +// A macro handler. This proxies the C API callback to the C++ API. +using macro_callback_t = bool (std::string_view); + +struct macro_handler { + std::function<macro_callback_t> callback; + + // Handle a callback from the C API. + static auto handle( + unsigned char const *data, + std::size_t len, void + *ud) + -> bool; +}; + +/* + * A UCL parser. This wraps the C ucl_parser API. + * + * parser itself is not exported; use make_parser() to create one. + */ +struct parser { + // Create a parser from a UCL parser. + parser(::ucl_parser *); + + // Destroy our parser when we're destroyed. + ~parser(); + + // Not copyable. + parser(parser const &) = delete; + auto operator=(this parser &, parser const &) -> parser & = delete; + + // Movable. + parser(parser &&) noexcept; + auto operator=(this parser &, parser &&) noexcept -> parser &; + + // Add a parser macro. Unlike ucl_parser_register_macro, this doesn't + // take a userdata parameter; it's assumed the user will use lambda + // capture or similar if needed. + template<std::invocable<std::string_view> F> + auto register_macro(this parser &self, + std::string_view name, + F &&func) + -> void + requires (std::same_as<bool, std::invoke_result<F>>) + { + auto handler = std::make_unique<macro_handler>( + std::forward<F>(func)); + + auto cname = std::string(name); + ::ucl_parser_register_macro( + self.get_parser(), cname.c_str(), + ¯o_handler::handle, handler.get()); + + self.m_macros.emplace_back(std::move(handler)); + } + + // Add a parser variable. + auto register_value(this parser &self, + std::string_view variable, + std::string_view value) + -> void; + + // Add data to the parser. + [[nodiscard]] auto add(this parser &self, + std::ranges::contiguous_range auto &&data) + -> std::expected<void, error> + // Only bytes (chars) are permitted. + requires(sizeof(std::ranges::range_value_t<decltype(data)>) == 1) + { + auto *p = self.get_parser(); + auto dptr = reinterpret_cast<unsigned char const *>( + std::ranges::data(data)); + + auto ret = ::ucl_parser_add_chunk( + p, dptr, std::ranges::size(data)); + + if (ret == true) + return {}; + + return std::unexpected(error(::ucl_parser_get_error(p))); + } + + [[nodiscard]] auto add(this parser &self, + std::ranges::range auto &&data) + -> std::expected<void, error> + requires (!std::ranges::contiguous_range<decltype(data)>) + { + auto cdata = std::vector<char>( + std::from_range, + std::forward<decltype(data)>(data)); + co_await self.add(std::move(cdata)); + co_return {}; + } + + // Return the top object of this parser. + [[nodiscard]] auto top(this parser &self) -> map<object>; + + // Return the stored parser object. + [[nodiscard]] auto get_parser(this parser &self) -> ::ucl_parser *; + +private: + // The parser object. Should never be null, unless we've been + // moved-from. + ucl_parser *m_parser; + + // Functions added by register_macro. We have to store these as + // pointers because we pass the address to libucl. + std::vector<std::unique_ptr<macro_handler>> m_macros; +}; + +// Create a parser with the given flags. +export [[nodiscard]] auto +make_parser(int flags = 0) -> std::expected<parser, error>; + +// Utility function to parse something and return the top-level object. +export [[nodiscard]] auto +parse(int flags, std::ranges::range auto &&data) + -> std::expected<map<object>, error> +{ + auto p = co_await make_parser(flags); + co_await p.add(std::forward<decltype(data)>(data)); + co_return p.top(); +} + +export [[nodiscard]] auto +parse(std::ranges::range auto &&data) + -> std::expected<map<object>, error> +{ + co_return co_await parse(0, std::forward<decltype(data)>(data)); +} + +} // namespace nihil::ucl diff --git a/nihil.ucl/real.cc b/nihil.ucl/real.cc new file mode 100644 index 0000000..6d9e082 --- /dev/null +++ b/nihil.ucl/real.cc @@ -0,0 +1,104 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <cassert> +#include <compare> +#include <cstdlib> +#include <expected> +#include <string> +#include <system_error> + +#include <ucl.h> + +module nihil.ucl; + +import nihil.error; + +namespace nihil::ucl { + +auto make_real(real::contained_type value) + -> std::expected<real, error> +{ + auto *uobj = ::ucl_object_fromdouble(value); + if (uobj == nullptr) + return std::unexpected(error( + errc::failed_to_create_object, + error(std::errc(errno)))); + + return real(noref, uobj); +} + +real::real() + : real(0) +{ +} + +real::real(contained_type value) + : real(noref, [&] { + auto *uobj = ::ucl_object_fromdouble(value); + if (uobj == nullptr) + throw std::system_error( + std::make_error_code(std::errc(errno))); + return uobj; + }()) +{ +} + +real::real(ref_t, ::ucl_object_t const *uobj) + : object(nihil::ucl::ref, [&] { + auto actual_type = static_cast<object_type>( + ::ucl_object_type(uobj)); + if (actual_type != real::ucl_type) + throw type_mismatch(real::ucl_type, actual_type); + return uobj; + }()) +{ +} + +real::real(noref_t, ::ucl_object_t *uobj) + : object(nihil::ucl::noref, [&] { + auto actual_type = static_cast<object_type>( + ::ucl_object_type(uobj)); + if (actual_type != real::ucl_type) + throw type_mismatch(real::ucl_type, actual_type); + return uobj; + }()) +{ +} + +auto real::value(this real const &self) -> contained_type +{ + auto v = contained_type{}; + auto const *uobj = self.get_ucl_object(); + + if (::ucl_object_todouble_safe(uobj, &v)) + return v; + + std::abort(); +} + +auto operator== (real const &a, real const &b) -> bool +{ + return a.value() == b.value(); +} + +auto operator<=> (real const &a, real const &b) -> std::partial_ordering +{ + return a.value() <=> b.value(); +} + +auto operator== (real const &a, real::contained_type b) -> bool +{ + return a.value() == b; +} + +auto operator<=> (real const &a, real::contained_type b) + -> std::partial_ordering +{ + return a.value() <=> b; +} + +} // namespace nihil::ucl diff --git a/nihil.ucl/real.ccm b/nihil.ucl/real.ccm new file mode 100644 index 0000000..f425a9a --- /dev/null +++ b/nihil.ucl/real.ccm @@ -0,0 +1,112 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <compare> +#include <expected> +#include <format> +#include <utility> + +#include <ucl.h> + +export module nihil.ucl:real; + +import :object; +import :type; + +namespace nihil::ucl { + +export struct real final : object { + using contained_type = double; + + inline static constexpr object_type ucl_type = object_type::real; + + /* + * Create a real holding the value 0. Throws std::system_error + * on failure. + */ + real(); + + /* + * Create a real holding a specific value. Throws std::system_error + * on failure. + */ + explicit real(contained_type value); + + /* + * Create a new real from a UCL object. Throws type_mismatch + * on failure. + */ + real(ref_t, ::ucl_object_t const *uobj); + real(noref_t, ::ucl_object_t *uobj); + + // Return the value of this real. + [[nodiscard]] auto value(this real const &self) -> contained_type; +}; + +/* + * Real constructors. These return an error instead of throwing. + */ + +export [[nodiscard]] auto +make_real(real::contained_type = 0) -> std::expected<real, error>; + +/* + * Comparison operators. + */ + +export [[nodiscard]] auto operator== (real const &a, real const &b) -> bool; + +export [[nodiscard]] auto operator== (real const &a, + real::contained_type b) -> bool; + +export [[nodiscard]] auto operator<=> (real const &a, real const &b) + -> std::partial_ordering; + +export [[nodiscard]] auto operator<=> (real const &a, real::contained_type b) + -> std::partial_ordering; + +/* + * Literal operator. + */ +inline namespace literals { +export constexpr auto operator""_ucl (long double d) -> real +{ + if (d > static_cast<long double>(std::numeric_limits<double>::max()) || + d < static_cast<long double>(std::numeric_limits<double>::min())) + throw std::out_of_range("literal out of range"); + + return real(static_cast<double>(d)); +} +} // namespace nihil::ucl::literals + +} // namespace nihil::ucl + +namespace nihil { inline namespace literals { + export using namespace ::nihil::ucl::literals; +}} // namespace nihil::literals + +/* + * std::formatter for a real. This provides the same format operations + * as std::formatter<double>; + */ +export template<> +struct std::formatter<nihil::ucl::real, char> +{ + std::formatter<double> base_formatter; + + template<class ParseContext> + constexpr ParseContext::iterator parse(ParseContext& ctx) + { + return base_formatter.parse(ctx); + } + + template<class FmtContext> + FmtContext::iterator format(nihil::ucl::real const &o, + FmtContext& ctx) const + { + return base_formatter.format(o.value(), ctx); + } +}; diff --git a/nihil.ucl/string.cc b/nihil.ucl/string.cc new file mode 100644 index 0000000..67e97f4 --- /dev/null +++ b/nihil.ucl/string.cc @@ -0,0 +1,187 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <cstdlib> +#include <expected> +#include <iosfwd> +#include <string> +#include <system_error> + +#include <ucl.h> + +module nihil.ucl; + +import nihil.error; + +namespace nihil::ucl { + +auto make_string() -> std::expected<string, error> +{ + return make_string(std::string_view("")); +} + +auto make_string(char const *s) -> std::expected<string, error> +{ + return make_string(std::string_view(s)); +} + +auto make_string(std::string_view s) -> std::expected<string, error> +{ + auto *uobj = ::ucl_object_fromstring_common( + s.data(), s.size(), UCL_STRING_RAW); + + if (uobj == nullptr) + return std::unexpected(error( + errc::failed_to_create_object, + error(std::errc(errno)))); + + return string(noref, uobj); +} + +string::string(ref_t, ::ucl_object_t const *uobj) + : object(nihil::ucl::ref, [&] { + auto actual_type = static_cast<object_type>( + ::ucl_object_type(uobj)); + if (actual_type != string::ucl_type) + throw type_mismatch(string::ucl_type, actual_type); + return uobj; + }()) +{ +} + +string::string(noref_t, ::ucl_object_t *uobj) + : object(nihil::ucl::noref, [&] { + auto actual_type = static_cast<object_type>( + ::ucl_object_type(uobj)); + if (actual_type != string::ucl_type) + throw type_mismatch(string::ucl_type, actual_type); + return uobj; + }()) +{ +} + +string::string() + : string(std::string_view("")) +{} + +string::string(std::string_view value) + : string(noref, [&] { + auto *uobj = ::ucl_object_fromstring_common( + value.data(), value.size(), UCL_STRING_RAW); + if (uobj == nullptr) + throw std::system_error( + std::make_error_code(std::errc(errno))); + return uobj; + }()) +{ +} + +string::string(char const *value) + : string(std::string_view(value)) +{ +} + +auto string::value(this string const &self) -> contained_type +{ + char const *dptr{}; + std::size_t dlen; + + auto const *uobj = self.get_ucl_object(); + if (::ucl_object_tolstring_safe(uobj, &dptr, &dlen)) + return {dptr, dlen}; + + // This should never fail. + std::abort(); +} + +auto string::size(this string const &self) -> size_type +{ + return self.value().size(); +} + +auto string::empty(this string const &self) -> bool +{ + return self.size() == 0; +} + +auto string::data(this string const &self) -> pointer +{ + char const *dptr{}; + + auto const *uobj = self.get_ucl_object(); + if (::ucl_object_tostring_safe(uobj, &dptr)) + return dptr; + + // This should never fail. + std::abort(); +} + +auto string::begin(this string const &self) -> iterator +{ + return self.data(); +} + +auto string::end(this string const &self) -> iterator +{ + return self.data() + self.size(); +} + +auto operator== (string const &a, string const &b) + -> bool +{ + return a.value() == b.value(); +} + +auto operator<=> (string const &a, string const &b) + -> std::strong_ordering +{ + return a.value() <=> b.value(); +} + +/* + * For convenience, allow comparison with C++ strings without having to + * construct a temporary UCL object. + */ + +auto operator==(string const &lhs, std::string_view rhs) -> bool +{ + return lhs.value() == rhs; +} + +auto operator<=>(string const &lhs, std::string_view rhs) + -> std::strong_ordering +{ + return lhs.value() <=> rhs; +} + +auto operator==(string const &lhs, std::string const &rhs) -> bool +{ + return lhs == std::string_view(rhs); +} + +auto operator<=>(string const &lhs, std::string const &rhs) + -> std::strong_ordering +{ + return lhs <=> std::string_view(rhs); +} + +auto operator==(string const &lhs, char const *rhs) -> bool +{ + return lhs == std::string_view(rhs); +} + +auto operator<=>(string const &lhs, char const *rhs) + -> std::strong_ordering +{ + return lhs <=> std::string_view(rhs); +} + +auto operator<<(std::ostream &strm, string const &s) -> std::ostream & +{ + return strm << s.value(); +} + +} // namespace nihil::ucl diff --git a/nihil.ucl/string.ccm b/nihil.ucl/string.ccm new file mode 100644 index 0000000..c757bf1 --- /dev/null +++ b/nihil.ucl/string.ccm @@ -0,0 +1,229 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <cstdlib> +#include <expected> +#include <format> +#include <iosfwd> +#include <string> + +#include <ucl.h> + +export module nihil.ucl:string; + +import :object; +import :type; + +namespace nihil::ucl { + +export struct string final : object { + using contained_type = std::string_view; + inline static constexpr object_type ucl_type = object_type::string; + + // string is a container of char + using value_type = char const; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using reference = value_type &; + using pointer = value_type *; + using iterator = pointer; + + /* + * Create a new empty string. Throws std::system_error on failure. + */ + string(); + + /* + * Create a string from a value. Throws std::system_error on failure. + */ + explicit string(std::string_view); + + /* + * Create a string from a C literal. Throws std::system_error + * on failure. + */ + explicit string(char const *); + + /* + * Create a string from a contiguous range. The range's value type + * must be char. Throws std::system_error on failure. + */ + template<std::ranges::contiguous_range Range> + requires (!std::same_as<std::string_view, Range> && + std::same_as<char, std::ranges::range_value_t<Range>>) + explicit string(Range &&range) + : string(std::string_view(std::ranges::begin(range), + std::ranges::end(range))) + {} + + /* + * Create a string from a non-contiguous range. This requires a + * temporary value due to limitations of the UCL C API. + */ + template<std::ranges::range Range> + requires (!std::ranges::contiguous_range<Range> && + std::same_as<char, std::ranges::range_value_t<Range>>) + explicit string(Range &&range) + : string(std::string(std::from_range, range)) + {} + + /* + * Create a string from an iterator pair. The iterator's value type + * must be char. If the iterator pair is not contiguous, the value + * will be copied to a temporary first. + * + * Throws std::system_error on failure. + */ + template<std::input_iterator Iterator> + requires (std::same_as<char, std::iter_value_t<Iterator>>) + string(Iterator first, Iterator last) + : string(std::ranges::subrange(first, last)) + {} + + /* + * Create a new string from a UCL object. Throws type_mismatch + * on failure. + */ + string(ref_t, ::ucl_object_t const *uobj); + string(noref_t, ::ucl_object_t *uobj); + + // Return the value of this string. + [[nodiscard]] auto value(this string const &self) -> contained_type; + + // Return the size of this string. + [[nodiscard]] auto size(this string const &self) -> size_type; + + // Test if this string is empty. + [[nodiscard]] auto empty(this string const &self) -> bool; + + // Access this string's data + [[nodiscard]] auto data(this string const &self) -> pointer; + + // Iterator access + [[nodiscard]] auto begin(this string const &self) -> iterator; + [[nodiscard]] auto end(this string const &self) -> iterator; +}; + +/* + * String constructors. These return an error instead of throwing. + */ + +// Empty string +export [[nodiscard]] auto +make_string() -> std::expected<string, error>; + +// From string_view +export [[nodiscard]] auto +make_string(std::string_view) -> std::expected<string, error>; + +// From C literal +export [[nodiscard]] auto +make_string(char const *) -> std::expected<string, error>; + +// From contiguous range +export template<std::ranges::contiguous_range Range> +requires (!std::same_as<std::string_view, Range> && + std::same_as<char, std::ranges::range_value_t<Range>>) +[[nodiscard]] auto make_string(Range &&range) +{ + return make_string(std::string_view(range)); +} + +// From non-contiguous range +export template<std::ranges::range Range> +requires (!std::ranges::contiguous_range<Range> && + std::same_as<char, std::ranges::range_value_t<Range>>) +[[nodiscard]] auto make_string(Range &&range) +{ + return make_string(std::string(std::from_range, range)); +} + +// From iterator pair +export template<std::input_iterator Iterator> +requires (std::same_as<char, std::iter_value_t<Iterator>>) +[[nodiscard]] auto make_string(Iterator first, Iterator last) +{ + return make_string(std::ranges::subrange(first, last)); +} + +/* + * Comparison operators. + */ + +export [[nodiscard]] auto operator== (string const &a, string const &b) -> bool; +export [[nodiscard]] auto operator<=> (string const &a, string const &b) + -> std::strong_ordering; + +/* + * For convenience, allow comparison with C++ strings without having to + * construct a temporary UCL object. + */ + +export [[nodiscard]] auto operator==(string const &lhs, + std::string_view rhs) -> bool; + +export [[nodiscard]] auto operator==(string const &lhs, + std::string const &rhs) -> bool; + +export [[nodiscard]] auto operator==(string const &lhs, + char const *rhs) -> bool; + +export [[nodiscard]] auto operator<=>(string const &lhs, + std::string_view rhs) + -> std::strong_ordering; + +export [[nodiscard]] auto operator<=>(string const &lhs, + std::string const &rhs) + -> std::strong_ordering; + +export [[nodiscard]] auto operator<=>(string const &lhs, + char const *rhs) + -> std::strong_ordering; + +/* + * Print a string to a stream. + */ +export auto operator<<(std::ostream &, string const &) -> std::ostream &; + +/* + * Literal operator. + */ +inline namespace literals { + export constexpr auto operator""_ucl (char const *s, std::size_t n) + -> string + { + return string(std::string_view(s, n)); + } +} // namespace nihil::ucl::literals + +} // namespace nihil::ucl + +namespace nihil { inline namespace literals { + export using namespace ::nihil::ucl::literals; +}} // namespace nihil::literals + +/* + * std::formatter for a string. This provides the same format operations + * as std::formatter<std::string_view>. + */ +export template<> +struct std::formatter<nihil::ucl::string, char> +{ + std::formatter<std::string_view> base_formatter; + + template<class ParseContext> + constexpr ParseContext::iterator parse(ParseContext& ctx) + { + return base_formatter.parse(ctx); + } + + template<class FmtContext> + FmtContext::iterator format(nihil::ucl::string const &o, + FmtContext& ctx) const + { + return base_formatter.format(o.value(), ctx); + } +}; diff --git a/nihil.ucl/tests/CMakeLists.txt b/nihil.ucl/tests/CMakeLists.txt new file mode 100644 index 0000000..0257b4f --- /dev/null +++ b/nihil.ucl/tests/CMakeLists.txt @@ -0,0 +1,22 @@ +# This source code is released into the public domain. + +add_executable(nihil.ucl.test + emit.cc + parse.cc + + object.cc + array.cc + boolean.cc + integer.cc + map.cc + real.cc + string.cc +) + +target_link_libraries(nihil.ucl.test PRIVATE nihil.ucl Catch2::Catch2WithMain) + +find_package(Catch2 REQUIRED) + +include(CTest) +include(Catch) +catch_discover_tests(nihil.ucl.test) diff --git a/nihil.ucl/tests/array.cc b/nihil.ucl/tests/array.cc new file mode 100644 index 0000000..866fa45 --- /dev/null +++ b/nihil.ucl/tests/array.cc @@ -0,0 +1,478 @@ +/* + * This source code is released into the public domain. + */ + +#include <algorithm> +#include <concepts> +#include <expected> +#include <ranges> +#include <string> + +#include <catch2/catch_test_macros.hpp> +#include <ucl.h> + +import nihil.ucl; + +TEST_CASE("ucl: array: invariants", "[ucl]") +{ + using namespace nihil::ucl; + + REQUIRE(array<>::ucl_type == object_type::array); + REQUIRE(static_cast<::ucl_type>(array<>::ucl_type) == UCL_ARRAY); + + static_assert(std::destructible<array<>>); + static_assert(std::default_initializable<array<>>); + static_assert(std::move_constructible<array<>>); + static_assert(std::copy_constructible<array<>>); + static_assert(std::equality_comparable<array<>>); + static_assert(std::totally_ordered<array<>>); + static_assert(std::swappable<array<>>); + + static_assert(std::ranges::sized_range<array<integer>>); + static_assert(std::same_as<std::ranges::range_value_t<array<integer>>, + integer>); +} + +TEST_CASE("ucl: array: constructor", "[ucl]") +{ + using namespace nihil::ucl; + + SECTION("default") { + auto arr = array<integer>(); + REQUIRE(arr.size() == 0); + REQUIRE(str(arr.type()) == "array"); + } + + SECTION("from range") { + auto vec = std::vector{integer(1), integer(42)}; + auto arr = array<integer>(std::from_range, vec); + + REQUIRE(arr.size() == 2); + REQUIRE(arr[0] == 1); + REQUIRE(arr[1] == 42); + } + + SECTION("from iterator pair") { + auto vec = std::vector{integer(1), integer(42)}; + auto arr = array<integer>(std::ranges::begin(vec), + std::ranges::end(vec)); + + REQUIRE(arr.size() == 2); + REQUIRE(arr[0] == 1); + REQUIRE(arr[1] == 42); + } + + SECTION("from initializer_list") { + auto arr = array<integer>{integer(1), integer(42)}; + + REQUIRE(arr.size() == 2); + REQUIRE(arr[0] == 1); + REQUIRE(arr[1] == 42); + } +} + +TEST_CASE("ucl: array: construct from UCL object", "[ucl]") +{ + using namespace nihil::ucl; + + SECTION("ref, correct type") { + auto uarr = ::ucl_object_typed_new(UCL_ARRAY); + auto uint = ::ucl_object_fromint(42); + ::ucl_array_append(uarr, uint); + + auto arr = array<integer>(ref, uarr); + REQUIRE(arr[0] == 42); + + ::ucl_object_unref(uarr); + } + + SECTION("noref, correct type") { + auto uarr = ::ucl_object_typed_new(UCL_ARRAY); + auto uint = ::ucl_object_fromint(42); + ::ucl_array_append(uarr, uint); + + auto arr = array<integer>(noref, uarr); + REQUIRE(arr[0] == 42); + } + + SECTION("ref, wrong element type") { + auto uarr = ::ucl_object_typed_new(UCL_ARRAY); + auto uint = ::ucl_object_frombool(true); + ::ucl_array_append(uarr, uint); + + auto arr = array<integer>(noref, uarr); + REQUIRE_THROWS_AS(arr[0], type_mismatch); + } + + SECTION("ref, wrong type") { + auto uobj = ::ucl_object_frombool(true); + + REQUIRE_THROWS_AS(array(ref, uobj), type_mismatch); + + ::ucl_object_unref(uobj); + } + + SECTION("noref, wrong type") { + auto uobj = ::ucl_object_frombool(true); + + REQUIRE_THROWS_AS(array(noref, uobj), type_mismatch); + + ::ucl_object_unref(uobj); + } +} + +TEST_CASE("ucl: array: swap", "[ucl]") +{ + // do not add using namespace nihil::ucl + + auto arr1 = nihil::ucl::array<nihil::ucl::integer>{ + nihil::ucl::integer(1), + nihil::ucl::integer(2) + }; + + auto arr2 = nihil::ucl::array<nihil::ucl::integer>{ + nihil::ucl::integer(3), + }; + + swap(arr1, arr2); + + REQUIRE(arr1.size() == 1); + REQUIRE(arr1[0] == 3); + + REQUIRE(arr2.size() == 2); + REQUIRE(arr2[0] == 1); +} + +TEST_CASE("ucl: array: push_back", "[ucl]") +{ + using namespace nihil::ucl; + + auto arr = array<integer>(); + REQUIRE(arr.size() == 0); + + arr.push_back(integer(1)); + arr.push_back(integer(42)); + arr.push_back(integer(666)); + + REQUIRE(arr.size() == 3); + REQUIRE(arr[0] == 1); + REQUIRE(arr[1] == 42); + REQUIRE(arr[2] == 666); + + REQUIRE_THROWS_AS(arr[3], std::out_of_range); + + REQUIRE(arr.front() == 1); + REQUIRE(arr.back() == 666); +} + +TEST_CASE("ucl: array: compare", "[ucl]") +{ + using namespace nihil::ucl; + + auto arr = array<integer>{ + integer(1), integer(42), integer(666) + }; + + auto arr2 = array<integer>(); + REQUIRE(arr != arr2); + + arr2.push_back(integer(1)); + arr2.push_back(integer(42)); + arr2.push_back(integer(666)); + REQUIRE(arr == arr2); + + auto arr3 = array<integer>{ + integer(1), integer(1), integer(1) + }; + + REQUIRE(arr != arr3); +} + +TEST_CASE("ucl: array: iterator", "[ucl]") +{ + using namespace nihil::ucl; + + auto arr = array<integer>{integer(1), integer(42), integer(666)}; + + auto it = arr.begin(); + REQUIRE(*it == 1); + auto end = arr.end(); + REQUIRE(it != end); + REQUIRE(it < end); + + ++it; + REQUIRE(*it == 42); + + ++it; + REQUIRE(*it == 666); + + --it; + REQUIRE(*it == 42); + + ++it; + REQUIRE(it != end); + ++it; + REQUIRE(it == end); +} + +TEST_CASE("ucl: array: parse", "[ucl]") +{ + using namespace std::literals; + using namespace nihil::ucl; + + auto obj = parse("value = [1, 42, 666]"sv).value(); + + auto arr = object_cast<array<integer>>(obj["value"]).value(); + + REQUIRE(arr.size() == 3); + REQUIRE(arr[0] == 1); + REQUIRE(arr[1] == 42); + REQUIRE(arr[2] == 666); +} + +TEST_CASE("ucl: array: emit", "[ucl]") +{ + using namespace nihil::ucl; + + auto ucl = parse("array = [1, 42, 666];").value(); + + auto output = std::format("{:c}", ucl); + REQUIRE(output == +"array [\n" +" 1,\n" +" 42,\n" +" 666,\n" +"]\n"); +} + +TEST_CASE("ucl: array: format", "[ucl]") +{ + using namespace nihil::ucl; + + SECTION("empty array") { + auto arr = array<integer>(); + REQUIRE(std::format("{}", arr) == "[]"); + } + + SECTION("bare array") { + auto arr = array<integer>{ + integer(1), integer(42), integer(666) + }; + + auto output = std::format("{}", arr); + REQUIRE(output == "[1, 42, 666]"); + } + + SECTION("parsed array") { + auto ucl = parse("array = [1, 42, 666];").value(); + auto arr = object_cast<array<integer>>(ucl["array"]).value(); + + auto output = std::format("{}", arr); + REQUIRE(output == "[1, 42, 666]"); + } +} + +TEST_CASE("ucl: array: print to ostream", "[ucl]") +{ + using namespace nihil::ucl; + + SECTION("empty array") { + auto arr = array<integer>(); + auto strm = std::ostringstream(); + strm << arr; + + REQUIRE(strm.str() == "[]"); + } + + SECTION("bare array") { + auto arr = array<integer>{ + integer(1), integer(42), integer(666) + }; + auto strm = std::ostringstream(); + strm << arr; + + REQUIRE(strm.str() == "[1, 42, 666]"); + } + + SECTION("parsed array") { + auto ucl = parse("array = [1, 42, 666];").value(); + auto arr = object_cast<array<integer>>(ucl["array"]).value(); + auto strm = std::ostringstream(); + strm << arr; + + REQUIRE(strm.str() == "[1, 42, 666]"); + } +} + +TEST_CASE("ucl: array is a sized_range", "[ucl]") +{ + using namespace nihil::ucl; + + auto arr = array<integer>{integer(1), integer(42), integer(666)}; + + auto size = std::ranges::size(arr); + REQUIRE(size == 3); + + auto begin = std::ranges::begin(arr); + static_assert(std::random_access_iterator<decltype(begin)>); + + auto end = std::ranges::end(arr); + static_assert(std::sentinel_for<decltype(end), decltype(begin)>); + + REQUIRE(std::distance(begin, end) == 3); + + auto vec = std::vector<integer>(); + std::ranges::copy(arr, std::back_inserter(vec)); + REQUIRE(std::ranges::equal(arr, vec)); + + auto arr_as_ints = + arr | std::views::transform(&integer::value); + auto int_vec = std::vector<integer::contained_type>(); + std::ranges::copy(arr_as_ints, std::back_inserter(int_vec)); + REQUIRE(int_vec == std::vector<std::int64_t>{1, 42, 666}); + +} + +TEST_CASE("ucl: array: bad object_cast", "[ucl]") +{ + using namespace nihil::ucl; + + auto arr = array<integer>(); + + auto cast_ok = object_cast<integer>(arr); + REQUIRE(!cast_ok); +} + +TEST_CASE("ucl: array: heterogeneous elements", "[ucl]") +{ + using namespace std::literals; + using namespace nihil::ucl; + + auto obj_err = parse("array [ 42, true, \"test\" ];"); + REQUIRE(obj_err); + auto obj = *obj_err; + + auto err = object_cast<array<>>(obj["array"]); + REQUIRE(err); + + auto arr = *err; + REQUIRE(arr.size() == 3); + + auto int_obj = object_cast<integer>(arr[0]); + REQUIRE(int_obj); + REQUIRE(*int_obj == 42); + + auto bool_obj = object_cast<boolean>(arr[1]); + REQUIRE(bool_obj); + REQUIRE(*bool_obj == true); + + auto string_obj = object_cast<string>(arr[2]); + REQUIRE(string_obj); + REQUIRE(*string_obj == "test"); +} + +TEST_CASE("ucl: array: heterogenous cast", "[ucl]") +{ + using namespace nihil::ucl; + + auto arr = array<>(); + arr.push_back(integer(42)); + arr.push_back(boolean(true)); + + // Converting to an array<integer> should fail. + auto cast_ok = object_cast<array<integer>>(arr); + REQUIRE(!cast_ok); + + // Converting to array<object> should succeed. + auto err = object_cast<array<object>>(arr); + REQUIRE(err); + + auto obj_arr = *err; + REQUIRE(obj_arr[0] == integer(42)); +} + +TEST_CASE("ucl: array: homogeneous cast", "[ucl]") +{ + using namespace nihil::ucl; + + auto arr = array<>(); + arr.push_back(integer(1)); + arr.push_back(integer(42)); + + auto obj = object(ref, arr.get_ucl_object()); + + // Converting to array<string> should fail. + auto cast_ok = object_cast<array<string>>(obj); + REQUIRE(!cast_ok); + + // Converting to an array<integer> should succeed. + auto err = object_cast<array<integer>>(obj); + REQUIRE(err); + + auto obj_arr = *err; + REQUIRE(obj_arr[0] == 1); + REQUIRE(obj_arr[1] == 42); +} + +TEST_CASE("array iterator: empty iterator", "[ucl]") +{ + using namespace nihil::ucl; + + auto it = array_iterator<integer>(); + + REQUIRE_THROWS_AS(*it, std::logic_error); + REQUIRE_THROWS_AS(it[0], std::logic_error); + REQUIRE_THROWS_AS(it++, std::logic_error); + REQUIRE_THROWS_AS(++it, std::logic_error); + + auto it2 = array_iterator<integer>(); + REQUIRE(it == it2); + REQUIRE((it < it2) == false); + REQUIRE((it > it2) == false); +} + +TEST_CASE("array iterator: invalid operations", "[ucl]") +{ + using namespace nihil::ucl; + + auto arr = array<integer>{ integer(42) }; + auto it = arr.begin(); + + SECTION("decrement before start") { + REQUIRE_THROWS_AS(--it, std::logic_error); + REQUIRE_THROWS_AS(it--, std::logic_error); + REQUIRE_THROWS_AS(it - 1, std::logic_error); + } + + SECTION("increment past end") { + ++it; + REQUIRE(it == arr.end()); + + REQUIRE_THROWS_AS(++it, std::logic_error); + REQUIRE_THROWS_AS(it++, std::logic_error); + REQUIRE_THROWS_AS(it + 1, std::logic_error); + } + + SECTION("dereference iterator at end") { + REQUIRE_THROWS_AS(it[1], std::logic_error); + + ++it; + REQUIRE(it == arr.end()); + + REQUIRE_THROWS_AS(*it, std::logic_error); + } + + SECTION("compare with different array") { + auto arr2 = array<integer>{ integer(42) }; + REQUIRE_THROWS_AS(it == arr2.begin(), std::logic_error); + REQUIRE_THROWS_AS(it > arr2.begin(), std::logic_error); + REQUIRE_THROWS_AS(it - arr2.begin(), std::logic_error); + } + + SECTION("compare with empty iterator") { + auto it2 = array_iterator<integer>(); + REQUIRE_THROWS_AS(it == it2, std::logic_error); + REQUIRE_THROWS_AS(it > it2, std::logic_error); + REQUIRE_THROWS_AS(it - it2, std::logic_error); + } +} diff --git a/nihil.ucl/tests/boolean.cc b/nihil.ucl/tests/boolean.cc new file mode 100644 index 0000000..f7ef95e --- /dev/null +++ b/nihil.ucl/tests/boolean.cc @@ -0,0 +1,224 @@ +/* + * This source code is released into the public domain. + */ + +#include <concepts> +#include <string> + +#include <catch2/catch_test_macros.hpp> +#include <ucl.h> + +import nihil.ucl; + +TEST_CASE("ucl: boolean: invariants", "[ucl]") +{ + using namespace nihil::ucl; + + static_assert(std::same_as<bool, boolean::contained_type>); + REQUIRE(boolean::ucl_type == object_type::boolean); + REQUIRE(static_cast<::ucl_type>(boolean::ucl_type) == UCL_BOOLEAN); + + static_assert(std::destructible<boolean>); + static_assert(std::default_initializable<boolean>); + static_assert(std::move_constructible<boolean>); + static_assert(std::copy_constructible<boolean>); + static_assert(std::equality_comparable<boolean>); + static_assert(std::totally_ordered<boolean>); + static_assert(std::swappable<boolean>); +} + +TEST_CASE("ucl: boolean: constructor", "[ucl]") +{ + using namespace nihil::ucl; + + SECTION("default") { + auto b = boolean(); + REQUIRE(b == false); + } + + SECTION("with value") { + auto b = boolean(true); + REQUIRE(b == true); + } +} + +TEST_CASE("ucl: boolean: construct from UCL object", "[ucl]") +{ + using namespace nihil::ucl; + + SECTION("ref, correct type") { + auto uobj = ::ucl_object_frombool(true); + + auto i = boolean(ref, uobj); + REQUIRE(i == true); + + ::ucl_object_unref(uobj); + } + + SECTION("noref, correct type") { + auto uobj = ::ucl_object_frombool(true); + + auto i = boolean(noref, uobj); + REQUIRE(i == true); + } + + SECTION("ref, wrong type") { + auto uobj = ::ucl_object_fromint(1); + + REQUIRE_THROWS_AS(boolean(ref, uobj), type_mismatch); + + ::ucl_object_unref(uobj); + } + + SECTION("noref, wrong type") { + auto uobj = ::ucl_object_fromint(1); + + REQUIRE_THROWS_AS(boolean(noref, uobj), type_mismatch); + + ::ucl_object_unref(uobj); + } +} + +TEST_CASE("ucl: boolean: make_boolean", "[ucl]") +{ + using namespace nihil::ucl; + + SECTION("default value") { + auto b = make_boolean().value(); + REQUIRE(b == false); + } + + SECTION("explicit value") { + auto b = make_boolean(true).value(); + REQUIRE(b == true); + } +} + +TEST_CASE("ucl: boolean: swap", "[ucl]") +{ + // do not add using namespace nihil::ucl + + auto b1 = nihil::ucl::boolean(true); + auto b2 = nihil::ucl::boolean(false); + + swap(b1, b2); + + REQUIRE(b1 == false); + REQUIRE(b2 == true); +} + +TEST_CASE("ucl: boolean: value()", "[ucl]") +{ + auto b = nihil::ucl::boolean(true); + REQUIRE(b.value() == true); +} + +TEST_CASE("ucl: boolean: key()", "[ucl]") +{ + using namespace nihil::ucl; + + auto err = parse("a_bool = true"); + REQUIRE(err); + + auto obj = *err; + REQUIRE(object_cast<boolean>(obj["a_bool"])->key() == "a_bool"); + + auto b = nihil::ucl::boolean(true); + REQUIRE(b.key() == ""); +} + +TEST_CASE("ucl: boolean: comparison", "[ucl]") +{ + using namespace nihil::ucl; + + auto b = boolean(true); + + SECTION("operator==") { + REQUIRE(b == true); + REQUIRE(b == boolean(true)); + } + + SECTION("operator!=") { + REQUIRE(b != false); + REQUIRE(b != boolean(false)); + } + + SECTION("operator<") { + REQUIRE(b <= true); + REQUIRE(b <= nihil::ucl::boolean(true)); + } + + SECTION("operator>") { + REQUIRE(b > false); + REQUIRE(b > nihil::ucl::boolean(false)); + } +} + +TEST_CASE("ucl: boolean: parse", "[ucl]") +{ + using namespace nihil::ucl; + + auto obj = parse("value = true").value(); + + auto v = obj["value"]; + REQUIRE(v.key() == "value"); + REQUIRE(object_cast<boolean>(v).value() == true); +} + +TEST_CASE("ucl: boolean: parse and emit", "[ucl]") +{ + using namespace nihil::ucl; + + auto ucl = parse("bool = true;").value(); + + auto output = std::string(); + emit(ucl, nihil::ucl::emitter::configuration, + std::back_inserter(output)); + + REQUIRE(output == "bool = true;\n"); +} + +TEST_CASE("ucl: boolean: format", "[ucl]") +{ + using namespace nihil::ucl; + + SECTION("bare boolean") { + auto str = std::format("{}", boolean(true)); + REQUIRE(str == "true"); + } + + SECTION("parsed boolean") { + auto obj = parse("bool = true;").value(); + auto b = object_cast<boolean>(obj["bool"]).value(); + + auto str = std::format("{}", b); + REQUIRE(str == "true"); + } + + SECTION("with format string") { + auto str = std::format("{: >5}", boolean(true)); + REQUIRE(str == " true"); + } +} + +TEST_CASE("ucl: boolean: print to ostream", "[ucl]") +{ + using namespace nihil::ucl; + + SECTION("bare boolean") { + auto strm = std::ostringstream(); + strm << boolean(true); + + REQUIRE(strm.str() == "true"); + } + + SECTION("parsed boolean") { + auto obj = parse("bool = true;").value(); + auto i = object_cast<boolean>(obj["bool"]).value(); + + auto strm = std::ostringstream(); + strm << i; + + REQUIRE(strm.str() == "true"); + } +} diff --git a/nihil.ucl/tests/emit.cc b/nihil.ucl/tests/emit.cc new file mode 100644 index 0000000..a7dcd71 --- /dev/null +++ b/nihil.ucl/tests/emit.cc @@ -0,0 +1,93 @@ +/* + * This source code is released into the public domain. + */ + +#include <format> +#include <sstream> + +#include <catch2/catch_test_macros.hpp> + +import nihil.ucl; + +TEST_CASE("ucl: emit to std::ostream", "[ucl]") +{ + using namespace std::literals; + + auto obj = nihil::ucl::parse("int = [1, 42, 666]"sv); + REQUIRE(obj); + + auto strm = std::ostringstream(); + strm << *obj; + + // The ostream emitter produces JSON. + REQUIRE(strm.str() == std::format("{:j}", *obj)); +} + +TEST_CASE("ucl: emit JSON with std::format", "[ucl]") +{ + using namespace std::literals; + + auto obj = nihil::ucl::parse("int = [1, 42, 666]"sv); + REQUIRE(obj); + + auto str = std::format("{:j}", *obj); + + REQUIRE(str == +"{\n" +" \"int\": [\n" +" 1,\n" +" 42,\n" +" 666\n" +" ]\n" +"}"); + + // Make sure JSON is the default format. + auto str2 = std::format("{}", *obj); + REQUIRE(str == str2); +} + +TEST_CASE("ucl: emit compact JSON with std::format", "[ucl]") +{ + using namespace std::literals; + + auto obj = nihil::ucl::parse("int = [1, 42, 666]"sv); + REQUIRE(obj); + + auto str = std::format("{:J}", *obj); + + REQUIRE(str == "{\"int\":[1,42,666]}"); +} + +TEST_CASE("ucl: emit configuration with std::format", "[ucl]") +{ + using namespace std::literals; + + auto obj = nihil::ucl::parse("int = [1, 42, 666]"sv); + REQUIRE(obj); + + auto str = std::format("{:c}", *obj); + + REQUIRE(str == +"int [\n" +" 1,\n" +" 42,\n" +" 666,\n" +"]\n"); +} + +TEST_CASE("ucl: emit YAML with std::format", "[ucl]") +{ + using namespace std::literals; + + auto obj = nihil::ucl::parse("int = [1, 42, 666]"sv); + REQUIRE(obj); + + auto str = std::format("{:y}", *obj); + + REQUIRE(str == +"int: [\n" +" 1,\n" +" 42,\n" +" 666\n" +"]"); +} diff --git a/nihil.ucl/tests/integer.cc b/nihil.ucl/tests/integer.cc new file mode 100644 index 0000000..6584764 --- /dev/null +++ b/nihil.ucl/tests/integer.cc @@ -0,0 +1,247 @@ +/* + * This source code is released into the public domain. + */ + +#include <concepts> +#include <cstdint> +#include <string> + +#include <catch2/catch_test_macros.hpp> +#include <ucl.h> + +import nihil.ucl; + +TEST_CASE("ucl: integer: invariants", "[ucl]") +{ + using namespace nihil::ucl; + + static_assert(std::same_as<std::int64_t, integer::contained_type>); + REQUIRE(integer::ucl_type == object_type::integer); + REQUIRE(static_cast<::ucl_type>(integer::ucl_type) == UCL_INT); + + static_assert(std::destructible<integer>); + static_assert(std::default_initializable<integer>); + static_assert(std::move_constructible<integer>); + static_assert(std::copy_constructible<integer>); + static_assert(std::equality_comparable<integer>); + static_assert(std::totally_ordered<integer>); + static_assert(std::swappable<integer>); +} + +TEST_CASE("ucl: integer: constructor", "[ucl]") +{ + using namespace nihil::ucl; + + SECTION("default") { + auto i = integer(); + REQUIRE(i == 0); + } + + SECTION("with value") { + auto i = integer(42); + REQUIRE(i == 42); + } +} + +TEST_CASE("ucl: integer: literal", "[ucl]") +{ + SECTION("with namespace nihil::ucl::literals") { + using namespace nihil::ucl::literals; + + auto i = 42_ucl; + REQUIRE(i.type() == nihil::ucl::object_type::integer); + REQUIRE(i == 42); + } + + SECTION("with namespace nihil::literals") { + using namespace nihil::literals; + + auto i = 42_ucl; + REQUIRE(i.type() == nihil::ucl::object_type::integer); + REQUIRE(i == 42); + } +} + +TEST_CASE("ucl: integer: construct from UCL object", "[ucl]") +{ + using namespace nihil::ucl; + + SECTION("ref, correct type") { + auto uobj = ::ucl_object_fromint(42); + + auto i = integer(ref, uobj); + REQUIRE(i == 42); + + ::ucl_object_unref(uobj); + } + + SECTION("noref, correct type") { + auto uobj = ::ucl_object_fromint(42); + + auto i = integer(noref, uobj); + REQUIRE(i == 42); + } + + SECTION("ref, wrong type") { + auto uobj = ::ucl_object_frombool(true); + + REQUIRE_THROWS_AS(integer(ref, uobj), type_mismatch); + + ::ucl_object_unref(uobj); + } + + SECTION("noref, wrong type") { + auto uobj = ::ucl_object_frombool(true); + + REQUIRE_THROWS_AS(integer(noref, uobj), type_mismatch); + + ::ucl_object_unref(uobj); + } +} + +TEST_CASE("ucl: integer: make_integer", "[ucl]") +{ + using namespace nihil::ucl; + + SECTION("default value") { + auto i = make_integer().value(); + REQUIRE(i == 0); + } + + SECTION("explicit value") { + auto i = make_integer(42).value(); + REQUIRE(i == 42); + } +} + +TEST_CASE("ucl: integer: swap", "[ucl]") +{ + // do not add using namespace nihil::ucl + + auto i1 = nihil::ucl::integer(1); + auto i2 = nihil::ucl::integer(2); + + swap(i1, i2); + + REQUIRE(i1 == 2); + REQUIRE(i2 == 1); +} + +TEST_CASE("ucl: integer: value()", "[ucl]") +{ + using namespace nihil::ucl; + + auto i = 42_ucl; + REQUIRE(i.value() == 42); +} + +TEST_CASE("ucl: integer: key()", "[ucl]") +{ + using namespace nihil::ucl; + + SECTION("parsed with key") { + auto obj = parse("an_int = 42").value(); + auto i = object_cast<integer>(obj["an_int"]).value(); + REQUIRE(i.key() == "an_int"); + } + + SECTION("bare integer, no key") { + auto i = 42_ucl; + REQUIRE(i.key() == ""); + } +} + +TEST_CASE("ucl: integer: comparison", "[ucl]") +{ + using namespace nihil::ucl; + + auto i = 42_ucl; + + SECTION("operator==") { + REQUIRE(i == 42); + REQUIRE(i == 42_ucl); + } + + SECTION("operator!=") { + REQUIRE(i != 1); + REQUIRE(i != 1_ucl); + } + + SECTION("operator<") { + REQUIRE(i < 43); + REQUIRE(i < 43_ucl); + } + + SECTION("operator>") { + REQUIRE(i > 1); + REQUIRE(i > 1_ucl); + } +} + +TEST_CASE("ucl: integer: parse", "[ucl]") +{ + using namespace nihil::ucl; + + auto obj = parse("value = 42").value(); + + auto v = obj["value"]; + REQUIRE(v.key() == "value"); + REQUIRE(object_cast<integer>(v) == 42); +} + +TEST_CASE("ucl: integer: parse and emit", "[ucl]") +{ + using namespace nihil::ucl; + + auto ucl = parse("int = 42;").value(); + + auto output = std::string(); + emit(ucl, emitter::configuration, std::back_inserter(output)); + + REQUIRE(output == "int = 42;\n"); +} + +TEST_CASE("ucl: integer: format", "[ucl]") +{ + using namespace nihil::ucl; + + SECTION("bare integer") { + auto str = std::format("{}", 42_ucl); + REQUIRE(str == "42"); + } + + SECTION("parsed integer") { + auto obj = parse("int = 42;").value(); + auto i = object_cast<integer>(obj["int"]).value(); + + auto str = std::format("{}", i); + REQUIRE(str == "42"); + } + + SECTION("with format string") { + auto str = std::format("{:-05}", 42_ucl); + REQUIRE(str == "00042"); + } +} + +TEST_CASE("ucl: integer: print to ostream", "[ucl]") +{ + using namespace nihil::ucl; + + SECTION("bare integer") { + auto strm = std::ostringstream(); + strm << 42_ucl; + + REQUIRE(strm.str() == "42"); + } + + SECTION("parsed integer") { + auto obj = parse("int = 42;").value(); + auto i = object_cast<integer>(obj["int"]).value(); + + auto strm = std::ostringstream(); + strm << i; + + REQUIRE(strm.str() == "42"); + } +} diff --git a/nihil.ucl/tests/map.cc b/nihil.ucl/tests/map.cc new file mode 100644 index 0000000..7240cb3 --- /dev/null +++ b/nihil.ucl/tests/map.cc @@ -0,0 +1,192 @@ +/* + * This source code is released into the public domain. + */ + +#include <concepts> + +#include <catch2/catch_test_macros.hpp> +#include <ucl.h> + +import nihil.ucl; + +//NOLINTBEGIN(bugprone-unchecked-optional-access) + +TEST_CASE("ucl: map: invariants", "[ucl]") +{ + using namespace nihil::ucl; + + REQUIRE(map<>::ucl_type == object_type::object); + REQUIRE(static_cast<::ucl_type>(map<>::ucl_type) == UCL_OBJECT); + + static_assert(std::destructible<map<>>); + static_assert(std::default_initializable<map<>>); + static_assert(std::move_constructible<map<>>); + static_assert(std::copy_constructible<map<>>); + static_assert(std::equality_comparable<map<>>); + static_assert(std::totally_ordered<map<>>); + static_assert(std::swappable<map<>>); + + static_assert(std::ranges::range<map<integer>>); + static_assert(std::same_as<std::pair<std::string_view, integer>, + std::ranges::range_value_t<map<integer>>>); +} + +TEST_CASE("ucl: map: default construct", "[ucl]") +{ + auto map = nihil::ucl::map<>(); + REQUIRE(str(map.type()) == "object"); +} + +TEST_CASE("ucl: map: construct from initializer_list", "[ucl]") +{ + using namespace nihil::ucl; + using namespace std::literals; + + auto map = nihil::ucl::map<integer>{ + {"1"sv, integer(1)}, + {"42"sv, integer(42)}, + }; + + REQUIRE(str(map.type()) == "object"); + REQUIRE(map["1"] == 1); + REQUIRE(map["42"] == 42); +} + +TEST_CASE("ucl: map: construct from range", "[ucl]") +{ + using namespace nihil::ucl; + using namespace std::literals; + + auto vec = std::vector<std::pair<std::string_view, integer>>{ + {"1"sv, integer(1)}, + {"42"sv, integer(42)}, + }; + + auto map = nihil::ucl::map<integer>(std::from_range, vec); + + REQUIRE(str(map.type()) == "object"); + REQUIRE(map["1"] == 1); + REQUIRE(map["42"] == 42); +} + +TEST_CASE("ucl: map: construct from iterator pair", "[ucl]") +{ + using namespace nihil::ucl; + using namespace std::literals; + + auto vec = std::vector<std::pair<std::string_view, integer>>{ + {"1"sv, integer(1)}, + {"42"sv, integer(42)}, + }; + + auto map = nihil::ucl::map<integer>(std::ranges::begin(vec), + std::ranges::end(vec)); + + REQUIRE(str(map.type()) == "object"); + REQUIRE(map["1"] == 1); + REQUIRE(map["42"] == 42); +} + +TEST_CASE("ucl: map: insert", "[ucl]") +{ + using namespace nihil::ucl; + using namespace std::literals; + + auto m = map<integer>(); + + m.insert({"test1"sv, integer(42)}); + m.insert({"test2"sv, integer(666)}); + + REQUIRE(m["test1"] == 42); + REQUIRE(m["test2"] == 666); +} + +TEST_CASE("ucl: map: find", "[ucl]") +{ + using namespace nihil::ucl; + using namespace std::literals; + + auto map = nihil::ucl::map<integer>{ + {"1"sv, integer(1)}, + {"42"sv, integer(42)}, + }; + + auto obj = map.find("42"); + REQUIRE(obj.value() == 42); + + obj = map.find("43"); + REQUIRE(!obj.has_value()); +} + +TEST_CASE("ucl: map: iterate", "[ucl]") +{ + using namespace nihil::ucl; + using namespace std::literals; + + auto map = nihil::ucl::map<integer>{ + {"1"sv, integer(1)}, + {"42"sv, integer(42)}, + }; + + auto i = 0u; + + for (auto [key, value] : map) { + if (key == "1") + REQUIRE(value == 1); + else if (key == "42") + REQUIRE(value == 42); + else + REQUIRE(false); + ++i; + } + + REQUIRE(i == 2); +} + +TEST_CASE("ucl: map: operator[] throws key_not_found", "[ucl]") +{ + auto map = nihil::ucl::map<nihil::ucl::integer>(); + REQUIRE_THROWS_AS(map["nonesuch"], nihil::ucl::key_not_found); +} + +TEST_CASE("ucl: map: remove", "[uc]") +{ + using namespace std::literals; + using namespace nihil::ucl; + + auto map = nihil::ucl::map<integer>{ + {"1"sv, integer(1)}, + {"42"sv, integer(42)}, + }; + + REQUIRE(map.find("42") != std::nullopt); + REQUIRE(map.remove("42") == true); + REQUIRE(map.find("42") == std::nullopt); + REQUIRE(map["1"] == 1); + + REQUIRE(map.remove("42") == false); +} + +TEST_CASE("ucl: map: pop", "[uc]") +{ + using namespace std::literals; + using namespace nihil::ucl; + + auto map = nihil::ucl::map<integer>{ + {"1"sv, integer(1)}, + {"42"sv, integer(42)}, + }; + + REQUIRE(map.find("42") != std::nullopt); + + auto obj = map.pop("42"); + REQUIRE(obj.value() == 42); + + REQUIRE(!map.find("42")); + REQUIRE(map["1"] == 1); + + obj = map.pop("42"); + REQUIRE(!obj); +} + +//NOLINTEND(bugprone-unchecked-optional-access) diff --git a/nihil.ucl/tests/object.cc b/nihil.ucl/tests/object.cc new file mode 100644 index 0000000..3ad180e --- /dev/null +++ b/nihil.ucl/tests/object.cc @@ -0,0 +1,44 @@ +/* + * This source code is released into the public domain. + */ + +#include <catch2/catch_test_macros.hpp> + +#include <ucl.h> + +import nihil.ucl; + +TEST_CASE("ucl object: get_ucl_object", "[ucl]") +{ + auto obj = nihil::ucl::integer(42); + + REQUIRE(obj.get_ucl_object() != nullptr); + static_assert(std::same_as<::ucl_object_t *, + decltype(obj.get_ucl_object())>); + + auto const cobj = obj; + static_assert(std::same_as<::ucl_object_t const *, + decltype(cobj.get_ucl_object())>); +} + +TEST_CASE("ucl object: compare", "[ucl]") +{ + using namespace std::literals; + + auto obj_41 = nihil::ucl::parse("int = 41;"sv); + REQUIRE(obj_41); + + auto obj_42 = nihil::ucl::parse("int = 42;"sv); + REQUIRE(obj_42); + + auto obj_42_2 = nihil::ucl::parse("int = 42;"sv); + REQUIRE(obj_42_2); + + auto obj_43 = nihil::ucl::parse("int = 43;"sv); + REQUIRE(obj_43); + + REQUIRE(*obj_42 == *obj_42_2); + REQUIRE(*obj_42 != *obj_43); + REQUIRE(*obj_42 < *obj_43); + REQUIRE(*obj_42 > *obj_41); +} diff --git a/nihil.ucl/tests/parse.cc b/nihil.ucl/tests/parse.cc new file mode 100644 index 0000000..43ce219 --- /dev/null +++ b/nihil.ucl/tests/parse.cc @@ -0,0 +1,55 @@ +/* + * This source code is released into the public domain. + */ + +#include <string> + +#include <catch2/catch_test_macros.hpp> +#include <catch2/matchers/catch_matchers_floating_point.hpp> + +import nihil.ucl; + +TEST_CASE("ucl parse: iterate array", "[ucl]") +{ + using namespace std::literals; + using namespace nihil::ucl; + + auto err = parse("value = [1, 42, 666];"sv); + REQUIRE(err); + + auto obj = *err; + + auto arr = obj["value"]; + REQUIRE(arr.key() == "value"); + + auto ints = object_cast<array<integer>>(arr); + REQUIRE(ints); + + auto vec = std::vector(std::from_range, *ints); + + REQUIRE(vec.size() == 3); + REQUIRE(vec[0] == 1); + REQUIRE(vec[1] == 42); + REQUIRE(vec[2] == 666); +} + +TEST_CASE("ucl parse: iterate hash", "[ucl]") +{ + using namespace std::literals; + using namespace nihil::ucl; + + auto input = "int = 42; bool = true; str = \"test\";"sv; + auto obj = parse(input); + REQUIRE(obj); + + for (auto &&[key, value] : *obj) { + REQUIRE(key == value.key()); + + if (key == "int") + REQUIRE(object_cast<integer>(value) == 42); + else if (key == "bool") + REQUIRE(object_cast<boolean>(value) == true); + else if (key == "str") + REQUIRE(object_cast<string>(value) == "test"); + } +} diff --git a/nihil.ucl/tests/real.cc b/nihil.ucl/tests/real.cc new file mode 100644 index 0000000..421917e --- /dev/null +++ b/nihil.ucl/tests/real.cc @@ -0,0 +1,248 @@ +/* + * This source code is released into the public domain. + */ + +#include <concepts> +#include <string> + +#include <catch2/catch_test_macros.hpp> +#include <catch2/matchers/catch_matchers_floating_point.hpp> +#include <ucl.h> + +import nihil.ucl; + +TEST_CASE("ucl: real: invariants", "[ucl]") +{ + using namespace nihil::ucl; + + static_assert(std::same_as<double, real::contained_type>); + REQUIRE(real::ucl_type == object_type::real); + REQUIRE(static_cast<::ucl_type>(real::ucl_type) == UCL_FLOAT); + + static_assert(std::destructible<real>); + static_assert(std::default_initializable<real>); + static_assert(std::move_constructible<real>); + static_assert(std::copy_constructible<real>); + static_assert(std::equality_comparable<real>); + static_assert(std::totally_ordered<real>); + static_assert(std::swappable<real>); +} + +TEST_CASE("ucl: real: constructor", "[ucl]") +{ + using namespace nihil::ucl; + + SECTION("default") { + auto r = real(); + REQUIRE(r == 0); + } + + SECTION("with value") { + auto r = real(42.1); + REQUIRE_THAT(r.value(), Catch::Matchers::WithinRel(42.1)); + } +} + +TEST_CASE("ucl: real: literal", "[ucl]") +{ + SECTION("with namespace nihil::ucl::literals") { + using namespace nihil::ucl::literals; + + auto r = 42.5_ucl; + REQUIRE(r.type() == nihil::ucl::object_type::real); + REQUIRE_THAT(r.value(), Catch::Matchers::WithinRel(42.5)); + } + + SECTION("with namespace nihil::literals") { + using namespace nihil::literals; + + auto r = 42.5_ucl; + REQUIRE(r.type() == nihil::ucl::object_type::real); + REQUIRE_THAT(r.value(), Catch::Matchers::WithinRel(42.5)); + } +} + +TEST_CASE("ucl: real: construct from UCL object", "[ucl]") +{ + using namespace nihil::ucl; + + SECTION("ref, correct type") { + auto uobj = ::ucl_object_fromdouble(42); + + auto r = real(ref, uobj); + REQUIRE(r == 42); + + ::ucl_object_unref(uobj); + } + + SECTION("noref, correct type") { + auto uobj = ::ucl_object_fromdouble(42); + + auto r = real(noref, uobj); + REQUIRE(r == 42); + } + + SECTION("ref, wrong type") { + auto uobj = ::ucl_object_fromint(42); + + REQUIRE_THROWS_AS(real(ref, uobj), type_mismatch); + + ::ucl_object_unref(uobj); + } + + SECTION("noref, wrong type") { + auto uobj = ::ucl_object_fromint(42); + + REQUIRE_THROWS_AS(real(noref, uobj), type_mismatch); + + ::ucl_object_unref(uobj); + } +} + +TEST_CASE("ucl: real: make_real", "[ucl]") +{ + using namespace nihil::ucl; + + SECTION("default value") { + auto i = make_real().value(); + REQUIRE(i == 0); + } + + SECTION("explicit value") { + auto i = make_real(42).value(); + REQUIRE(i == 42); + } +} + +TEST_CASE("ucl: real: swap", "[ucl]") +{ + // do not add using namespace nihil::ucl + + auto r1 = nihil::ucl::real(1); + auto r2 = nihil::ucl::real(2); + + swap(r1, r2); + + REQUIRE(r1 == 2.); + REQUIRE(r2 == 1.); +} + +TEST_CASE("ucl: real: value()", "[ucl]") +{ + using namespace nihil::ucl; + + auto r = 42.5_ucl; + REQUIRE_THAT(r.value(), Catch::Matchers::WithinRel(42.5)); +} + +TEST_CASE("ucl: real: key()", "[ucl]") +{ + using namespace nihil::ucl; + + SECTION("parsed with key") { + auto obj = parse("a_real = 42.5").value(); + auto r = object_cast<real>(obj["a_real"]).value(); + REQUIRE(r.key() == "a_real"); + } + + SECTION("bare real, no key") { + auto i = 42.5_ucl; + REQUIRE(i.key() == ""); + } +} + +TEST_CASE("ucl: real: comparison", "[ucl]") +{ + using namespace nihil::ucl; + + auto i = nihil::ucl::real(42.5); + + SECTION("operator==") { + REQUIRE(i == 42.5); + REQUIRE(i == 42.5_ucl); + } + + SECTION("operator!=") { + REQUIRE(i != 1); + REQUIRE(i != 1._ucl); + } + + SECTION("operator<") { + REQUIRE(i < 43); + REQUIRE(i < 43._ucl); + } + + SECTION("operator>") { + REQUIRE(i > 1); + REQUIRE(i > 1._ucl); + } +} + +TEST_CASE("ucl: real: parse", "[ucl]") +{ + using namespace nihil::ucl; + + auto obj = parse("value = 42.1").value(); + + auto v = obj["value"]; + REQUIRE(v.key() == "value"); + REQUIRE_THAT(object_cast<real>(v).value().value(), + Catch::Matchers::WithinRel(42.1)); +} + +TEST_CASE("ucl: real: parse and emit", "[ucl]") +{ + using namespace nihil::ucl; + + auto ucl = parse("real = 42.2").value(); + + auto output = std::string(); + emit(ucl, emitter::configuration, std::back_inserter(output)); + + REQUIRE(output == "real = 42.2;\n"); +} + +TEST_CASE("ucl: real: format", "[ucl]") +{ + using namespace nihil::ucl; + + SECTION("bare real") { + auto str = std::format("{}", 42.5_ucl); + REQUIRE(str == "42.5"); + } + + SECTION("parsed real") { + auto obj = parse("real = 42.5;").value(); + auto r = object_cast<real>(obj["real"]).value(); + + auto str = std::format("{}", r); + REQUIRE(str == "42.5"); + } + + SECTION("with format string") { + auto str = std::format("{:10.5f}", 42.5_ucl); + REQUIRE(str == " 42.50000"); + } +} + +TEST_CASE("ucl: real: print to ostream", "[ucl]") +{ + using namespace nihil::ucl; + + SECTION("bare real") { + auto strm = std::ostringstream(); + strm << 42.5_ucl; + + REQUIRE(strm.str() == "42.5"); + } + + SECTION("parsed real") { + auto obj = parse("real = 42.5;").value(); + auto i = object_cast<real>(obj["real"]).value(); + + auto strm = std::ostringstream(); + strm << i; + + REQUIRE(strm.str() == "42.5"); + } +} diff --git a/nihil.ucl/tests/string.cc b/nihil.ucl/tests/string.cc new file mode 100644 index 0000000..6409b8d --- /dev/null +++ b/nihil.ucl/tests/string.cc @@ -0,0 +1,415 @@ +/* + * This source code is released into the public domain. + */ + +#include <concepts> +#include <list> +#include <sstream> +#include <string> +#include <vector> + +#include <catch2/catch_test_macros.hpp> +#include <ucl.h> + +import nihil.ucl; + +TEST_CASE("ucl: string: invariants", "[ucl]") +{ + using namespace nihil::ucl; + + static_assert(std::same_as<std::string_view, string::contained_type>); + REQUIRE(string::ucl_type == object_type::string); + REQUIRE(static_cast<::ucl_type>(string::ucl_type) == UCL_STRING); + + static_assert(std::destructible<string>); + static_assert(std::default_initializable<string>); + static_assert(std::move_constructible<string>); + static_assert(std::copy_constructible<string>); + static_assert(std::equality_comparable<string>); + static_assert(std::totally_ordered<string>); + static_assert(std::swappable<string>); + + static_assert(std::ranges::contiguous_range<string>); + static_assert(std::same_as<char, std::ranges::range_value_t<string>>); +} + +TEST_CASE("ucl: string: literal", "[ucl]") +{ + SECTION("with namespace nihil::ucl::literals") { + using namespace nihil::ucl::literals; + + auto s = "testing"_ucl; + REQUIRE(s.type() == nihil::ucl::object_type::string); + REQUIRE(s == "testing"); + } + + SECTION("with namespace nihil::literals") { + using namespace nihil::literals; + + auto s = "testing"_ucl; + REQUIRE(s.type() == nihil::ucl::object_type::string); + REQUIRE(s == "testing"); + } +} + +TEST_CASE("ucl: string: construct", "[ucl]") +{ + using namespace nihil::ucl; + using namespace std::literals; + + SECTION("empty string") { + auto str = string(); + REQUIRE(str.type() == object_type::string); + REQUIRE(str == ""); + } + + SECTION("with integer-like value") { + auto str = "42"_ucl; + REQUIRE(str.type() == object_type::string); + REQUIRE(str == "42"); + } + + SECTION("with boolean-like value") { + auto str = "true"_ucl; + REQUIRE(str.type() == object_type::string); + REQUIRE(str == "true"); + } + + SECTION("from string literal") { + auto str = string("testing"); + REQUIRE(str.type() == object_type::string); + REQUIRE(str == "testing"); + } + + SECTION("from std::string") { + auto str = string("testing"s); + REQUIRE(str.type() == object_type::string); + REQUIRE(str == "testing"); + } + + SECTION("from std::string_view") { + auto str = string("testing"sv); + REQUIRE(str.type() == object_type::string); + REQUIRE(str == "testing"); + } + + SECTION("from contiguous range") { + auto s = std::vector{'t', 'e', 's', 't', 'i', 'n', 'g'}; + auto str = string(s); + REQUIRE(str.type() == object_type::string); + REQUIRE(str == "testing"); + } + + SECTION("from non-contiguous range") { + auto s = std::list{'t', 'e', 's', 't', 'i', 'n', 'g'}; + auto str = string(s); + REQUIRE(str.type() == object_type::string); + REQUIRE(str == "testing"); + } + + SECTION("from contiguous iterator pair") { + auto s = std::vector{'t', 'e', 's', 't', 'i', 'n', 'g'}; + auto str = string(s.begin(), s.end()); + REQUIRE(str.type() == object_type::string); + REQUIRE(str == "testing"); + } + + SECTION("from non-contiguous iterator pair") { + auto s = std::list{'t', 'e', 's', 't', 'i', 'n', 'g'}; + auto str = string(s.begin(), s.end()); + REQUIRE(str.type() == object_type::string); + REQUIRE(str == "testing"); + } +} + +TEST_CASE("ucl: string: construct from UCL object", "[ucl]") +{ + using namespace nihil::ucl; + + SECTION("ref, correct type") { + auto uobj = ::ucl_object_fromstring("testing"); + + auto s = string(ref, uobj); + REQUIRE(s == "testing"); + + ::ucl_object_unref(uobj); + } + + SECTION("noref, correct type") { + auto uobj = ::ucl_object_fromstring("testing"); + + auto s = string(noref, uobj); + REQUIRE(s == "testing"); + } + + SECTION("ref, wrong type") { + auto uobj = ::ucl_object_frombool(true); + + REQUIRE_THROWS_AS(string(ref, uobj), type_mismatch); + + ::ucl_object_unref(uobj); + } + + SECTION("noref, wrong type") { + auto uobj = ::ucl_object_frombool(true); + + REQUIRE_THROWS_AS(string(noref, uobj), type_mismatch); + + ::ucl_object_unref(uobj); + } +} + +TEST_CASE("ucl: string: make_string", "[ucl]") +{ + using namespace nihil::ucl; + using namespace std::literals; + + SECTION("empty string") { + auto str = make_string().value(); + REQUIRE(str.type() == object_type::string); + REQUIRE(str == ""); + } + + SECTION("from string literal") { + auto str = make_string("testing").value(); + REQUIRE(str.type() == object_type::string); + REQUIRE(str == "testing"); + } + + SECTION("from std::string") { + auto str = make_string("testing"s).value(); + REQUIRE(str.type() == object_type::string); + REQUIRE(str == "testing"); + } + + SECTION("from std::string_view") { + auto str = make_string("testing"sv).value(); + REQUIRE(str.type() == object_type::string); + REQUIRE(str == "testing"); + } + + SECTION("from contiguous range") { + auto s = std::vector{'t', 'e', 's', 't', 'i', 'n', 'g'}; + auto str = make_string(s).value(); + REQUIRE(str.type() == object_type::string); + REQUIRE(str == "testing"); + } + + SECTION("from non-contiguous range") { + auto s = std::list{'t', 'e', 's', 't', 'i', 'n', 'g'}; + auto str = make_string(s).value(); + REQUIRE(str.type() == object_type::string); + REQUIRE(str == "testing"); + } + + SECTION("from contiguous iterator pair") { + auto s = std::vector{'t', 'e', 's', 't', 'i', 'n', 'g'}; + auto str = make_string(s.begin(), s.end()).value(); + REQUIRE(str.type() == object_type::string); + REQUIRE(str == "testing"); + } + + SECTION("from non-contiguous iterator pair") { + auto s = std::list{'t', 'e', 's', 't', 'i', 'n', 'g'}; + auto str = make_string(s.begin(), s.end()).value(); + REQUIRE(str.type() == object_type::string); + REQUIRE(str == "testing"); + } +} + +TEST_CASE("ucl: string: swap", "[ucl]") +{ + // do not add using namespace nihil::ucl + + auto s1 = nihil::ucl::string("one"); + auto s2 = nihil::ucl::string("two"); + + swap(s1, s2); + + REQUIRE(s1 == "two"); + REQUIRE(s2 == "one"); +} + +TEST_CASE("ucl: string: value()", "[ucl]") +{ + using namespace nihil::ucl; + + auto s = string("te\"st"); + REQUIRE(s.value() == "te\"st"); +} + +TEST_CASE("ucl: string: key()", "[ucl]") +{ + using namespace nihil::ucl; + + auto err = parse("a_string = \"test\""); + REQUIRE(err); + + auto obj = *err; + REQUIRE(object_cast<string>(obj["a_string"])->key() == "a_string"); + + auto s = string("test"); + REQUIRE(s.key() == ""); +} + +TEST_CASE("ucl: string: size", "[ucl]") +{ + using namespace nihil::ucl; + + REQUIRE(string().size() == 0); + REQUIRE(string("test").size() == 4); +} + +TEST_CASE("ucl: string: empty", "[ucl]") +{ + using namespace nihil::ucl; + + REQUIRE(string().empty() == true); + REQUIRE(string("test").empty() == false); +} + +TEST_CASE("ucl: string: iterate", "[ucl]") +{ + using namespace nihil::ucl; + + auto str = "test"_ucl; + + SECTION("as iterator pair") { + auto begin = str.begin(); + static_assert(std::contiguous_iterator<decltype(begin)>); + + auto end = str.end(); + static_assert(std::sentinel_for<decltype(end), + decltype(begin)>); + + REQUIRE(*begin == 't'); + ++begin; + REQUIRE(*begin == 'e'); + ++begin; + REQUIRE(*begin == 's'); + ++begin; + REQUIRE(*begin == 't'); + ++begin; + + REQUIRE(begin == end); + } + + SECTION("as range") { + auto s = std::string(std::from_range, str); + REQUIRE(s == "test"); + } +} + +TEST_CASE("ucl: string: comparison", "[ucl]") +{ + using namespace nihil::ucl; + + auto str = "testing"_ucl; + + SECTION("operator==") { + REQUIRE(str == "testing"_ucl); + REQUIRE(str == std::string_view("testing")); + REQUIRE(str == std::string("testing")); + REQUIRE(str == "testing"); + } + + SECTION("operator!=") { + REQUIRE(str != "test"_ucl); + REQUIRE(str != std::string_view("test")); + REQUIRE(str != std::string("test")); + REQUIRE(str != "test"); + } + + SECTION("operator<") { + REQUIRE(str < "zzz"_ucl); + REQUIRE(str < std::string_view("zzz")); + REQUIRE(str < std::string("zzz")); + REQUIRE(str < "zzz"); + } + + SECTION("operator>") { + REQUIRE(str > "aaa"_ucl); + REQUIRE(str > std::string_view("aaa")); + REQUIRE(str > std::string("aaa")); + REQUIRE(str > "aaa"); + } +} + +TEST_CASE("ucl: string: parse", "[ucl]") +{ + using namespace nihil::ucl; + + auto obj = parse("value = \"te\\\"st\"").value(); + + auto v = obj["value"]; + REQUIRE(v.key() == "value"); + REQUIRE(object_cast<nihil::ucl::string>(v).value() == "te\"st"); +} + +TEST_CASE("ucl: string: emit", "[ucl]") +{ + using namespace nihil::ucl; + + auto ucl = parse("str = \"te\\\"st\";").value(); + + auto output = std::string(); + emit(ucl, emitter::configuration, std::back_inserter(output)); + + REQUIRE(output == "str = \"te\\\"st\";\n"); +} + +TEST_CASE("ucl: string: format", "[ucl]") +{ + using namespace nihil::ucl; + using namespace std::literals; + + auto constexpr test_string = "te\"st"sv; + + SECTION("bare string") { + auto str = std::format("{}", string(test_string)); + REQUIRE(str == test_string); + } + + SECTION("parsed string") { + auto obj = parse("string = \"te\\\"st\";").value(); + auto s = object_cast<string>(obj["string"]).value(); + + auto str = std::format("{}", s); + REQUIRE(str == test_string); + } + + SECTION("with format string") { + auto str = std::format("{: >10}", string(test_string)); + REQUIRE(str == " te\"st"); + } +} + +TEST_CASE("ucl: string: print to ostream", "[ucl]") +{ + using namespace nihil::ucl; + using namespace std::literals; + + auto constexpr test_string = "te\"st"sv; + + SECTION("bare string") { + auto strm = std::ostringstream(); + strm << string(test_string); + + REQUIRE(strm.str() == test_string); + } + + SECTION("parsed string") { + auto obj = parse("string = \"te\\\"st\";").value(); + auto s = object_cast<string>(obj["string"]).value(); + + auto strm = std::ostringstream(); + strm << s; + + REQUIRE(strm.str() == test_string); + } + + SECTION("with format string") { + auto str = std::format("{: >10}", string(test_string)); + REQUIRE(str == " te\"st"); + } +} diff --git a/nihil.ucl/type.cc b/nihil.ucl/type.cc new file mode 100644 index 0000000..7d9cad7 --- /dev/null +++ b/nihil.ucl/type.cc @@ -0,0 +1,62 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <format> + +module nihil.ucl; + +namespace nihil::ucl { + +auto str(object_type type) -> std::string_view { + using namespace std::literals; + + switch (type) { + case object_type::object: + return "object"sv; + case object_type::array: + return "array"sv; + case object_type::integer: + return "integer"sv; + case object_type::real: + return "real"sv; + case object_type::string: + return "string"sv; + case object_type::boolean: + return "boolean"sv; + case object_type::time: + return "time"sv; + case object_type::userdata: + return "userdata"sv; + case object_type::null: + return "null"sv; + default: + // Don't fail here, since UCL might add more types that we + // don't know about. + return "unknown"sv; + } +} + +type_mismatch::type_mismatch(object_type expected_type, + object_type actual_type) + : error(std::format( + "expected type '{}' != actual type '{}'", + ucl::str(expected_type), ucl::str(actual_type))) + , m_expected_type(expected_type) + , m_actual_type(actual_type) +{ +} + +auto type_mismatch::expected_type(this type_mismatch const &self) -> object_type +{ + return self.m_expected_type; +} + +auto type_mismatch::actual_type(this type_mismatch const &self) -> object_type +{ + return self.m_actual_type; +} + +} // namespace nihil::ucl diff --git a/nihil.ucl/type.ccm b/nihil.ucl/type.ccm new file mode 100644 index 0000000..f3b3aef --- /dev/null +++ b/nihil.ucl/type.ccm @@ -0,0 +1,58 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <concepts> +#include <format> +#include <stdexcept> +#include <string> + +#include <ucl.h> + +export module nihil.ucl:type; + +import nihil.error; + +namespace nihil::ucl { + +// Our strongly-typed version of ::ucl_type. +export enum struct object_type { + object = UCL_OBJECT, + array = UCL_ARRAY, + integer = UCL_INT, + real = UCL_FLOAT, + string = UCL_STRING, + boolean = UCL_BOOLEAN, + time = UCL_TIME, + userdata = UCL_USERDATA, + null = UCL_NULL, +}; + +// Get the name of a type. +export auto str(object_type type) -> std::string_view; + +// Concept of a UCL data type. +export template<typename T> +concept datatype = requires(T o) { + { o.get_ucl_object() } -> std::convertible_to<::ucl_object_t const *>; + { o.type() } -> std::same_as<object_type>; + { T::ucl_type } -> std::convertible_to<object_type>; +}; + +// Exception thrown when a type assertion fails. +export struct type_mismatch : error { + type_mismatch(object_type expected_type, object_type actual_type); + + // The type we expected. + auto expected_type(this type_mismatch const &self) -> object_type; + // The type we got. + auto actual_type(this type_mismatch const &self) -> object_type; + +private: + object_type m_expected_type; + object_type m_actual_type; +}; + +} // namespace nihil::ucl diff --git a/nihil.util/CMakeLists.txt b/nihil.util/CMakeLists.txt new file mode 100644 index 0000000..a07ea7d --- /dev/null +++ b/nihil.util/CMakeLists.txt @@ -0,0 +1,38 @@ +# This source code is released into the public domain. + +add_library(nihil.util STATIC) +target_link_libraries(nihil.util PRIVATE nihil.core nihil.error nihil.monad) +target_sources(nihil.util + PUBLIC FILE_SET modules TYPE CXX_MODULES FILES + nihil.util.ccm + + capture_stream.ccm + ctype.ccm + parse_size.ccm + next_word.ccm + skipws.ccm + tabulate.ccm +) + +if(NIHIL_TESTS) + enable_testing() + + add_executable(nihil.util.test + test_capture_stream.cc + test_ctype.cc + test_parse_size.cc + test_next_word.cc + test_skipws.cc + test_tabulate.cc + ) + target_link_libraries(nihil.util.test PRIVATE + nihil.util + Catch2::Catch2WithMain + ) + + find_package(Catch2 REQUIRED) + + include(CTest) + include(Catch) + catch_discover_tests(nihil.util.test) +endif() diff --git a/nihil.util/capture_stream.ccm b/nihil.util/capture_stream.ccm new file mode 100644 index 0000000..7ec39a9 --- /dev/null +++ b/nihil.util/capture_stream.ccm @@ -0,0 +1,62 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <iostream> + +export module nihil.util:capture_stream; + +namespace nihil { + +/* + * Capture output written to a stream and redirect it to an internal string + * buffer. Call .str() to get the data written. Call .release() to stop + * capturing (or simply delete the capture_stream object). + */ +export template<typename Char, typename Traits> +struct capture_stream { + capture_stream(std::basic_ostream<Char, Traits> &stream) + : m_stream(&stream) + { + m_old_streambuf = m_stream->rdbuf(); + m_stream->rdbuf(m_buffer.rdbuf()); + } + + ~capture_stream() + { + if (m_old_streambuf == nullptr) + return; + m_stream->rdbuf(m_old_streambuf); + } + + /* + * Release this capture, returning the stream to its previous state. + */ + auto release(this capture_stream &self) -> void + { + if (self.m_old_streambuf == nullptr) + throw std::logic_error( + "release() called on empty capture_stream"); + + self.m_stream->rdbuf(self.m_old_streambuf); + self.m_old_streambuf = nullptr; + } + + /* + * Get the data which has been written to the stream. + */ + [[nodiscard]] auto str(this capture_stream const &self) + -> std::basic_string_view<Char, Traits> + { + return self.m_buffer.view(); + } + +private: + std::basic_ostringstream<Char, Traits> m_buffer; + std::basic_ostream<Char, Traits> *m_stream; + std::streambuf *m_old_streambuf; +}; + +} // namespace nihil diff --git a/nihil.util/ctype.ccm b/nihil.util/ctype.ccm new file mode 100644 index 0000000..6d30c4f --- /dev/null +++ b/nihil.util/ctype.ccm @@ -0,0 +1,87 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <concepts> +#include <locale> + +export module nihil.util:ctype; + +namespace nihil { + +/* + * ctype_is: wrap std::ctype<T>::is() in a form suitable for use as an algorithm + * predicate, i.e., ctype_is(m) will return a functor object that takes any char + * type as an argument and returns bool. + * + * If the locale is not specified, the current global locale is used by default. + * + * ctype_is copies the locale, so passing a temporary is fine. + */ + +export struct ctype_is final { + ctype_is(std::ctype_base::mask mask_, + std::locale const &locale_ = std::locale()) + : m_mask(mask_) + , m_locale(locale_) + {} + + [[nodiscard]] auto operator()(this ctype_is const &self, + std::integral auto c) + { + using ctype = std::ctype<decltype(c)>; + auto &facet = std::use_facet<ctype>(self.m_locale); + return facet.is(self.m_mask, c); + } + +private: + std::ctype_base::mask m_mask; + std::locale m_locale; +}; + +// Predefined tests for the current global locale. + +export inline auto is_space = ctype_is(std::ctype_base::space); +export inline auto is_print = ctype_is(std::ctype_base::print); +export inline auto is_cntrl = ctype_is(std::ctype_base::cntrl); +export inline auto is_upper = ctype_is(std::ctype_base::upper); +export inline auto is_lower = ctype_is(std::ctype_base::lower); +export inline auto is_alpha = ctype_is(std::ctype_base::alpha); +export inline auto is_digit = ctype_is(std::ctype_base::digit); +export inline auto is_punct = ctype_is(std::ctype_base::punct); +export inline auto is_xdigit = ctype_is(std::ctype_base::xdigit); +export inline auto is_blank = ctype_is(std::ctype_base::blank); +export inline auto is_alnum = ctype_is(std::ctype_base::alnum); +export inline auto is_graph = ctype_is(std::ctype_base::graph); + +// Predefined tests for the C locale. The C locale is guaranteed to always be +// available, so this doesn't create lifetime issues. + +export inline auto is_c_space = + ctype_is(std::ctype_base::space, std::locale::classic()); +export inline auto is_c_print = + ctype_is(std::ctype_base::print, std::locale::classic()); +export inline auto is_c_cntrl = + ctype_is(std::ctype_base::cntrl, std::locale::classic()); +export inline auto is_c_upper = + ctype_is(std::ctype_base::upper, std::locale::classic()); +export inline auto is_c_lower = + ctype_is(std::ctype_base::lower, std::locale::classic()); +export inline auto is_c_alpha = + ctype_is(std::ctype_base::alpha, std::locale::classic()); +export inline auto is_c_digit = + ctype_is(std::ctype_base::digit, std::locale::classic()); +export inline auto is_c_punct = + ctype_is(std::ctype_base::punct, std::locale::classic()); +export inline auto is_c_xdigit = + ctype_is(std::ctype_base::xdigit, std::locale::classic()); +export inline auto is_c_blank = + ctype_is(std::ctype_base::blank, std::locale::classic()); +export inline auto is_c_alnum = + ctype_is(std::ctype_base::alnum, std::locale::classic()); +export inline auto is_c_graph = + ctype_is(std::ctype_base::graph, std::locale::classic()); + +} // namespace nihil diff --git a/nihil.util/next_word.ccm b/nihil.util/next_word.ccm new file mode 100644 index 0000000..c5d3ad7 --- /dev/null +++ b/nihil.util/next_word.ccm @@ -0,0 +1,49 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <algorithm> +#include <locale> +#include <ranges> +#include <string> +#include <utility> + +export module nihil.util:next_word; + +import :skipws; + +namespace nihil { + +/* + * Return the next word from a string_view. Skips leading whitespace, so + * calling this repeatedly will return each word from the string. + */ + +export template<typename Char> [[nodiscard]] +auto next_word(std::basic_string_view<Char> text, + std::locale const &locale = std::locale()) + -> std::pair<std::basic_string_view<Char>, + std::basic_string_view<Char>> +{ + text = skipws(text, locale); + + auto is_space = ctype_is(std::ctype_base::space, locale); + auto split_pos = std::ranges::find_if(text, is_space); + + return {{std::ranges::begin(text), split_pos}, + {split_pos, std::ranges::end(text)}}; +} + +export template<typename Char> +auto next_word(std::basic_string_view<Char> *text, + std::locale const &locale = std::locale()) + -> std::basic_string_view<Char> +{ + auto [word, rest] = next_word(*text, locale); + *text = rest; + return word; +} + +} // namespace nihil diff --git a/nihil.util/nihil.util.ccm b/nihil.util/nihil.util.ccm new file mode 100644 index 0000000..89510c9 --- /dev/null +++ b/nihil.util/nihil.util.ccm @@ -0,0 +1,14 @@ +/* + * This source code is released into the public domain. + */ + +module; + +export module nihil.util; + +export import :capture_stream; +export import :ctype; +export import :parse_size; +export import :next_word; +export import :skipws; +export import :tabulate; diff --git a/nihil.util/parse_size.ccm b/nihil.util/parse_size.ccm new file mode 100644 index 0000000..c95ac50 --- /dev/null +++ b/nihil.util/parse_size.ccm @@ -0,0 +1,107 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <algorithm> +#include <coroutine> +#include <cstdint> +#include <expected> +#include <ranges> +#include <string> +#include <system_error> +#include <utility> + +export module nihil.util:parse_size; + +import nihil.core; +import nihil.error; +import nihil.monad; + +import :ctype; + +namespace nihil { + +template<typename Char> +auto get_multiplier(Char c) -> std::expected<std::uint64_t, error> +{ + auto ret = std::uint64_t{1}; + + switch (c) { + case 'p': case 'P': ret *= 1024; //NOLINT + case 't': case 'T': ret *= 1024; //NOLINT + case 'g': case 'G': ret *= 1024; //NOLINT + case 'm': case 'M': ret *= 1024; //NOLINT + case 'k': case 'K': ret *= 1024; //NOLINT + return ret; + + default: + return std::unexpected(error(errc::invalid_unit)); + } +} + +/* + * Parse a string containing a human-formatted size, such as "1024" + * or "4g". Parsing is always done in the "C" locale and does not + * recognise thousands separators or negative numbers. + */ +export template<typename T, typename Char> [[nodiscard]] +auto parse_size(std::basic_string_view<Char> str) + -> std::expected<T, error> +{ + // Extract the numeric part of the string. + auto it = std::ranges::find_if_not(str, is_c_digit); + auto num_str = std::basic_string_view<Char>( + std::ranges::begin(str), it); + + if (num_str.empty()) + co_return std::unexpected(error(errc::empty_string)); + + auto ret = T{0}; + + for (auto c : num_str) { + if (ret > (std::numeric_limits<T>::max() / 10)) + co_return std::unexpected(error( + std::errc::result_out_of_range)); + ret *= 10; + + auto digit = static_cast<T>(c - '0'); + if ((std::numeric_limits<T>::max() - digit) < ret) + co_return std::unexpected(error( + std::errc::result_out_of_range)); + ret += digit; + } + + if (it == str.end()) + // No multiplier. + co_return ret; + + auto mchar = *it++; + + if (it != str.end()) + // Multiplier is more than one character. + co_return std::unexpected(error(errc::invalid_unit)); + + auto mult = co_await get_multiplier(mchar); + + if (std::cmp_greater(ret, std::numeric_limits<T>::max() / mult)) + co_return std::unexpected(error( + std::errc::result_out_of_range)); + + co_return ret * mult; +} + +export template<typename T> +[[nodiscard]] inline auto parse_size(char const *s) +{ + return parse_size<T>(std::string_view(s)); +} + +export template<typename T> +[[nodiscard]] inline auto parse_size(wchar_t const *s) +{ + return parse_size<T>(std::wstring_view(s)); +} + +} diff --git a/nihil.util/skipws.ccm b/nihil.util/skipws.ccm new file mode 100644 index 0000000..4813ae8 --- /dev/null +++ b/nihil.util/skipws.ccm @@ -0,0 +1,40 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <algorithm> +#include <locale> +#include <ranges> +#include <string> + +export module nihil.util:skipws; + +import :ctype; + +namespace nihil { + +/* + * Remove leading whitespace from a string. + */ + +export template<typename Char> [[nodiscard]] +auto skipws(std::basic_string_view<Char> text, + std::locale const &locale = std::locale()) + -> std::basic_string_view<Char> +{ + auto is_space = ctype_is(std::ctype_base::space, locale); + auto nonws = std::ranges::find_if_not(text, is_space); + return {nonws, std::ranges::end(text)}; +} + +export template<typename Char> +auto skipws(std::basic_string_view<Char> *text, + std::locale const &locale = std::locale()) + -> void +{ + *text = skipws(*text, locale); +} + +} // namespace nihil diff --git a/nihil.util/tabulate.ccm b/nihil.util/tabulate.ccm new file mode 100644 index 0000000..5998b24 --- /dev/null +++ b/nihil.util/tabulate.ccm @@ -0,0 +1,312 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <algorithm> +#include <cstdlib> +#include <format> +#include <ranges> +#include <iterator> +#include <vector> + +export module nihil.util:tabulate; + +import nihil.error; +import :ctype; + +namespace nihil { + +/* + * tabulate: format the given range in an ASCII table and write the output + * to the given output iterator. The range's values will be converted to + * strings as if by std::format. + * + * tabulate is implemented by copying the range; this allows it to work on + * input/forward ranges at the cost of slightly increased memory use. + * + * The table spec is a string consisting of zero or more field formats, + * formatted as {flags:fieldname}; both flags and fieldname are optional. + * If there are fewer field formats than fields, the remaining fields + * are formatted as if by {:}. + * + * The following flags are supported: + * + * < left-align this column (default) + * > right-align this column + */ + +// Exception thrown when a table spec is invalid. +export struct table_spec_error : error { + table_spec_error(std::string_view what) + : error(what) + { + } +}; + +/* + * The specification for a single field. + */ +template<typename Char> +struct field_spec { + enum align_t { left, right }; + + // Get the name of this field. + auto name(this field_spec const &self) + -> std::basic_string_view<Char> + { + return self.m_name; + } + + // Set the name of this field. + auto name(this field_spec &self, + std::basic_string_view<Char> new_name) + -> void + { + self.m_name = new_name; + } + + // Set this field's alignment. + auto align(this field_spec &self, align_t new_align) -> void + { + self.m_align = new_align; + } + + // Ensure the length of this field is at least the given width. + auto ensure_width(this field_spec &self, std::size_t newwidth) + -> void + { + self.m_width = std::max(self.m_width, newwidth); + } + + // Format an object to a string based on our field spec. + [[nodiscard]] auto format(this field_spec const &, auto &&obj) + -> std::basic_string<Char> + { + auto format_string = std::basic_string<Char>{'{', '}'}; + return std::format(std::runtime_format(format_string), obj); + } + + // Print a column value to an output iterator according to our field + // spec. If is_last is true, this is the last field on the line, so + // we won't output any trailling padding. + auto print(this field_spec const &self, + std::basic_string_view<Char> value, + std::output_iterator<Char> auto &out, + bool is_last) + -> void + { + auto padding = self.m_width - value.size(); + + if (self.m_align == right) + for (std::size_t i = 0; i < padding; ++i) + *out++ = ' '; + + std::ranges::copy(value, out); + + if (!is_last && self.m_align == left) + for (std::size_t i = 0; i < padding; ++i) + *out++ = ' '; + } + +private: + std::basic_string_view<Char> m_name; + std::size_t m_width = 0; + align_t m_align = left; +}; + +/* + * The specification for an entire table. + */ +template<typename Char> +struct table_spec { + // Add a new field spec to this table. + auto add(this table_spec &self, field_spec<Char> field) -> void + { + self.m_fields.emplace_back(std::move(field)); + } + + // Return the field spec for a given field. If the field doesn't + // exist, this field and any intermediate fields will be created. + [[nodiscard]] auto field(this table_spec &self, std::size_t fieldnr) + -> field_spec<Char> & + { + if (fieldnr >= self.m_fields.size()) + self.m_fields.resize(fieldnr + 1); + return self.m_fields.at(fieldnr); + } + + // The number of columns in this table. + [[nodiscard]] auto columns(this table_spec const &self) -> std::size_t + { + return self.m_fields.size(); + } + + // Return all the fields in this table. + [[nodiscard]] auto fields(this table_spec const &self) + -> std::vector<field_spec<Char>> const & + { + return self.m_fields; + } + +private: + std::vector<field_spec<Char>> m_fields; +}; + +// Parse the field flags, e.g. '<'. +template<typename Char, + std::input_iterator Iterator, std::sentinel_for<Iterator> Sentinel> +auto parse_field_flags(field_spec<Char> &field, Iterator &pos, Sentinel end) + -> void +{ + while (pos < end) { + switch (*pos) { + case '<': + field.align(field_spec<Char>::left); + break; + case '>': + field.align(field_spec<Char>::right); + break; + case ':': + ++pos; + /*FALLTHROUGH*/ + case '}': + return; + default: + throw table_spec_error("Invalid table spec: " + "unknown flag character"); + } + + if (++pos == end) + throw table_spec_error("Invalid table spec: " + "unterminated field"); + } +} + +// Parse a complete field spec, e.g. "{<:NAME}". +template<typename Char, + std::input_iterator Iterator, std::sentinel_for<Iterator> Sentinel> +[[nodiscard]] auto parse_field(Iterator &pos, Sentinel end) + -> field_spec<Char> +{ + auto field = field_spec<Char>{}; + + if (pos == end) + throw table_spec_error("Invalid table spec: empty field"); + + // The field spec should start with a '{'. + if (*pos != '{') + throw table_spec_error("Invalid table spec: expected '{'"); + + if (++pos == end) + throw table_spec_error("Invalid table spec: unterminated field"); + + // This consumes 'pos' up to and including the ':'. + parse_field_flags(field, pos, end); + + auto brace = std::ranges::find(pos, end, '}'); + if (brace == end) + throw table_spec_error("Invalid table spec: expected '}'"); + + field.name(std::basic_string_view<Char>(pos, brace)); + pos = std::next(brace); + + // The field must be at least as wide as its header. + field.ensure_width(field.name().size()); + + return field; +} + +template<typename Char> +[[nodiscard]] auto parse_table_spec(std::basic_string_view<Char> spec) + -> table_spec<Char> +{ + auto table = table_spec<Char>(); + + auto pos = std::ranges::begin(spec); + auto end = std::ranges::end(spec); + + for (;;) { + // Skip leading whitespace + while (pos < end && is_c_space(*pos)) + ++pos; + + if (pos == end) + break; + + table.add(parse_field<Char>(pos, end)); + } + + return table; +} + +export template<typename Char, + std::ranges::range Range, + std::output_iterator<Char> Iterator> +auto basic_tabulate(std::basic_string_view<Char> table_spec, + Range &&range, + Iterator &&out) + -> void +{ + // Parse the table spec. + auto table = parse_table_spec(table_spec); + + // Create our copy of the input data. + auto data = std::vector<std::vector<std::basic_string<Char>>>(); + // Reserve the first row for the header. + data.resize(1); + + // Find the required length of each field. + for (auto &&row : range) { + // LLVM doesn't have std::enumerate_view yet + auto i = std::size_t{0}; + auto &this_row = data.emplace_back(); + + for (auto &&column : row) { + auto &field = table.field(i); + auto &str = this_row.emplace_back(field.format(column)); + field.ensure_width(str.size()); + ++i; + } + } + + // Add the header row. + for (auto &&field : table.fields()) + data.at(0).emplace_back(std::from_range, field.name()); + + // Print the values. + for (auto &&row : data) { + for (std::size_t i = 0; i < row.size(); ++i) { + auto &field = table.field(i); + bool is_last = (i == row.size() - 1); + + field.print(row[i], out, is_last); + + if (!is_last) + *out++ = ' '; + } + + *out++ = '\n'; + } +} + +export auto tabulate(std::string_view table_spec, + std::ranges::range auto &&range, + std::output_iterator<char> auto &&out) +{ + return basic_tabulate<char>(table_spec, + std::forward<decltype(range)>(range), + std::forward<decltype(out)>(out)); +} + +export auto wtabulate(std::wstring_view table_spec, + std::ranges::range auto &&range, + std::output_iterator<wchar_t> auto &&out) +{ + return basic_tabulate<wchar_t>(table_spec, + std::forward<decltype(range)>(range), + std::forward<decltype(out)>(out)); +} + +} // namespace nihil diff --git a/nihil.util/test_capture_stream.cc b/nihil.util/test_capture_stream.cc new file mode 100644 index 0000000..27c8596 --- /dev/null +++ b/nihil.util/test_capture_stream.cc @@ -0,0 +1,44 @@ +/* + * This source code is released into the public domain. + */ + +#include <iostream> + +#include <catch2/catch_test_macros.hpp> + +import nihil.util; + +TEST_CASE("nihil.util: capture", "[nihil][nihil.util]") +{ + SECTION("std::cout with release()") { + auto cap = nihil::capture_stream(std::cout); + + std::cout << 1 << '+' << 1 << '=' << (1 + 1) << '\n'; + REQUIRE(cap.str() == "1+1=2\n"); + + cap.release(); + REQUIRE(cap.str() == "1+1=2\n"); + } + + SECTION("std::cout with dtor") { + auto cap = nihil::capture_stream(std::cout); + std::cout << 1 << '+' << 1 << '=' << (1 + 1) << '\n'; + REQUIRE(cap.str() == "1+1=2\n"); + } + + SECTION("std::cerr with release()") { + auto cap = nihil::capture_stream(std::cerr); + + std::cerr << 1 << '+' << 1 << '=' << (1 + 1) << '\n'; + REQUIRE(cap.str() == "1+1=2\n"); + + cap.release(); + REQUIRE(cap.str() == "1+1=2\n"); + } + + SECTION("std::cerr with dtor") { + auto cap = nihil::capture_stream(std::cerr); + std::cerr << 1 << '+' << 1 << '=' << (1 + 1) << '\n'; + REQUIRE(cap.str() == "1+1=2\n"); + } +} diff --git a/nihil.util/test_ctype.cc b/nihil.util/test_ctype.cc new file mode 100644 index 0000000..62721d1 --- /dev/null +++ b/nihil.util/test_ctype.cc @@ -0,0 +1,373 @@ +/* + * This source code is released into the public domain. + */ + +#include <catch2/catch_test_macros.hpp> + +import nihil.util; + +TEST_CASE("ctype: space", "[ctype]") { + auto is_utf8_space = + nihil::ctype_is(std::ctype_base::space, + std::locale("C.UTF-8")); + + // '\v' (vertical tab) is a space + REQUIRE(nihil::is_space('\v') == true); + REQUIRE(nihil::is_space(L'\v') == true); + + REQUIRE(nihil::is_c_space('\v') == true); + REQUIRE(nihil::is_c_space(L'\v') == true); + + REQUIRE(is_utf8_space('\v') == true); + REQUIRE(is_utf8_space(L'\v') == true); + + // 'x' is not a space + REQUIRE(nihil::is_space('x') == false); + REQUIRE(nihil::is_space(L'x') == false); + + REQUIRE(nihil::is_c_space('x') == false); + REQUIRE(nihil::is_c_space(L'x') == false); + + REQUIRE(is_utf8_space('x') == false); + REQUIRE(is_utf8_space(L'x') == false); + + // U+2003 EM SPACE is a space + REQUIRE(nihil::is_space(L'\u2003') == false); + REQUIRE(nihil::is_c_space(L'\u2003') == false); + REQUIRE(is_utf8_space(L'\u2003') == true); +} + +TEST_CASE("ctype: print", "[ctype]") { + auto is_utf8_print = + nihil::ctype_is(std::ctype_base::print, + std::locale("C.UTF-8")); + + // 'x' is printable + REQUIRE(nihil::is_print('x') == true); + REQUIRE(nihil::is_print(L'x') == true); + + REQUIRE(nihil::is_c_print('x') == true); + REQUIRE(nihil::is_c_print(L'x') == true); + + REQUIRE(is_utf8_print('x') == true); + REQUIRE(is_utf8_print(L'x') == true); + + // '\003' is not printable + REQUIRE(nihil::is_print('\003') == false); + REQUIRE(nihil::is_print(L'\003') == false); + + REQUIRE(nihil::is_c_print('\003') == false); + REQUIRE(nihil::is_c_print(L'\003') == false); + + REQUIRE(is_utf8_print('\003') == false); + REQUIRE(is_utf8_print(L'\003') == false); + + // U+0410 CYRILLIC CAPITAL LETTER A is printable + REQUIRE(nihil::is_print(L'\u0410') == false); + REQUIRE(nihil::is_c_print(L'\u0410') == false); + REQUIRE(is_utf8_print(L'\u0410') == true); +} + +TEST_CASE("ctype: cntrl", "[ctype]") { + auto is_utf8_cntrl = + nihil::ctype_is(std::ctype_base::cntrl, + std::locale("C.UTF-8")); + + // '\003' is a control character + REQUIRE(nihil::is_cntrl('\003') == true); + REQUIRE(nihil::is_cntrl(L'\003') == true); + + REQUIRE(nihil::is_c_cntrl('\003') == true); + REQUIRE(nihil::is_c_cntrl(L'\003') == true); + + REQUIRE(is_utf8_cntrl('\003') == true); + REQUIRE(is_utf8_cntrl(L'\003') == true); + + + // 'x' is not a control character + REQUIRE(nihil::is_cntrl('x') == false); + REQUIRE(nihil::is_cntrl(L'x') == false); + + REQUIRE(nihil::is_c_cntrl('x') == false); + REQUIRE(nihil::is_c_cntrl(L'x') == false); + + REQUIRE(is_utf8_cntrl('x') == false); + REQUIRE(is_utf8_cntrl(L'x') == false); + + // U+00AD SOFT HYPHEN is a control character. + REQUIRE(nihil::is_cntrl(L'\u00ad') == false); + REQUIRE(nihil::is_c_cntrl(L'\u00ad') == false); + REQUIRE(is_utf8_cntrl(L'\u00ad') == true); +} + +TEST_CASE("ctype: upper", "[ctype]") { + auto is_utf8_upper = + nihil::ctype_is(std::ctype_base::upper, + std::locale("C.UTF-8")); + + // 'A' is upper case + REQUIRE(nihil::is_upper('A') == true); + REQUIRE(nihil::is_upper(L'A') == true); + + REQUIRE(nihil::is_c_upper('A') == true); + REQUIRE(nihil::is_c_upper(L'A') == true); + + REQUIRE(is_utf8_upper('A') == true); + REQUIRE(is_utf8_upper(L'A') == true); + + // 'a' is not upper case + REQUIRE(nihil::is_upper('a') == false); + REQUIRE(nihil::is_upper(L'a') == false); + + REQUIRE(nihil::is_c_upper('a') == false); + REQUIRE(nihil::is_c_upper(L'a') == false); + + REQUIRE(is_utf8_upper('a') == false); + REQUIRE(is_utf8_upper(L'a') == false); + + // U+0410 CYRILLIC CAPITAL LETTER A is upper case + REQUIRE(nihil::is_upper(L'\u0410') == false); + REQUIRE(nihil::is_c_upper(L'\u0410') == false); + REQUIRE(is_utf8_upper(L'\u0410') == true); +} + +TEST_CASE("ctype: lower", "[ctype]") { + auto is_utf8_lower = + nihil::ctype_is(std::ctype_base::lower, + std::locale("C.UTF-8")); + + // 'a' is lower case + REQUIRE(nihil::is_lower('a') == true); + REQUIRE(nihil::is_lower(L'a') == true); + + REQUIRE(nihil::is_c_lower('a') == true); + REQUIRE(nihil::is_c_lower(L'a') == true); + + REQUIRE(is_utf8_lower('a') == true); + REQUIRE(is_utf8_lower(L'a') == true); + + // 'A' is not lower case + REQUIRE(nihil::is_lower('A') == false); + REQUIRE(nihil::is_lower(L'A') == false); + + REQUIRE(nihil::is_c_lower('A') == false); + REQUIRE(nihil::is_c_lower(L'A') == false); + + REQUIRE(is_utf8_lower('A') == false); + REQUIRE(is_utf8_lower(L'A') == false); + + // U+0430 CYRILLIC SMALL LETTER A + REQUIRE(nihil::is_lower(L'\u0430') == false); + REQUIRE(nihil::is_c_lower(L'\u0430') == false); + REQUIRE(is_utf8_lower(L'\u0430') == true); +} + +TEST_CASE("ctype: alpha", "[ctype]") { + auto is_utf8_alpha = + nihil::ctype_is(std::ctype_base::alpha, + std::locale("C.UTF-8")); + + // 'a' is alphabetical + REQUIRE(nihil::is_alpha('a') == true); + REQUIRE(nihil::is_alpha(L'a') == true); + + REQUIRE(nihil::is_c_alpha('a') == true); + REQUIRE(nihil::is_c_alpha(L'a') == true); + + REQUIRE(is_utf8_alpha('a') == true); + REQUIRE(is_utf8_alpha(L'a') == true); + + // '1' is not alphabetical + REQUIRE(nihil::is_alpha('1') == false); + REQUIRE(nihil::is_alpha(L'1') == false); + + REQUIRE(nihil::is_c_alpha('1') == false); + REQUIRE(nihil::is_c_alpha(L'1') == false); + + REQUIRE(is_utf8_alpha('1') == false); + REQUIRE(is_utf8_alpha(L'1') == false); + + // U+0430 CYRILLIC SMALL LETTER A + REQUIRE(nihil::is_alpha(L'\u0430') == false); + REQUIRE(nihil::is_c_alpha(L'\u0430') == false); + REQUIRE(is_utf8_alpha(L'\u0430') == true); +} + +TEST_CASE("ctype: digit", "[ctype]") { + auto is_utf8_digit = + nihil::ctype_is(std::ctype_base::digit, + std::locale("C.UTF-8")); + + // '1' is a digit + REQUIRE(nihil::is_digit('1') == true); + REQUIRE(nihil::is_digit(L'1') == true); + + REQUIRE(nihil::is_c_digit('1') == true); + REQUIRE(nihil::is_c_digit(L'1') == true); + + REQUIRE(is_utf8_digit('1') == true); + REQUIRE(is_utf8_digit(L'1') == true); + + // 'a' is not a digit + REQUIRE(nihil::is_digit('a') == false); + REQUIRE(nihil::is_digit(L'a') == false); + + REQUIRE(nihil::is_c_digit('a') == false); + REQUIRE(nihil::is_c_digit(L'a') == false); + + REQUIRE(is_utf8_digit('a') == false); + REQUIRE(is_utf8_digit(L'a') == false); + + // U+0660 ARABIC-INDIC DIGIT ZERO + REQUIRE(nihil::is_digit(L'\u0660') == false); + REQUIRE(nihil::is_c_digit(L'\u0660') == false); + REQUIRE(is_utf8_digit(L'\u0660') == true); +} + +TEST_CASE("ctype: punct", "[ctype]") { + auto is_utf8_punct = + nihil::ctype_is(std::ctype_base::punct, + std::locale("C.UTF-8")); + + // ';' is punctuation + REQUIRE(nihil::is_punct(';') == true); + REQUIRE(nihil::is_punct(L';') == true); + + REQUIRE(nihil::is_c_punct(';') == true); + REQUIRE(nihil::is_c_punct(L';') == true); + + REQUIRE(is_utf8_punct(';') == true); + REQUIRE(is_utf8_punct(L';') == true); + + // 'a' is not punctuation + REQUIRE(nihil::is_punct('a') == false); + REQUIRE(nihil::is_punct(L'a') == false); + + REQUIRE(nihil::is_c_punct('a') == false); + REQUIRE(nihil::is_c_punct(L'a') == false); + + REQUIRE(is_utf8_punct('a') == false); + REQUIRE(is_utf8_punct(L'a') == false); + + // U+00A1 INVERTED EXCLAMATION MARK + REQUIRE(nihil::is_punct(L'\u00A1') == false); + REQUIRE(nihil::is_c_punct(L'\u00A1') == false); + REQUIRE(is_utf8_punct(L'\u00A1') == true); +} + +TEST_CASE("ctype: xdigit", "[ctype]") { + auto is_utf8_xdigit = + nihil::ctype_is(std::ctype_base::xdigit, + std::locale("C.UTF-8")); + + // 'f' is an xdigit + REQUIRE(nihil::is_xdigit('f') == true); + REQUIRE(nihil::is_xdigit(L'f') == true); + + REQUIRE(nihil::is_c_xdigit('f') == true); + REQUIRE(nihil::is_c_xdigit(L'f') == true); + + REQUIRE(is_utf8_xdigit('f') == true); + REQUIRE(is_utf8_xdigit(L'f') == true); + + // 'g' is not an xdigit + REQUIRE(nihil::is_xdigit('g') == false); + REQUIRE(nihil::is_xdigit(L'g') == false); + + REQUIRE(nihil::is_c_xdigit('g') == false); + REQUIRE(nihil::is_c_xdigit(L'g') == false); + + REQUIRE(is_utf8_xdigit('g') == false); + REQUIRE(is_utf8_xdigit(L'g') == false); +} + +TEST_CASE("ctype: blank", "[ctype]") { + auto is_utf8_blank = + nihil::ctype_is(std::ctype_base::blank, + std::locale("C.UTF-8")); + + // '\t' is a blank + REQUIRE(nihil::is_blank('\t') == true); + REQUIRE(nihil::is_blank(L'\t') == true); + + REQUIRE(nihil::is_c_blank('\t') == true); + REQUIRE(nihil::is_c_blank(L'\t') == true); + + REQUIRE(is_utf8_blank('\t') == true); + REQUIRE(is_utf8_blank(L'\t') == true); + + // '\v' is not a blank + REQUIRE(nihil::is_blank('\v') == false); + REQUIRE(nihil::is_blank(L'\v') == false); + + REQUIRE(nihil::is_c_blank('\v') == false); + REQUIRE(nihil::is_c_blank(L'\v') == false); + + REQUIRE(is_utf8_blank('\v') == false); + REQUIRE(is_utf8_blank(L'\v') == false); + + // There don't seem to be any UTF-8 blank characters, at least + // in FreeBSD libc. +} + +TEST_CASE("ctype: alnum", "[ctype]") { + auto is_utf8_alnum = + nihil::ctype_is(std::ctype_base::alnum, + std::locale("C.UTF-8")); + + // 'a' is alphanumeric + REQUIRE(nihil::is_alnum('a') == true); + REQUIRE(nihil::is_alnum(L'a') == true); + + REQUIRE(nihil::is_c_alnum('a') == true); + REQUIRE(nihil::is_c_alnum(L'a') == true); + + REQUIRE(is_utf8_alnum('a') == true); + REQUIRE(is_utf8_alnum(L'a') == true); + + // '\t' is not a alnum + REQUIRE(nihil::is_alnum('\t') == false); + REQUIRE(nihil::is_alnum(L'\t') == false); + + REQUIRE(nihil::is_c_alnum('\t') == false); + REQUIRE(nihil::is_c_alnum(L'\t') == false); + + REQUIRE(is_utf8_alnum('\t') == false); + REQUIRE(is_utf8_alnum(L'\t') == false); + + // U+0430 CYRILLIC SMALL LETTER A + REQUIRE(nihil::is_alnum(L'\u0430') == false); + REQUIRE(nihil::is_c_alnum(L'\u0430') == false); + REQUIRE(is_utf8_alnum(L'\u0430') == true); +} + +TEST_CASE("ctype: graph", "[ctype]") { + auto is_utf8_graph = + nihil::ctype_is(std::ctype_base::graph, + std::locale("C.UTF-8")); + + // 'a' is graphical + REQUIRE(nihil::is_graph('a') == true); + REQUIRE(nihil::is_graph(L'a') == true); + + REQUIRE(nihil::is_c_graph('a') == true); + REQUIRE(nihil::is_c_graph(L'a') == true); + + REQUIRE(is_utf8_graph('a') == true); + REQUIRE(is_utf8_graph(L'a') == true); + + // '\t' is not graphical + REQUIRE(nihil::is_graph('\t') == false); + REQUIRE(nihil::is_graph(L'\t') == false); + + REQUIRE(nihil::is_c_graph('\t') == false); + REQUIRE(nihil::is_c_graph(L'\t') == false); + + REQUIRE(is_utf8_graph('\t') == false); + REQUIRE(is_utf8_graph(L'\t') == false); + + // U+0430 CYRILLIC SMALL LETTER A + REQUIRE(nihil::is_graph(L'\u0430') == false); + REQUIRE(nihil::is_c_graph(L'\u0430') == false); + REQUIRE(is_utf8_graph(L'\u0430') == true); +} diff --git a/nihil.util/test_next_word.cc b/nihil.util/test_next_word.cc new file mode 100644 index 0000000..7e61237 --- /dev/null +++ b/nihil.util/test_next_word.cc @@ -0,0 +1,65 @@ +/* + * This source code is released into the public domain. + */ + +#include <locale> +#include <string> + +#include <catch2/catch_test_macros.hpp> + +import nihil.util; + +TEST_CASE("next_word: basic", "[next_word]") +{ + using namespace std::literals; + auto s = "foo bar baz"sv; + + auto words = nihil::next_word(s); + REQUIRE(words.first == "foo"); + REQUIRE(words.second == " bar baz"); + + auto word = nihil::next_word(&s); + REQUIRE(word == "foo"); + REQUIRE(s == " bar baz"); +} + +TEST_CASE("next_word: multiple spaces", "[next_word]") +{ + using namespace std::literals; + auto s = "foo bar baz"sv; + + auto words = nihil::next_word(s); + REQUIRE(words.first == "foo"); + REQUIRE(words.second == " bar baz"); + + auto word = nihil::next_word(&s); + REQUIRE(word == "foo"); + REQUIRE(s == " bar baz"); +} + +TEST_CASE("next_word: leading spaces", "[next_word]") +{ + using namespace std::literals; + auto s = " \tfoo bar baz"sv; + + auto words = nihil::next_word(s); + REQUIRE(words.first == "foo"); + REQUIRE(words.second == " bar baz"); + + auto word = nihil::next_word(&s); + REQUIRE(word == "foo"); + REQUIRE(s == " bar baz"); +} + +TEST_CASE("next_word: locale", "[next_word]") +{ + using namespace std::literals; + auto s = L"\u2003foo\u2003bar\u2003baz"sv; + + auto words = nihil::next_word(s); + REQUIRE(words.first == s); + + words = nihil::next_word(s, std::locale("C.UTF-8")); + REQUIRE(words.first == L"foo"); + REQUIRE(words.second == L"\u2003bar\u2003baz"); +} diff --git a/nihil.util/test_parse_size.cc b/nihil.util/test_parse_size.cc new file mode 100644 index 0000000..692039b --- /dev/null +++ b/nihil.util/test_parse_size.cc @@ -0,0 +1,169 @@ +/* + * This source code is released into the public domain. + */ + +#include <cstdint> +#include <system_error> + +#include <catch2/catch_test_macros.hpp> + +import nihil.core; +import nihil.error; +import nihil.util; + +TEST_CASE("parse_size: empty value", "[nihil]") +{ + using namespace nihil; + + auto n = parse_size<std::uint64_t>(""); + REQUIRE(!n); + REQUIRE(n.error() == nihil::errc::empty_string); +} + +TEST_CASE("parse_size: basic", "[nihil]") +{ + using namespace nihil; + + SECTION("bare number") { + auto n = parse_size<std::uint64_t>("1024").value(); + REQUIRE(n == 1024); + } + + SECTION("max value, unsigned") { + auto n = parse_size<std::uint16_t>("65535").value(); + REQUIRE(n == 65535); + } + + SECTION("max value, signed") { + auto n = parse_size<std::uint16_t>("32767").value(); + REQUIRE(n == 32767); + } + + SECTION("overflow by 1, unsigned") { + auto n = parse_size<std::uint16_t>("65536"); + REQUIRE(!n); + REQUIRE(n.error() == std::errc::result_out_of_range); + } + + SECTION("overflow by 1, signed") { + auto n = parse_size<std::int16_t>("32768"); + REQUIRE(!n); + REQUIRE(n.error() == std::errc::result_out_of_range); + } + + SECTION("overflow by many, unsigned") { + auto n = parse_size<std::uint16_t>("100000"); + REQUIRE(!n); + REQUIRE(n.error() == std::errc::result_out_of_range); + } + + SECTION("overflow by many, signed") { + auto n = parse_size<std::int16_t>("100000"); + REQUIRE(!n); + REQUIRE(n.error() == std::errc::result_out_of_range); + } +} + +TEST_CASE("parse_size: invalid multiplier", "[nihil]") +{ + using namespace nihil; + + auto n = parse_size<std::uint64_t>("4z"); + REQUIRE(!n); + REQUIRE(n.error() == nihil::errc::invalid_unit); + + n = parse_size<std::uint64_t>("4kz"); + REQUIRE(!n); + REQUIRE(n.error() == nihil::errc::invalid_unit); +} + +TEST_CASE("parse_size: multipliers", "[nihil]") +{ + using namespace nihil; + + auto sf = static_cast<std::uint64_t>(4); + + SECTION("k") { + auto n = parse_size<std::uint64_t>("4k").value(); + REQUIRE(n == sf * 1024); + } + + SECTION("m") { + auto n = parse_size<std::uint64_t>("4m").value(); + REQUIRE(n == sf * 1024 * 1024); + } + + SECTION("g") { + auto n = parse_size<std::uint64_t>("4g").value(); + REQUIRE(n == sf * 1024 * 1024 * 1024); + } + + SECTION("t") { + auto n = parse_size<std::uint64_t>("4t").value(); + REQUIRE(n == sf * 1024 * 1024 * 1024 * 1024); + } + + SECTION("p") { + auto n = parse_size<std::uint64_t>("4p").value(); + REQUIRE(n == sf * 1024 * 1024 * 1024 * 1024 * 1024); + } +} + +TEST_CASE("parse_size: multiplier overflow", "[nihil]") +{ + using namespace nihil; + + SECTION("signed") { + auto n = parse_size<std::uint16_t>("64k"); + REQUIRE(!n); + REQUIRE(n.error() == std::errc::result_out_of_range); + } + + SECTION("unsigned") { + auto n = parse_size<std::int16_t>("32k"); + REQUIRE(!n); + REQUIRE(n.error() == std::errc::result_out_of_range); + } +} + +TEST_CASE("parse_size: wide", "[nihil]") +{ + using namespace nihil; + + SECTION("bare number") { + auto n = parse_size<std::uint64_t>(L"1024").value(); + REQUIRE(n == 1024); + } +} + +TEST_CASE("parse_size: wide multipliers", "[nihil]") +{ + using namespace nihil; + + auto sf = static_cast<std::uint64_t>(4); + + SECTION("k") { + auto n = parse_size<std::uint64_t>(L"4k").value(); + REQUIRE(n == sf * 1024); + } + + SECTION("m") { + auto n = parse_size<std::uint64_t>(L"4m").value(); + REQUIRE(n == sf * 1024 * 1024); + } + + SECTION("g") { + auto n = parse_size<std::uint64_t>(L"4g").value(); + REQUIRE(n == sf * 1024 * 1024 * 1024); + } + + SECTION("t") { + auto n = parse_size<std::uint64_t>(L"4t").value(); + REQUIRE(n == sf * 1024 * 1024 * 1024 * 1024); + } + + SECTION("p") { + auto n = parse_size<std::uint64_t>(L"4p").value(); + REQUIRE(n == sf * 1024 * 1024 * 1024 * 1024 * 1024); + } +} diff --git a/nihil.util/test_skipws.cc b/nihil.util/test_skipws.cc new file mode 100644 index 0000000..837c1f3 --- /dev/null +++ b/nihil.util/test_skipws.cc @@ -0,0 +1,45 @@ +/* + * This source code is released into the public domain. + */ + +#include <locale> +#include <string> +using namespace std::literals; + +#include <catch2/catch_test_macros.hpp> + +import nihil.util; + +TEST_CASE("skipws: basic", "[skipws]") +{ + REQUIRE(nihil::skipws("foo"sv) == "foo"); + REQUIRE(nihil::skipws(" foo"sv) == "foo"); + REQUIRE(nihil::skipws("foo "sv) == "foo "); + REQUIRE(nihil::skipws("foo bar"sv) == "foo bar"); +} + +TEST_CASE("skipws: pointer", "[skipws]") +{ + auto s = "foo"sv; + nihil::skipws(&s); + REQUIRE(s == "foo"); + + s = " foo"sv; + nihil::skipws(&s); + REQUIRE(s == "foo"); + + s = "foo "sv; + nihil::skipws(&s); + REQUIRE(s == "foo "); + + s = "foo bar"sv; + nihil::skipws(&s); + REQUIRE(s == "foo bar"); +} + +TEST_CASE("skipws: locale", "[skipws]") +{ + // Assume the default locale is C. + REQUIRE(nihil::skipws(L"\u2003foo"sv) == L"\u2003foo"); + REQUIRE(nihil::skipws(L"\u2003foo"sv, std::locale("C.UTF-8")) == L"foo"); +} diff --git a/nihil.util/test_tabulate.cc b/nihil.util/test_tabulate.cc new file mode 100644 index 0000000..8dee796 --- /dev/null +++ b/nihil.util/test_tabulate.cc @@ -0,0 +1,75 @@ +/* + * This source code is released into the public domain. + */ + +#include <iterator> +#include <string> +#include <vector> + +#include <catch2/catch_test_macros.hpp> + +import nihil.util; + +using namespace std::literals; +using namespace nihil; + +TEST_CASE("tabulate: basic", "[tabulate]") +{ + auto input = std::vector{ + std::vector{"a", "foo", "b"}, + std::vector{"bar", "c", "baz"}, + }; + + auto result = std::string(); + tabulate("{:1} {:2} {:3}", input, std::back_inserter(result)); + REQUIRE(result == +"1 2 3\n" +"a foo b\n" +"bar c baz\n"); +} + +TEST_CASE("tabulate: basic wide", "[tabulate]") +{ + auto input = std::vector{ + std::vector{L"a", L"foo", L"b"}, + std::vector{L"bar", L"c", L"baz"}, + }; + + auto result = std::wstring(); + wtabulate(L"{:1} {:2} {:3}", input, std::back_inserter(result)); + + REQUIRE(result == +L"1 2 3\n" +"a foo b\n" +"bar c baz\n"); +} + +TEST_CASE("tabulate: jagged", "[tabulate]") +{ + auto input = std::vector{ + std::vector{"a", "foo", "b"}, + std::vector{"bar", "baz"}, + }; + + auto result = std::string(); + tabulate("{:1} {:2} {:3}", input, std::back_inserter(result)); + REQUIRE(result == +"1 2 3\n" +"a foo b\n" +"bar baz\n"); +} + +TEST_CASE("tabulate: align", "[tabulate]") +{ + auto input = std::vector{ + std::vector{"a", "longvalue", "s"}, + std::vector{"a", "s", "longvalue"}, + }; + + auto result = std::string(); + tabulate("{:1} {<:2} {>:3}", input, std::back_inserter(result)); + REQUIRE(result == +"1 2 3\n" +"a longvalue s\n" +"a s longvalue\n"); +} diff --git a/nihil.uuid/CMakeLists.txt b/nihil.uuid/CMakeLists.txt new file mode 100644 index 0000000..fe037e7 --- /dev/null +++ b/nihil.uuid/CMakeLists.txt @@ -0,0 +1,23 @@ +# This source code is released into the public domain. + +add_library(nihil.uuid STATIC) +target_sources(nihil.uuid + PUBLIC FILE_SET modules TYPE CXX_MODULES FILES + uuid.ccm +) + +if(NIHIL_TESTS) + enable_testing() + + add_executable(nihil.uuid.test test.cc) + target_link_libraries(nihil.uuid.test PRIVATE + nihil.uuid + Catch2::Catch2WithMain + ) + + find_package(Catch2 REQUIRED) + + include(CTest) + include(Catch) + catch_discover_tests(nihil.uuid.test) +endif() diff --git a/nihil.uuid/test.cc b/nihil.uuid/test.cc new file mode 100644 index 0000000..0f21298 --- /dev/null +++ b/nihil.uuid/test.cc @@ -0,0 +1,1001 @@ +/* + * From https://github.com/mariusbancila/stduuid + * + * Copyright (c) 2017 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include <algorithm> +#include <random> +#include <set> +#include <unordered_set> + +#include <catch2/catch_test_macros.hpp> + +//NOLINTBEGIN(bugprone-unchecked-optional-access) + +namespace +{ + +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0205r0.html +template <typename EngineT, std::size_t StateSize = EngineT::state_size> +void seed_rng(EngineT& engine) +{ + using engine_type = typename EngineT::result_type; + using device_type = std::random_device::result_type; + using seedseq_type = std::seed_seq::result_type; + + constexpr auto bytes_needed = StateSize * sizeof(engine_type); + constexpr auto numbers_needed = + (sizeof(device_type) < sizeof(seedseq_type)) + ? (bytes_needed / sizeof(device_type)) + : (bytes_needed / sizeof(seedseq_type)); + + auto numbers = std::array<device_type, numbers_needed>{}; + auto rnddev = std::random_device{}; + std::ranges::generate(numbers, std::ref(rnddev)); + + auto seedseq = std::seed_seq(std::cbegin(numbers), + std::cend(numbers)); + engine.seed(seedseq); +} + +} // anonymous namespace + +import nihil.uuid; + +using namespace nihil; + +TEST_CASE("uuid: Test multiple default generators", "[uuid]") +{ + uuid id1; + uuid id2; + + { + std::random_device rd; + auto seed_data = std::array<int, std::mt19937::state_size> {}; + std::ranges::generate(seed_data, std::ref(rd)); + std::seed_seq seq(std::begin(seed_data), std::end(seed_data)); + std::mt19937 generator(seq); + + id1 = uuid_random_generator{ generator }(); + REQUIRE(!id1.is_nil()); + REQUIRE(id1.version() == uuid_version::random_number_based); + REQUIRE(id1.variant() == uuid_variant::rfc); + } + + { + std::random_device rd; + auto seed_data = std::array<int, std::mt19937::state_size> {}; + std::ranges::generate(seed_data, std::ref(rd)); + std::seed_seq seq(std::begin(seed_data), std::end(seed_data)); + std::mt19937 generator(seq); + + id2 = uuid_random_generator{ generator }(); + REQUIRE(!id2.is_nil()); + REQUIRE(id2.version() == uuid_version::random_number_based); + REQUIRE(id2.variant() == uuid_variant::rfc); + } + + REQUIRE(id1 != id2); +} + +TEST_CASE("uuid: Test default generator", "[uuid]") +{ + std::random_device rd; + auto seed_data = std::array<int, std::mt19937::state_size> {}; + std::ranges::generate(seed_data, std::ref(rd)); + std::seed_seq seq(std::begin(seed_data), std::end(seed_data)); + std::mt19937 generator(seq); + + uuid const guid = uuid_random_generator{generator}(); + REQUIRE(!guid.is_nil()); + REQUIRE(guid.version() == uuid_version::random_number_based); + REQUIRE(guid.variant() == uuid_variant::rfc); +} + +TEST_CASE("uuid: Test random generator (conversion ctor w/ smart ptr)", + "[uuid]") +{ + std::random_device rd; + auto seed_data = std::array<int, std::mt19937::state_size> {}; + std::ranges::generate(seed_data, std::ref(rd)); + std::seed_seq seq(std::begin(seed_data), std::end(seed_data)); + std::mt19937 generator(seq); + + uuid_random_generator dgen(&generator); + auto id1 = dgen(); + REQUIRE(!id1.is_nil()); + REQUIRE(id1.version() == uuid_version::random_number_based); + REQUIRE(id1.variant() == uuid_variant::rfc); + + auto id2 = dgen(); + REQUIRE(!id2.is_nil()); + REQUIRE(id2.version() == uuid_version::random_number_based); + REQUIRE(id2.variant() == uuid_variant::rfc); + + REQUIRE(id1 != id2); +} + +TEST_CASE("uuid: Test random generator (conversion ctor w/ ptr)", "[uuid]") +{ + std::random_device rd; + auto seed_data = std::array<int, std::mt19937::state_size> {}; + std::ranges::generate(seed_data, std::ref(rd)); + std::seed_seq seq(std::begin(seed_data), std::end(seed_data)); + auto generator = std::make_unique<std::mt19937>(seq); + + uuid_random_generator dgen(generator.get()); + auto id1 = dgen(); + REQUIRE(!id1.is_nil()); + REQUIRE(id1.version() == uuid_version::random_number_based); + REQUIRE(id1.variant() == uuid_variant::rfc); + + auto id2 = dgen(); + REQUIRE(!id1.is_nil()); + REQUIRE(id2.version() == uuid_version::random_number_based); + REQUIRE(id2.variant() == uuid_variant::rfc); + + REQUIRE(id1 != id2); +} + +TEST_CASE("uuid: Test random generator (conversion ctor w/ ref)", "[uuid]") +{ + std::random_device rd; + auto seed_data = std::array<int, std::mt19937::state_size> {}; + std::ranges::generate(seed_data, std::ref(rd)); + std::seed_seq seq(std::begin(seed_data), std::end(seed_data)); + std::mt19937 generator(seq); + + uuid_random_generator dgen(generator); + auto id1 = dgen(); + REQUIRE(!id1.is_nil()); + REQUIRE(id1.version() == uuid_version::random_number_based); + REQUIRE(id1.variant() == uuid_variant::rfc); + + auto id2 = dgen(); + REQUIRE(!id2.is_nil()); + REQUIRE(id2.version() == uuid_version::random_number_based); + REQUIRE(id2.variant() == uuid_variant::rfc); + + REQUIRE(id1 != id2); +} + +TEST_CASE("uuid: Test basic random generator (conversion ctor w/ ptr) " + "w/ ranlux48_base", "[uuid]") +{ + std::random_device rd; + auto seed_data = std::array<int, 6> {}; + std::ranges::generate(seed_data, std::ref(rd)); + std::seed_seq seq(std::begin(seed_data), std::end(seed_data)); + std::ranlux48_base generator(seq); + + basic_uuid_random_generator<std::ranlux48_base> dgen(&generator); + auto id1 = dgen(); + REQUIRE(!id1.is_nil()); + REQUIRE(id1.version() == uuid_version::random_number_based); + REQUIRE(id1.variant() == uuid_variant::rfc); + + auto id2 = dgen(); + REQUIRE(!id2.is_nil()); + REQUIRE(id2.version() == uuid_version::random_number_based); + REQUIRE(id2.variant() == uuid_variant::rfc); + + REQUIRE(id1 != id2); +} + +TEST_CASE("uuid: Test basic random generator (conversion ctor w/ smart ptr) " + "w/ ranlux48_base", "[uuid]") +{ + std::random_device rd; + auto seed_data = std::array<int, 6> {}; + std::ranges::generate(seed_data, std::ref(rd)); + std::seed_seq seq(std::begin(seed_data), std::end(seed_data)); + auto generator = std::make_unique<std::ranlux48_base>(seq); + + basic_uuid_random_generator<std::ranlux48_base> dgen(generator.get()); + auto id1 = dgen(); + REQUIRE(!id1.is_nil()); + REQUIRE(id1.version() == uuid_version::random_number_based); + REQUIRE(id1.variant() == uuid_variant::rfc); + + auto id2 = dgen(); + REQUIRE(!id2.is_nil()); + REQUIRE(id2.version() == uuid_version::random_number_based); + REQUIRE(id2.variant() == uuid_variant::rfc); + + REQUIRE(id1 != id2); +} + +TEST_CASE("uuid: Test basic random generator (conversion ctor w/ ref) " + "w/ ranlux48_base", "[uuid]") +{ + std::random_device rd; + auto seed_data = std::array<int, 6> {}; + std::ranges::generate(seed_data, std::ref(rd)); + std::seed_seq seq(std::begin(seed_data), std::end(seed_data)); + std::ranlux48_base generator(seq); + + basic_uuid_random_generator<std::ranlux48_base> dgen(generator); + auto id1 = dgen(); + REQUIRE(!id1.is_nil()); + REQUIRE(id1.version() == uuid_version::random_number_based); + REQUIRE(id1.variant() == uuid_variant::rfc); + + auto id2 = dgen(); + REQUIRE(!id2.is_nil()); + REQUIRE(id2.version() == uuid_version::random_number_based); + REQUIRE(id2.variant() == uuid_variant::rfc); + + REQUIRE(id1 != id2); +} + +TEST_CASE("uuid: Test namespaces", "[uuid]") +{ + REQUIRE(uuid_namespace_dns == uuid::from_string("6ba7b810-9dad-11d1-80b4-00c04fd430c8")); + REQUIRE(uuid_namespace_url == uuid::from_string("6ba7b811-9dad-11d1-80b4-00c04fd430c8")); + REQUIRE(uuid_namespace_oid == uuid::from_string("6ba7b812-9dad-11d1-80b4-00c04fd430c8")); + REQUIRE(uuid_namespace_x500 == uuid::from_string("6ba7b814-9dad-11d1-80b4-00c04fd430c8")); +} + +TEST_CASE("uuid: Test name generator (char*)", "[uuid]") +{ + uuid_name_generator dgen(uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43").value()); + + auto id1 = dgen("john"); + REQUIRE(!id1.is_nil()); + REQUIRE(id1.version() == uuid_version::name_based_sha1); + REQUIRE(id1.variant() == uuid_variant::rfc); + + auto id2 = dgen("jane"); + REQUIRE(!id2.is_nil()); + REQUIRE(id2.version() == uuid_version::name_based_sha1); + REQUIRE(id2.variant() == uuid_variant::rfc); + + auto id3 = dgen("jane"); + REQUIRE(!id3.is_nil()); + REQUIRE(id3.version() == uuid_version::name_based_sha1); + REQUIRE(id3.variant() == uuid_variant::rfc); + + auto id4 = dgen(L"jane"); + REQUIRE(!id4.is_nil()); + REQUIRE(id4.version() == uuid_version::name_based_sha1); + REQUIRE(id4.variant() == uuid_variant::rfc); + + REQUIRE(id1 != id2); + REQUIRE(id2 == id3); + REQUIRE(id3 != id4); +} + +TEST_CASE("uuid: Test name generator (std::string)", "[uuid]") +{ + using namespace std::string_literals; + + uuid_name_generator dgen(uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43").value()); + auto id1 = dgen("john"s); + REQUIRE(!id1.is_nil()); + REQUIRE(id1.version() == uuid_version::name_based_sha1); + REQUIRE(id1.variant() == uuid_variant::rfc); + + auto id2 = dgen("jane"s); + REQUIRE(!id2.is_nil()); + REQUIRE(id2.version() == uuid_version::name_based_sha1); + REQUIRE(id2.variant() == uuid_variant::rfc); + + auto id3 = dgen("jane"s); + REQUIRE(!id3.is_nil()); + REQUIRE(id3.version() == uuid_version::name_based_sha1); + REQUIRE(id3.variant() == uuid_variant::rfc); + + auto id4 = dgen(L"jane"s); + REQUIRE(!id4.is_nil()); + REQUIRE(id4.version() == uuid_version::name_based_sha1); + REQUIRE(id4.variant() == uuid_variant::rfc); + + REQUIRE(id1 != id2); + REQUIRE(id2 == id3); + REQUIRE(id3 != id4); +} + +TEST_CASE("uuid: Test name generator (std::string_view)", "[uuid]") +{ + using namespace std::string_view_literals; + + uuid_name_generator dgen(uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43").value()); + auto id1 = dgen("john"sv); + REQUIRE(!id1.is_nil()); + REQUIRE(id1.version() == uuid_version::name_based_sha1); + REQUIRE(id1.variant() == uuid_variant::rfc); + + auto id2 = dgen("jane"sv); + REQUIRE(!id2.is_nil()); + REQUIRE(id2.version() == uuid_version::name_based_sha1); + REQUIRE(id2.variant() == uuid_variant::rfc); + + auto id3 = dgen("jane"sv); + REQUIRE(!id3.is_nil()); + REQUIRE(id3.version() == uuid_version::name_based_sha1); + REQUIRE(id3.variant() == uuid_variant::rfc); + + auto id4 = dgen(L"jane"sv); + REQUIRE(!id4.is_nil()); + REQUIRE(id4.version() == uuid_version::name_based_sha1); + REQUIRE(id4.variant() == uuid_variant::rfc); + + REQUIRE(id1 != id2); + REQUIRE(id2 == id3); + REQUIRE(id3 != id4); +} + +TEST_CASE("uuid: Test name generator equality (char const*, std::string, " + "std::string_view)", "[uuid]") +{ + using namespace std::literals; + + auto dgen = uuid_name_generator(uuid::from_string( + "47183823-2574-4bfd-b411-99ed177d3e43").value()); + auto id1 = dgen("john"); + auto id2 = dgen("john"s); + auto id3 = dgen("john"sv); + + REQUIRE(id1 == id2); + REQUIRE(id2 == id3); +} + +TEST_CASE("uuid: Test default constructor", "[uuid]") +{ + auto empty = uuid(); + REQUIRE(empty.is_nil()); +} + +TEST_CASE("uuid: Test string conversion", "[uuid]") +{ + auto empty = uuid(); + REQUIRE(to_string(empty) == + "00000000-0000-0000-0000-000000000000"); + REQUIRE(to_string<wchar_t>(empty) == + L"00000000-0000-0000-0000-000000000000"); +} + +TEST_CASE("uuid: Test is_valid_uuid(char*)", "[uuid]") +{ + REQUIRE(uuid::is_valid_uuid( + "47183823-2574-4bfd-b411-99ed177d3e43")); + REQUIRE(uuid::is_valid_uuid( + "{47183823-2574-4bfd-b411-99ed177d3e43}")); + REQUIRE(uuid::is_valid_uuid( + L"47183823-2574-4bfd-b411-99ed177d3e43")); + REQUIRE(uuid::is_valid_uuid( + L"{47183823-2574-4bfd-b411-99ed177d3e43}")); + REQUIRE(uuid::is_valid_uuid( + "00000000-0000-0000-0000-000000000000")); + REQUIRE(uuid::is_valid_uuid( + "{00000000-0000-0000-0000-000000000000}")); + REQUIRE(uuid::is_valid_uuid( + L"00000000-0000-0000-0000-000000000000")); + REQUIRE(uuid::is_valid_uuid( + L"{00000000-0000-0000-0000-000000000000}")); +} + +TEST_CASE("uuid: Test is_valid_uuid(basic_string)", "[uuid]") +{ + using namespace std::string_literals; + + { + auto str = "47183823-2574-4bfd-b411-99ed177d3e43"s; + REQUIRE(uuid::is_valid_uuid(str)); + } + + { + auto str = "{47183823-2574-4bfd-b411-99ed177d3e43}"s; + REQUIRE(uuid::is_valid_uuid(str)); + } + + { + auto str = L"47183823-2574-4bfd-b411-99ed177d3e43"s; + REQUIRE(uuid::is_valid_uuid(str)); + } + + { + auto str = L"{47183823-2574-4bfd-b411-99ed177d3e43}"s; + REQUIRE(uuid::is_valid_uuid(str)); + } + + { + auto str = "00000000-0000-0000-0000-000000000000"s; + REQUIRE(uuid::is_valid_uuid(str)); + } + + { + auto str = "{00000000-0000-0000-0000-000000000000}"s; + REQUIRE(uuid::is_valid_uuid(str)); + } + + { + auto str = L"00000000-0000-0000-0000-000000000000"s; + REQUIRE(uuid::is_valid_uuid(str)); + } + + { + auto str = L"{00000000-0000-0000-0000-000000000000}"s; + REQUIRE(uuid::is_valid_uuid(str)); + } +} + +TEST_CASE("uuid: Test is_valid_uuid(basic_string_view)", "[uuid]") +{ + using namespace std::string_view_literals; + + REQUIRE(uuid::is_valid_uuid( + "47183823-2574-4bfd-b411-99ed177d3e43"sv)); + REQUIRE(uuid::is_valid_uuid( + "{47183823-2574-4bfd-b411-99ed177d3e43}"sv)); + REQUIRE(uuid::is_valid_uuid( + L"47183823-2574-4bfd-b411-99ed177d3e43"sv)); + REQUIRE(uuid::is_valid_uuid( + L"{47183823-2574-4bfd-b411-99ed177d3e43}"sv)); + REQUIRE(uuid::is_valid_uuid( + "00000000-0000-0000-0000-000000000000"sv)); + REQUIRE(uuid::is_valid_uuid( + "{00000000-0000-0000-0000-000000000000}"sv)); + REQUIRE(uuid::is_valid_uuid( + L"00000000-0000-0000-0000-000000000000"sv)); + REQUIRE(uuid::is_valid_uuid( + L"{00000000-0000-0000-0000-000000000000}"sv)); +} + +TEST_CASE("uuid: Test is_valid_uuid(char*) invalid format", "[uuid]") +{ + REQUIRE(!uuid::is_valid_uuid("")); + REQUIRE(!uuid::is_valid_uuid("{}")); + REQUIRE(!uuid::is_valid_uuid( + "47183823-2574-4bfd-b411-99ed177d3e4")); + REQUIRE(!uuid::is_valid_uuid( + "47183823-2574-4bfd-b411-99ed177d3e430")); + REQUIRE(!uuid::is_valid_uuid( + "{47183823-2574-4bfd-b411-99ed177d3e43")); + REQUIRE(!uuid::is_valid_uuid( + "47183823-2574-4bfd-b411-99ed177d3e43}")); +} + +TEST_CASE("uuid: Test is_valid_uuid(basic_string) invalid format", "[uuid]") +{ + using namespace std::string_literals; + + { + auto str = ""s; + REQUIRE(!uuid::is_valid_uuid(str)); + } + + { + auto str = "{}"s; + REQUIRE(!uuid::is_valid_uuid(str)); + } + + { + auto str = "47183823-2574-4bfd-b411-99ed177d3e4"s; + REQUIRE(!uuid::is_valid_uuid(str)); + } + + { + auto str = "47183823-2574-4bfd-b411-99ed177d3e430"s; + REQUIRE(!uuid::is_valid_uuid(str)); + } + + { + auto str = "{47183823-2574-4bfd-b411-99ed177d3e43"s; + REQUIRE(!uuid::is_valid_uuid(str)); + } + + { + auto str = "47183823-2574-4bfd-b411-99ed177d3e43}"s; + REQUIRE(!uuid::is_valid_uuid(str)); + } +} + +TEST_CASE("uuid: Test is_valid_uuid(basic_string_view) invalid format", + "[uuid]") +{ + using namespace std::string_view_literals; + + REQUIRE(!uuid::is_valid_uuid(""sv)); + REQUIRE(!uuid::is_valid_uuid("{}"sv)); + REQUIRE(!uuid::is_valid_uuid( + "47183823-2574-4bfd-b411-99ed177d3e4"sv)); + REQUIRE(!uuid::is_valid_uuid( + "47183823-2574-4bfd-b411-99ed177d3e430"sv)); + REQUIRE(!uuid::is_valid_uuid( + "{47183823-2574-4bfd-b411-99ed177d3e43"sv)); + REQUIRE(!uuid::is_valid_uuid( + "47183823-2574-4bfd-b411-99ed177d3e43}"sv)); +} + +TEST_CASE("uuid: Test from_string(char*)", "[uuid]") +{ + { + auto str = "47183823-2574-4bfd-b411-99ed177d3e43"; + auto guid = uuid::from_string(str).value(); + REQUIRE(to_string(guid) == str); + } + + { + auto str = "{47183823-2574-4bfd-b411-99ed177d3e43}"; + auto guid = uuid::from_string(str).value(); + REQUIRE(to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43"); + } + + { + auto guid = uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43").value(); + REQUIRE(to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43"); + REQUIRE(to_string<wchar_t>(guid) == L"47183823-2574-4bfd-b411-99ed177d3e43"); + } + + { + auto str = L"47183823-2574-4bfd-b411-99ed177d3e43"; + auto guid = uuid::from_string(str).value(); + REQUIRE(to_string<wchar_t>(guid) == str); + } + + { + auto str = "4718382325744bfdb41199ed177d3e43"; + REQUIRE_NOTHROW(uuid::from_string(str)); + REQUIRE(uuid::from_string(str).has_value()); + } + + { + auto str = "00000000-0000-0000-0000-000000000000"; + auto guid = uuid::from_string(str).value(); + REQUIRE(guid.is_nil()); + } + + { + auto str = "{00000000-0000-0000-0000-000000000000}"; + auto guid = uuid::from_string(str).value(); + REQUIRE(guid.is_nil()); + } + + { + auto str = L"00000000-0000-0000-0000-000000000000"; + auto guid = uuid::from_string(str).value(); + REQUIRE(guid.is_nil()); + } + + { + auto str = L"{00000000-0000-0000-0000-000000000000}"; + auto guid = uuid::from_string(str).value(); + REQUIRE(guid.is_nil()); + } +} + +TEST_CASE("uuid: Test from_string(basic_string)", "[uuid]") +{ + using namespace std::string_literals; + + { + auto str = "47183823-2574-4bfd-b411-99ed177d3e43"s; + auto guid = uuid::from_string(str).value(); + REQUIRE(to_string(guid) == str); + } + + { + auto str = "{47183823-2574-4bfd-b411-99ed177d3e43}"s; + auto guid = uuid::from_string(str).value(); + REQUIRE(to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43"); + } + + { + auto guid = uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43"s).value(); + REQUIRE(to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43"); + REQUIRE(to_string<wchar_t>(guid) == L"47183823-2574-4bfd-b411-99ed177d3e43"); + } + + { + auto str = L"47183823-2574-4bfd-b411-99ed177d3e43"s; + auto guid = uuid::from_string(str).value(); + REQUIRE(to_string<wchar_t>(guid) == str); + } + + { + auto str = "4718382325744bfdb41199ed177d3e43"s; + REQUIRE_NOTHROW(uuid::from_string(str)); + REQUIRE(uuid::from_string(str).has_value()); + } + + { + auto str = "00000000-0000-0000-0000-000000000000"s; + auto guid = uuid::from_string(str).value(); + REQUIRE(guid.is_nil()); + } + + { + auto str = "{00000000-0000-0000-0000-000000000000}"s; + auto guid = uuid::from_string(str).value(); + REQUIRE(guid.is_nil()); + } + + { + auto str = L"00000000-0000-0000-0000-000000000000"s; + auto guid = uuid::from_string(str).value(); + REQUIRE(guid.is_nil()); + } + + { + auto str = L"{00000000-0000-0000-0000-000000000000}"s; + auto guid = uuid::from_string(str).value(); + REQUIRE(guid.is_nil()); + } +} + +TEST_CASE("uuid: Test from_string(basic_string_view)", "[uuid]") +{ + using namespace std::string_view_literals; + + { + auto str = "47183823-2574-4bfd-b411-99ed177d3e43"sv; + auto guid = uuid::from_string(str).value(); + REQUIRE(to_string(guid) == str); + } + + { + auto str = "{47183823-2574-4bfd-b411-99ed177d3e43}"sv; + auto guid = uuid::from_string(str).value(); + REQUIRE(to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43"); + } + + { + auto guid = uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43"sv).value(); + REQUIRE(to_string(guid) == "47183823-2574-4bfd-b411-99ed177d3e43"); + REQUIRE(to_string<wchar_t>(guid) == L"47183823-2574-4bfd-b411-99ed177d3e43"); + } + + { + auto str = L"47183823-2574-4bfd-b411-99ed177d3e43"sv; + auto guid = uuid::from_string(str).value(); + REQUIRE(to_string<wchar_t>(guid) == str); + } + + { + auto str = "4718382325744bfdb41199ed177d3e43"sv; + REQUIRE_NOTHROW(uuid::from_string(str)); + REQUIRE(uuid::from_string(str).has_value()); + } + + { + auto str = "00000000-0000-0000-0000-000000000000"sv; + auto guid = uuid::from_string(str).value(); + REQUIRE(guid.is_nil()); + } + + { + auto str = "{00000000-0000-0000-0000-000000000000}"sv; + auto guid = uuid::from_string(str).value(); + REQUIRE(guid.is_nil()); + } + + { + auto str = L"00000000-0000-0000-0000-000000000000"sv; + auto guid = uuid::from_string(str).value(); + REQUIRE(guid.is_nil()); + } + + { + auto str = L"{00000000-0000-0000-0000-000000000000}"sv; + auto guid = uuid::from_string(str).value(); + REQUIRE(guid.is_nil()); + } +} + +TEST_CASE("uuid: Test constexpr from_string", "[uuid]") +{ + constexpr uuid value = uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43").value(); + static_assert(!value.is_nil()); + static_assert(value.variant() == uuid_variant::rfc); + static_assert(value.version() != uuid_version::none); +} + +TEST_CASE("uuid: Test from_string(char*) invalid format", "[uuid]") +{ + REQUIRE(!uuid::from_string("").has_value()); + REQUIRE(!uuid::from_string("{}").has_value()); + REQUIRE(!uuid::from_string( + "47183823-2574-4bfd-b411-99ed177d3e4").has_value()); + REQUIRE(!uuid::from_string( + "47183823-2574-4bfd-b411-99ed177d3e430").has_value()); + REQUIRE(!uuid::from_string( + "{47183823-2574-4bfd-b411-99ed177d3e43").has_value()); + REQUIRE(!uuid::from_string( + "47183823-2574-4bfd-b411-99ed177d3e43}").has_value()); +} + +TEST_CASE("uuid: Test from_string(basic_string) invalid format", "[uuid]") +{ + using namespace std::string_literals; + + { + auto str = ""s; + REQUIRE(!uuid::from_string(str).has_value()); + } + + { + auto str = "{}"s; + REQUIRE(!uuid::from_string(str).has_value()); + } + + { + auto str = "47183823-2574-4bfd-b411-99ed177d3e4"s; + REQUIRE(!uuid::from_string(str).has_value()); + } + + { + auto str = "47183823-2574-4bfd-b411-99ed177d3e430"s; + REQUIRE(!uuid::from_string(str).has_value()); + } + + { + auto str = "{47183823-2574-4bfd-b411-99ed177d3e43"s; + REQUIRE(!uuid::from_string(str).has_value()); + } + + { + auto str = "47183823-2574-4bfd-b411-99ed177d3e43}"s; + REQUIRE(!uuid::from_string(str).has_value()); + } +} + +TEST_CASE("uuid: Test from_string(basic_string_view) invalid format", "[uuid]") +{ + using namespace std::string_view_literals; + + REQUIRE(!uuid::from_string(""sv).has_value()); + REQUIRE(!uuid::from_string("{}"sv).has_value()); + REQUIRE(!uuid::from_string( + "47183823-2574-4bfd-b411-99ed177d3e4"sv).has_value()); + REQUIRE(!uuid::from_string( + "47183823-2574-4bfd-b411-99ed177d3e430"sv).has_value()); + REQUIRE(!uuid::from_string( + "{47183823-2574-4bfd-b411-99ed177d3e43"sv).has_value()); + REQUIRE(!uuid::from_string( + "47183823-2574-4bfd-b411-99ed177d3e43}"sv).has_value()); +} + +TEST_CASE("uuid: Test iterators constructor", "[uuid]") +{ + using namespace std::string_literals; + + { + std::array<uuid::value_type, 16> arr{{ + 0x47, 0x18, 0x38, 0x23, + 0x25, 0x74, + 0x4b, 0xfd, + 0xb4, 0x11, + 0x99, 0xed, 0x17, 0x7d, 0x3e, 0x43 + }}; + + uuid guid(std::begin(arr), std::end(arr)); + REQUIRE(to_string(guid) == + "47183823-2574-4bfd-b411-99ed177d3e43"s); + } + + { + uuid::value_type arr[16] = { // NOLINT + 0x47, 0x18, 0x38, 0x23, + 0x25, 0x74, + 0x4b, 0xfd, + 0xb4, 0x11, + 0x99, 0xed, 0x17, 0x7d, 0x3e, 0x43 + }; + + uuid guid(std::begin(arr), std::end(arr)); + REQUIRE(to_string(guid) == + "47183823-2574-4bfd-b411-99ed177d3e43"s); + } +} + +TEST_CASE("uuid: Test array constructors", "[uuid]") +{ + using namespace std::string_literals; + + { + uuid guid{{ + 0x47, 0x18, 0x38, 0x23, + 0x25, 0x74, + 0x4b, 0xfd, + 0xb4, 0x11, + 0x99, 0xed, 0x17, 0x7d, 0x3e, 0x43 + }}; + + REQUIRE(to_string(guid) == + "47183823-2574-4bfd-b411-99ed177d3e43"s); + } + + { + std::array<uuid::value_type, 16> arr{{ + 0x47, 0x18, 0x38, 0x23, + 0x25, 0x74, + 0x4b, 0xfd, + 0xb4, 0x11, + 0x99, 0xed, 0x17, 0x7d, 0x3e, 0x43 + }}; + + uuid guid(arr); + REQUIRE(to_string(guid) == + "47183823-2574-4bfd-b411-99ed177d3e43"s); + } + + { + uuid::value_type arr[16] { //NOLINT + 0x47, 0x18, 0x38, 0x23, + 0x25, 0x74, + 0x4b, 0xfd, + 0xb4, 0x11, + 0x99, 0xed, 0x17, 0x7d, 0x3e, 0x43 + }; + + uuid guid(arr); + REQUIRE(to_string(guid) == + "47183823-2574-4bfd-b411-99ed177d3e43"s); + } +} + +TEST_CASE("uuid: Test equality", "[uuid]") +{ + uuid empty; + + auto engine = uuid_random_generator::engine_type{}; + seed_rng(engine); + uuid guid = uuid_random_generator{engine}(); + + REQUIRE(empty == empty); + REQUIRE(guid == guid); + REQUIRE(empty != guid); +} + +TEST_CASE("Test comparison", "[uuid]") +{ + auto empty = uuid{}; + + auto engine = uuid_random_generator::engine_type{}; + seed_rng(engine); + + uuid_random_generator gen{engine}; + auto id = gen(); + + REQUIRE(empty < id); + + std::set<uuid> ids{ + uuid{}, + gen(), + gen(), + gen(), + gen() + }; + + REQUIRE(ids.size() == 5); + REQUIRE(ids.find(uuid{}) != ids.end()); +} + +TEST_CASE("uuid: Test hashing", "[uuid]") +{ + using namespace std::string_literals; + + auto str = "47183823-2574-4bfd-b411-99ed177d3e43"s; + auto guid = uuid::from_string(str).value(); + + auto h1 = std::hash<std::string>{}; + auto h2 = std::hash<uuid>{}; + REQUIRE(h1(str) != h2(guid)); + + auto engine = uuid_random_generator::engine_type{}; + seed_rng(engine); + uuid_random_generator gen{ engine }; + + std::unordered_set<uuid> ids{ + uuid{}, + gen(), + gen(), + gen(), + gen() + }; + + REQUIRE(ids.size() == 5); + REQUIRE(ids.find(uuid{}) != ids.end()); +} + +TEST_CASE("uuid: Test swap", "[uuid]") +{ + uuid empty; + + auto engine = uuid_random_generator::engine_type{}; + seed_rng(engine); + uuid guid = uuid_random_generator{engine}(); + + REQUIRE(empty.is_nil()); + REQUIRE(!guid.is_nil()); + + std::swap(empty, guid); + + REQUIRE(!empty.is_nil()); + REQUIRE(guid.is_nil()); + + empty.swap(guid); + + REQUIRE(empty.is_nil()); + REQUIRE(!guid.is_nil()); +} + +TEST_CASE("uuid: Test constexpr", "[uuid]") +{ + constexpr uuid empty; + static_assert(empty.is_nil()); + static_assert(empty.variant() == uuid_variant::ncs); + static_assert(empty.version() == uuid_version::none); +} + +TEST_CASE("uuid: Test size", "[uuid]") +{ + REQUIRE(sizeof(uuid) == 16); +} + +TEST_CASE("uuid: Test assignment", "[uuid]") +{ + auto id1 = uuid::from_string("47183823-2574-4bfd-b411-99ed177d3e43").value(); + auto id2 = id1; + REQUIRE(id1 == id2); + + id1 = uuid::from_string("{fea43102-064f-4444-adc2-02cec42623f8}").value(); + REQUIRE(id1 != id2); + + auto id3 = std::move(id2); + REQUIRE(to_string(id3) == "47183823-2574-4bfd-b411-99ed177d3e43"); +} + +TEST_CASE("uuid: Test trivial", "[uuid]") +{ + REQUIRE(std::is_trivially_copyable_v<uuid>); +} + +TEST_CASE("uuid: Test as_bytes", "[uuid]") +{ + std::array<uuid::value_type, 16> arr{{ + 0x47, 0x18, 0x38, 0x23, + 0x25, 0x74, + 0x4b, 0xfd, + 0xb4, 0x11, + 0x99, 0xed, 0x17, 0x7d, 0x3e, 0x43 + }}; + + { + uuid id{ arr }; + REQUIRE(!id.is_nil()); + + auto view = id.as_bytes(); + REQUIRE(memcmp(view.data(), arr.data(), arr.size()) == 0); + } + + { + const uuid id{ arr }; + REQUIRE(!id.is_nil()); + + auto view = id.as_bytes(); + REQUIRE(memcmp(view.data(), arr.data(), arr.size()) == 0); + } +} + +//NOLINTEND(bugprone-unchecked-optional-access) diff --git a/nihil.uuid/uuid.ccm b/nihil.uuid/uuid.ccm new file mode 100644 index 0000000..4aa424e --- /dev/null +++ b/nihil.uuid/uuid.ccm @@ -0,0 +1,842 @@ +/* + * From https://github.com/mariusbancila/stduuid + * + * Copyright (c) 2017 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +module; + +#include <algorithm> +#include <array> +#include <atomic> +#include <chrono> +#include <cstring> +#include <functional> +#include <iomanip> +#include <iterator> +#include <memory> +#include <numeric> +#include <optional> +#include <random> +#include <ranges> +#include <span> +#include <string> +#include <sstream> +#include <string_view> +#include <type_traits> + +export module nihil.uuid; + +namespace nihil { + +template <typename TChar> +[[nodiscard]] constexpr auto hex2char(TChar const ch) noexcept -> unsigned char +{ + if (ch >= static_cast<TChar>('0') && ch <= static_cast<TChar>('9')) + return static_cast<unsigned char>( + ch - static_cast<TChar>('0')); + + if (ch >= static_cast<TChar>('a') && ch <= static_cast<TChar>('f')) + return static_cast<unsigned char>( + 10 + ch - static_cast<TChar>('a')); + + if (ch >= static_cast<TChar>('A') && ch <= static_cast<TChar>('F')) + return static_cast<unsigned char>( + 10 + ch - static_cast<TChar>('A')); + + return 0; +} + +template <typename TChar> +[[nodiscard]] constexpr auto is_hex(TChar const ch) noexcept -> bool +{ + return (ch >= static_cast<TChar>('0') && + ch <= static_cast<TChar>('9')) || + (ch >= static_cast<TChar>('a') && + ch <= static_cast<TChar>('f')) || + (ch >= static_cast<TChar>('A') && + ch <= static_cast<TChar>('F')); +} + +template <typename TChar> +[[nodiscard]] constexpr auto to_string_view(TChar const *str) noexcept + -> std::basic_string_view<TChar> +{ + if (str) + return str; + return {}; +} + +template <typename StringType> +[[nodiscard]] constexpr auto to_string_view(StringType const &str) noexcept + -> std::basic_string_view< + typename StringType::value_type, + typename StringType::traits_type> +{ + return str; +} + +struct sha1 +{ + using digest32_t = std::array<std::uint32_t, 5>; + using digest8_t = std::array<std::uint8_t, 20>; + + static constexpr unsigned int block_bytes = 64; + + sha1() + { + reset(); + } + + [[nodiscard]] inline static auto + left_rotate(std::uint32_t value, std::size_t const count) noexcept + -> std::uint32_t + { + return (value << count) ^ (value >> (32 - count)); + } + + auto reset(this sha1 &self) noexcept -> void + { + self.m_digest[0] = 0x67452301; + self.m_digest[1] = 0xEFCDAB89; + self.m_digest[2] = 0x98BADCFE; + self.m_digest[3] = 0x10325476; + self.m_digest[4] = 0xC3D2E1F0; + self.m_blockByteIndex = 0; + self.m_byteCount = 0; + } + + auto process_byte(this sha1 &self, std::uint8_t octet) -> void + { + self.m_block[self.m_blockByteIndex++] = octet; + ++self.m_byteCount; + + if (self.m_blockByteIndex == block_bytes) { + self.m_blockByteIndex = 0; + self.process_block(); + } + } + + auto process_block(this sha1 &self, + void const *const start, + void const *const end) + -> void + { + auto *first = static_cast<uint8_t const *>(start); + auto *last = static_cast<uint8_t const *>(end); + + while (first != last) { + self.process_byte(*first); + first++; + } + } + + auto process_bytes(this sha1 &self, + void const *const data, + size_t const len) + -> void + { + auto *block = static_cast<uint8_t const *>(data); + self.process_block(block, block + len); + } + + auto get_digest(this sha1 &self) -> digest32_t + { + auto const bit_count = self.m_byteCount * 8; + + self.process_byte(0x80); + if (self.m_blockByteIndex > 56) { + while (self.m_blockByteIndex != 0) + self.process_byte(0); + + while (self.m_blockByteIndex < 56) + self.process_byte(0); + } else { + while (self.m_blockByteIndex < 56) + self.process_byte(0); + } + + self.process_byte(0); + self.process_byte(0); + self.process_byte(0); + self.process_byte(0); + self.process_byte(static_cast<unsigned char>( + (bit_count >> 24) & 0xFF)); + self.process_byte(static_cast<unsigned char>( + (bit_count >> 16) & 0xFF)); + self.process_byte(static_cast<unsigned char>( + (bit_count >> 8) & 0xFF)); + self.process_byte(static_cast<unsigned char>( + (bit_count) & 0xFF)); + + return self.m_digest; + } + + auto get_digest_bytes(this sha1 &self) -> digest8_t + { + auto d32 = self.get_digest(); + auto digest = digest8_t{}; + + auto di = std::size_t{0}; + + digest[di++] = static_cast<uint8_t>(d32[0] >> 24); + digest[di++] = static_cast<uint8_t>(d32[0] >> 16); + digest[di++] = static_cast<uint8_t>(d32[0] >> 8); + digest[di++] = static_cast<uint8_t>(d32[0] >> 0); + + digest[di++] = static_cast<uint8_t>(d32[1] >> 24); + digest[di++] = static_cast<uint8_t>(d32[1] >> 16); + digest[di++] = static_cast<uint8_t>(d32[1] >> 8); + digest[di++] = static_cast<uint8_t>(d32[1] >> 0); + + digest[di++] = static_cast<uint8_t>(d32[2] >> 24); + digest[di++] = static_cast<uint8_t>(d32[2] >> 16); + digest[di++] = static_cast<uint8_t>(d32[2] >> 8); + digest[di++] = static_cast<uint8_t>(d32[2] >> 0); + + digest[di++] = static_cast<uint8_t>(d32[3] >> 24); + digest[di++] = static_cast<uint8_t>(d32[3] >> 16); + digest[di++] = static_cast<uint8_t>(d32[3] >> 8); + digest[di++] = static_cast<uint8_t>(d32[3] >> 0); + + digest[di++] = static_cast<uint8_t>(d32[4] >> 24); + digest[di++] = static_cast<uint8_t>(d32[4] >> 16); + digest[di++] = static_cast<uint8_t>(d32[4] >> 8); + digest[di++] = static_cast<uint8_t>(d32[4] >> 0); + + return digest; + } + +private: + auto process_block(this sha1 &self) -> void + { + auto w = std::array<std::uint32_t, 80>(); + + for (std::size_t i = 0; i < 16; i++) { + w[i] = static_cast<std::uint32_t>( + self.m_block[i * 4 + 0]) << 24; + w[i] |= static_cast<std::uint32_t>( + self.m_block[i * 4 + 1]) << 16; + w[i] |= static_cast<std::uint32_t>( + self.m_block[i * 4 + 2]) << 8; + w[i] |= static_cast<std::uint32_t>( + self.m_block[i * 4 + 3]); + } + + for (std::size_t i = 16; i < 80; i++) { + w[i] = left_rotate((w[i - 3] ^ w[i - 8] ^ + w[i - 14] ^ w[i - 16]), 1); + } + + auto a = self.m_digest[0]; + auto b = self.m_digest[1]; + auto c = self.m_digest[2]; + auto d = self.m_digest[3]; + auto e = self.m_digest[4]; + + for (std::size_t i = 0; i < 80; ++i) { + auto f = std::uint32_t{0}; + auto k = std::uint32_t{0}; + + if (i < 20) { + f = (b & c) | (~b & d); + k = 0x5A827999; + } else if (i < 40) { + f = b ^ c ^ d; + k = 0x6ED9EBA1; + } else if (i < 60) { + f = (b & c) | (b & d) | (c & d); + k = 0x8F1BBCDC; + } else { + f = b ^ c ^ d; + k = 0xCA62C1D6; + } + + auto temp = std::uint32_t{left_rotate(a, 5) + + f + e + k + w[i]}; + e = d; + d = c; + c = left_rotate(b, 30); + b = a; + a = temp; + } + + self.m_digest[0] += a; + self.m_digest[1] += b; + self.m_digest[2] += c; + self.m_digest[3] += d; + self.m_digest[4] += e; + } + + digest32_t m_digest; + std::array<std::uint8_t, 64> m_block; + std::size_t m_blockByteIndex; + std::size_t m_byteCount; +}; + +template <typename CharT> +inline constexpr std::string_view empty_guid = + "00000000-0000-0000-0000-000000000000"; + +template <> +inline constexpr std::wstring_view empty_guid<wchar_t> + = L"00000000-0000-0000-0000-000000000000"; + +template <typename CharT> +inline constexpr std::string_view guid_encoder = "0123456789abcdef"; + +template <> +inline constexpr std::wstring_view guid_encoder<wchar_t> = L"0123456789abcdef"; + +// --------------------------------------------------------------------- +// UUID format https://tools.ietf.org/html/rfc4122 +// --------------------------------------------------------------------- + +// --------------------------------------------------------------------- +// Field NDR Data Type Octet # Note +// Note +// --------------------------------------------------------------------- +// time_low unsigned long 0 - 3 +// The low field of the timestamp. +// time_mid unsigned short 4 - 5 +// The middle field of the timestamp. +// time_hi_and_version unsigned short 6 - 7 +// The high field of the timestamp multiplexed with the version number. +// clock_seq_hi_and_reserved unsigned small 8 +// The high field of the clock sequence multiplexed with the variant. +// clock_seq_low unsigned small 9 +// The low field of the clock sequence. +// node character 10 - 15 +// The spatially unique node identifier. +// --------------------------------------------------------------------- +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | time_low | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | time_mid | time_hi_and_version | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |clk_seq_hi_res | clk_seq_low | node (0-1) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | node (2-5) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +// --------------------------------------------------------------------- +// enumerations +// --------------------------------------------------------------------- + +// indicated by a bit pattern in octet 8, marked with N in +// xxxxxxxx-xxxx-xxxx-Nxxx-xxxxxxxxxxxx +export enum struct uuid_variant { + // NCS backward compatibility (with the obsolete Apollo Network + // Computing System 1.5 UUID format). + // N bit pattern: 0xxx + // > the first 6 octets of the UUID are a 48-bit timestamp (the number + // of 4 microsecond units of time since 1 Jan 1980 UTC); + // > the next 2 octets are reserved; + // > the next octet is the "address family"; + // > the final 7 octets are a 56-bit host ID in the form specified by + // the address family + ncs, + + // RFC 4122/DCE 1.1 + // N bit pattern: 10xx + // > big-endian byte order + rfc, + + // Microsoft Corporation backward compatibility + // N bit pattern: 110x + // > little endian byte order + // > formely used in the Component Object Model (COM) library + microsoft, + + // reserved for possible future definition + // N bit pattern: 111x + reserved +}; + +// indicated by a bit pattern in octet 6, marked with M in +// xxxxxxxx-xxxx-Mxxx-xxxx-xxxxxxxxxxxx +export enum struct uuid_version { + // only possible for nil or invalid uuids + none = 0, + // The time-based version specified in RFC 4122 + time_based = 1, + // DCE Security version, with embedded POSIX UIDs. + dce_security = 2, + // The name-based version specified in RFS 4122 with MD5 hashing + name_based_md5 = 3, + // The randomly or pseudo-randomly generated version specified in RFS 4122 + random_number_based = 4, + // The name-based version specified in RFS 4122 with SHA1 hashing + name_based_sha1 = 5 +}; + +// Forward declare uuid and to_string so that we can declare to_string as a +// friend later. +export struct uuid; +export template <typename CharT = char, + typename Traits = std::char_traits<CharT>, + typename Allocator = std::allocator<CharT>> +auto to_string(uuid const &id) -> std::basic_string<CharT, Traits, Allocator>; + +// -------------------------------------------------------------------------------------------------------------------------- +// uuid class +// -------------------------------------------------------------------------------------------------------------------------- +export struct uuid { + using value_type = std::uint8_t; + + constexpr uuid() noexcept = default; + + uuid(value_type(&arr)[16]) noexcept // NOLINT + { + std::ranges::copy(arr, std::ranges::begin(data)); + } + + constexpr uuid(std::array<value_type, 16> const &arr) noexcept + : data{arr} + { + } + + explicit uuid(std::span<value_type, 16> bytes) + { + std::ranges::copy(bytes, std::ranges::begin(data)); + } + + explicit uuid(std::span<value_type> bytes) + { + if (bytes.size() != 16) + throw std::logic_error("wrong size for uuid"); + std::ranges::copy(bytes, std::ranges::begin(data)); + } + + template<typename ForwardIterator> + explicit uuid(ForwardIterator first, ForwardIterator last) + { + if (std::distance(first, last) != 16) + throw std::logic_error("wrong size for uuid"); + + std::copy(first, last, std::begin(data)); + } + + [[nodiscard]] constexpr auto variant() const noexcept -> uuid_variant + { + if ((data[8] & 0x80) == 0x00) + return uuid_variant::ncs; + else if ((data[8] & 0xC0) == 0x80) + return uuid_variant::rfc; + else if ((data[8] & 0xE0) == 0xC0) + return uuid_variant::microsoft; + else + return uuid_variant::reserved; + } + + [[nodiscard]] constexpr auto version() const noexcept -> uuid_version + { + if ((data[6] & 0xF0) == 0x10) + return uuid_version::time_based; + else if ((data[6] & 0xF0) == 0x20) + return uuid_version::dce_security; + else if ((data[6] & 0xF0) == 0x30) + return uuid_version::name_based_md5; + else if ((data[6] & 0xF0) == 0x40) + return uuid_version::random_number_based; + else if ((data[6] & 0xF0) == 0x50) + return uuid_version::name_based_sha1; + else + return uuid_version::none; + } + + [[nodiscard]] constexpr auto is_nil() const noexcept -> bool + { + for (auto i : data) + if (i != 0) + return false; + return true; + } + + auto swap(uuid &other) noexcept -> void + { + data.swap(other.data); + } + + [[nodiscard]] inline auto as_bytes() const + -> std::span<std::byte const, 16> + { + return std::span<std::byte const, 16>( + reinterpret_cast<std::byte const*>(data.data()), + 16); + } + + template <typename StringType> + [[nodiscard]] constexpr static auto + is_valid_uuid(StringType const &in_str) noexcept + -> bool + { + auto str = to_string_view(in_str); + auto firstDigit = true; + auto hasBraces = std::size_t{0}; + auto index = std::size_t{0}; + + if (str.empty()) + return false; + + if (str.front() == '{') + hasBraces = 1; + + if (hasBraces && str.back() != '}') + return false; + + for (size_t i = hasBraces; i < str.size() - hasBraces; ++i) { + if (str[i] == '-') + continue; + + if (index >= 16 || !is_hex(str[i])) + return false; + + if (firstDigit) { + firstDigit = false; + } else { + index++; + firstDigit = true; + } + } + + if (index < 16) + return false; + + return true; + } + + template <typename StringType> + [[nodiscard]] constexpr static auto + from_string(StringType const & in_str) noexcept + -> std::optional<uuid> + { + auto str = to_string_view(in_str); + bool firstDigit = true; + size_t hasBraces = 0; + size_t index = 0; + + std::array<uint8_t, 16> data{ { 0 } }; + + if (str.empty()) + return {}; + + if (str.front() == '{') + hasBraces = 1; + if (hasBraces && str.back() != '}') + return {}; + + for (size_t i = hasBraces; i < str.size() - hasBraces; ++i) { + if (str[i] == '-') + continue; + + if (index >= 16 || !is_hex(str[i])) { + return {}; + } + + if (firstDigit) { + data[index] = static_cast<uint8_t>(hex2char(str[i]) << 4); + firstDigit = false; + } else { + data[index] = static_cast<uint8_t>(data[index] | hex2char(str[i])); + index++; + firstDigit = true; + } + } + + if (index < 16) { + return {}; + } + + return uuid{data}; + } + +private: + std::array<value_type, 16> data{ { 0 } }; + + friend auto operator==(uuid const &, uuid const &) noexcept -> bool; + friend auto operator<(uuid const &, uuid const &) noexcept -> bool; + + template <class Elem, class Traits> + friend auto operator<<(std::basic_ostream<Elem, Traits> &s, + uuid const &id) + -> std::basic_ostream<Elem, Traits> &; + + template<class CharT, class Traits, class Allocator> + friend auto to_string(uuid const &id) + -> std::basic_string<CharT, Traits, Allocator>; + + friend std::hash<uuid>; +}; + +// -------------------------------------------------------------------------------------------------------------------------- +// operators and non-member functions +// -------------------------------------------------------------------------------------------------------------------------- + +export [[nodiscard]] +auto operator== (uuid const &lhs, uuid const &rhs) noexcept -> bool +{ + return lhs.data == rhs.data; +} + +export [[nodiscard]] +auto operator!= (uuid const &lhs, uuid const &rhs) noexcept -> bool +{ + return !(lhs == rhs); +} + +export [[nodiscard]] +auto operator< (uuid const &lhs, uuid const &rhs) noexcept -> bool +{ + return lhs.data < rhs.data; +} + +export template <typename CharT, typename Traits, typename Allocator> +[[nodiscard]] auto to_string(uuid const &id) + -> std::basic_string<CharT, Traits, Allocator> +{ + auto uustr = std::basic_string<CharT, Traits, Allocator>( + std::from_range, empty_guid<CharT>); + + for (std::size_t i = 0, index = 0; i < 36; ++i) { + if (i == 8 || i == 13 || i == 18 || i == 23) + continue; + + uustr[i] = guid_encoder<CharT>[id.data[index] >> 4 & 0x0f]; + uustr[++i] = guid_encoder<CharT>[id.data[index] & 0x0f]; + index++; + } + + return uustr; +} + +export template <class Elem, class Traits> +auto operator<<(std::basic_ostream<Elem, Traits> &s, uuid const &id) + -> std::basic_ostream<Elem, Traits> & +{ + s << to_string(id); + return s; +} + +export auto swap(uuid &lhs, uuid &rhs) noexcept -> void +{ + lhs.swap(rhs); +} + +/*********************************************************************** + * namespace IDs that could be used for generating name-based uuids + */ + +// Name string is a fully-qualified domain name +export uuid uuid_namespace_dns{{ + 0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, + 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 +}}; + +// Name string is a URL +export uuid uuid_namespace_url{{ + 0x6b, 0xa7, 0xb8, 0x11, 0x9d, 0xad, 0x11, 0xd1, + 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 +}}; + +// Name string is an ISO OID (See https://oidref.com/, +// https://en.wikipedia.org/wiki/Object_identifier) +export uuid uuid_namespace_oid{{ + 0x6b, 0xa7, 0xb8, 0x12, 0x9d, 0xad, 0x11, 0xd1, + 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 +}}; + +// Name string is an X.500 DN, in DER or a text output format (See +// https://en.wikipedia.org/wiki/X.500, +// https://en.wikipedia.org/wiki/Abstract_Syntax_Notation_One) +export uuid uuid_namespace_x500{{ + 0x6b, 0xa7, 0xb8, 0x14, 0x9d, 0xad, 0x11, 0xd1, + 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 +}}; + +/*********************************************************************** + * uuid generators + */ + +export template <typename UniformRandomNumberGenerator> +struct basic_uuid_random_generator +{ + using engine_type = UniformRandomNumberGenerator; + + explicit basic_uuid_random_generator(engine_type &gen) + : generator(&gen, [](auto) {}) + { + } + + explicit basic_uuid_random_generator(engine_type *gen) + : generator(gen, [](auto) {}) + { + } + + [[nodiscard]] auto operator()() -> uuid + { + alignas(std::uint32_t) + auto bytes = std::array<std::uint8_t, 16>{}; + + for (int i = 0; i < 16; i += 4) + *reinterpret_cast<std::uint32_t *>(bytes.data() + i) = + distribution(*generator); + + // variant must be 10xxxxxx + bytes[8] &= 0xBF; + bytes[8] |= 0x80; + + // version must be 0100xxxx + bytes[6] &= 0x4F; + bytes[6] |= 0x40; + + return uuid{std::begin(bytes), std::end(bytes)}; + } + +private: + std::uniform_int_distribution<std::uint32_t> distribution; + std::shared_ptr<UniformRandomNumberGenerator> generator; +}; + +export using uuid_random_generator = basic_uuid_random_generator<std::mt19937>; + +export struct uuid_name_generator +{ + explicit uuid_name_generator(uuid const &namespace_uuid) noexcept + : nsuuid(namespace_uuid) + { + } + + template <typename StringType> + [[nodiscard]] auto operator()(StringType const &name) -> uuid + { + reset(); + process_characters(to_string_view(name)); + return make_uuid(); + } + +private: + auto reset() -> void + { + hasher.reset(); + + auto nsbytes = nsuuid.as_bytes(); + + auto bytes = std::array<std::byte, 16>(); + std::ranges::copy(nsbytes, std::ranges::begin(bytes)); + + hasher.process_bytes(bytes.data(), bytes.size()); + } + + template <typename CharT, typename Traits> + auto process_characters( + std::basic_string_view<CharT, Traits> const str) -> void + { + for (std::uint32_t c : str) { + hasher.process_byte(static_cast<std::uint8_t>(c & 0xFF)); + if constexpr (!std::is_same_v<CharT, char>) { + hasher.process_byte( + static_cast<uint8_t>((c >> 8) & 0xFF)); + hasher.process_byte( + static_cast<uint8_t>((c >> 16) & 0xFF)); + hasher.process_byte( + static_cast<uint8_t>((c >> 24) & 0xFF)); + } + } + } + + [[nodiscard]] auto make_uuid() -> uuid + { + auto digest = hasher.get_digest_bytes(); + + // variant must be 0b10xxxxxx + digest[8] &= 0xBF; + digest[8] |= 0x80; + + // version must be 0b0101xxxx + digest[6] &= 0x5F; + digest[6] |= 0x50; + + return uuid(std::span(digest).subspan(0, 16)); + } + +private: + uuid nsuuid; + sha1 hasher; +}; + +/* + * Create a random UUID. + */ +export auto random_uuid() -> uuid +{ + auto rd = std::random_device(); + auto seed_data = std::array<int, std::mt19937::state_size> {}; + std::ranges::generate(seed_data, std::ref(rd)); + + auto seq = std::seed_seq(std::ranges::begin(seed_data), + std::ranges::end(seed_data)); + auto generator = std::mt19937(seq); + auto gen = uuid_random_generator{generator}; + + return gen(); +} + +} // namespace nihil + +namespace std { + +export template <> +struct hash<nihil::uuid> +{ + using argument_type = nihil::uuid; + using result_type = std::size_t; + + [[nodiscard]] auto operator()(argument_type const &uuid) const + -> result_type + { + std::uint64_t l = + static_cast<uint64_t>(uuid.data[0]) << 56 | + static_cast<uint64_t>(uuid.data[1]) << 48 | + static_cast<uint64_t>(uuid.data[2]) << 40 | + static_cast<uint64_t>(uuid.data[3]) << 32 | + static_cast<uint64_t>(uuid.data[4]) << 24 | + static_cast<uint64_t>(uuid.data[5]) << 16 | + static_cast<uint64_t>(uuid.data[6]) << 8 | + static_cast<uint64_t>(uuid.data[7]); + + std::uint64_t h = + static_cast<uint64_t>(uuid.data[8]) << 56 | + static_cast<uint64_t>(uuid.data[9]) << 48 | + static_cast<uint64_t>(uuid.data[10]) << 40 | + static_cast<uint64_t>(uuid.data[11]) << 32 | + static_cast<uint64_t>(uuid.data[12]) << 24 | + static_cast<uint64_t>(uuid.data[13]) << 16 | + static_cast<uint64_t>(uuid.data[14]) << 8 | + static_cast<uint64_t>(uuid.data[15]); + + return std::hash<std::uint64_t>{}(l ^ h); + } +}; + +} // namespace std |
