From 259b128f0ac7d6f4289948f3e7ba9dc862aa962b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20W=C3=B6lzer?= <martin@libclapp.org> Date: Sun, 12 Apr 2020 08:51:01 +0200 Subject: [PATCH] added new features: - bump version to 0.2.0 - updated README.md - added and updated several tests - added and updated doc - updated sample_extractor and added sample_executor - added clang-format specification - added support for sanitizers - added more exhaustive gitlab-pipeline - added more specific exceptions - updated help-message generation in parsers (sub_parser, main_parser and basic_parser) - minor improvements of CMakeLists.txt (e.g. added cppcheck) - renamed full_example to large_example - minor improvements to examples - added test examples scripts - added build_info_string - unified arguments and options --- .gitignore | 2 + .gitlab-ci.yml | 974 ++++++++++++++++------------ CMakeLists.txt | 50 +- README.md | 69 +- cmake/git.cmake | 19 +- cmake/sanitizer.cmake | 37 ++ doc/CMakeLists.txt | 20 +- doc/doc.md | 698 ++++++++++++++++++-- doc/sample_executor.p6 | 39 ++ doc/sample_extractor.p6 | 31 +- examples/CMakeLists.txt | 24 +- examples/large_example.cpp | 396 +++++++++++ examples/short_example.cpp | 6 +- examples/sub_parser_example.cpp | 8 +- examples/test_large_example.sh | 122 ++++ examples/test_short_example.sh | 110 ++++ examples/test_sub_parser_example.sh | 97 +++ src/clapp/CMakeLists.txt | 4 +- src/clapp/exception.cpp | 42 +- src/clapp/main_parser.cpp | 20 +- src/clapp/parser.cpp | 272 +++++--- src/clapp/sub_parser.cpp | 30 +- src/clapp/value.cpp | 2 +- src/include/clapp/argument.h | 10 +- src/include/clapp/argument.hpp | 30 +- src/include/clapp/build_info.h.in | 28 +- src/include/clapp/exception.h | 24 +- src/include/clapp/main_parser.h | 6 +- src/include/clapp/option.h | 32 +- src/include/clapp/option.hpp | 73 ++- src/include/clapp/parser.h | 48 +- src/include/clapp/parser.hpp | 14 +- src/include/clapp/sub_parser.h | 19 +- src/include/clapp/sub_parser.hpp | 34 +- src/include/clapp/value.h | 4 +- tests/CMakeLists.txt | 6 +- tests/argument.cpp | 66 +- tests/build_info.cpp | 20 + tests/exception.cpp | 136 +++- tests/main_parser.cpp | 62 +- tests/option.cpp | 554 +++++++++++++--- tests/parser.cpp | 347 +++++++--- tests/sub_parser.cpp | 202 ++++-- tests/value.cpp | 163 ++--- 44 files changed, 3840 insertions(+), 1110 deletions(-) create mode 100644 .gitignore create mode 100644 cmake/sanitizer.cmake create mode 100755 doc/sample_executor.p6 create mode 100644 examples/large_example.cpp create mode 100755 examples/test_large_example.sh create mode 100755 examples/test_short_example.sh create mode 100755 examples/test_sub_parser_example.sh create mode 100644 tests/build_info.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..8cf43cb3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +doc/*.cpp +doc/*.out diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 34e13ad9..04e07e10 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,573 +1,721 @@ stages: - build - test + variables: GIT_STRATEGY: clone GIT_SUBMODULE_STRATEGY: recursive -compile_install_g++9: +.compile_install_template: &compile_install_definition stage: build script: + - SRC_DIR=`pwd` - mkdir build - - ./doc/sample_extractor.p6 doc - cd build - - CXX=g++-9 CC=gcc-9 cmake -DlibClaPP_BUILD_TESTS=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_BUILD_COVERAGE=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=Release .. - - make -j2 VERBOSE=1 + - cmake -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=${BUILD_TYPE} ${SRC_DIR} + - cmake --build . - make install - - echo -e '#include<clapp/main_parser.h>\n#include<clapp/build_info.h>\n#include<iostream>\nint main (int argc, char *argv[]) {\nstd::cout << clapp::build_info::project_name << " v" << clapp::build_info::version << std::endl;\n}' | g++-9 `pkg-config --libs --cflags libclapp` -I../third_party/GSL/include/ --std=c++17 -o out -x c++ - - tags: - - g++-9 - - cmake - artifacts: - paths: - - build + - echo -e '#include<clapp/main_parser.h>\n#include<clapp/build_info.h>\n#include<iostream>\nint main (int argc, char *argv[]) {\nstd::cout << clapp::build_info::project_name << " v" << clapp::build_info::version << std::endl;\n}' | ${CXX} `pkg-config --libs --cflags libclapp` -I${SRC_DIR}/third_party/GSL/include/ --std=c++17 -o out -x c++ - -compile_release_g++9: +.compile_template: &compile_definition stage: build script: + - SRC_DIR=`pwd` - mkdir build - - ./doc/sample_extractor.p6 doc - cd build - - CXX=g++-9 CC=gcc-9 cmake -DlibClaPP_BUILD_TESTS=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_BUILD_COVERAGE=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=Release .. - - make -j2 VERBOSE=1 - tags: - - g++-9 - - cmake + - cmake -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -DlibClaPP_BUILD_TESTS=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_BUILD_COVERAGE=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=${BUILD_TYPE} ${ADDITIONAL_OPTION} -G "${GENERATOR}" ${SRC_DIR} + - cmake --build . artifacts: paths: - build -test_release_g++9: - stage: test +.compile_clang_tidy_template: &compile_clang_tidy_definition + stage: build script: + - SRC_DIR=`pwd` + - mkdir build - cd build - - ./tests/libclapp_tests - - gcovr -r ../ -e ../third_party/ -e ../tests/ -e ../examples/ --gcov-executable="gcov-9" - tags: - - g++-9 - - cmake - dependencies: - - compile_release_g++9 + - cmake -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -DCLANG_TIDY=${CLANG_TIDY} -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_BUILD_TESTS=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DlibClaPP_BUILD_COVERAGE=On -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DlibClaPP_CLANG_TIDY=On ${SRC_DIR} + - cmake --build . -compile_debug_g++9: - stage: build +.test_gcov_template: &test_gcov_definition + stage: test script: - - mkdir build - - ./doc/sample_extractor.p6 doc - cd build - - CXX=g++-9 CC=gcc-9 cmake -DlibClaPP_BUILD_TESTS=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_BUILD_COVERAGE=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=Debug .. - - make -j2 VERBOSE=1 - tags: - - g++-9 - - cmake - artifacts: - paths: - - build + - ctest -V -R libclapp_tests + - gcovr -r ../ -e ../third_party/ -e ../tests/ -e ../examples/ -e doc -e ../doc/ --gcov-executable="${GCOV_EXECUTABLE}" + - ctest -V -E libclapp_tests + - cd ../ -test_debug_g++9: +.test_template: &test_definition stage: test script: - cd build - - ./tests/libclapp_tests - - gcovr -r ../ -e ../third_party/ -e ../tests/ -e ../examples/ --gcov-executable="gcov-9" + - ctest -V + - cd ../ + +compile_install_release_g++9: + variables: + CXX: "g++-9" + CC: "gcc-9" + BUILD_TYPE: "Release" tags: - g++-9 - cmake - coverage: '/^TOTAL.*\s+(\d+%)$/' - dependencies: - - compile_debug_g++9 + <<: *compile_install_definition -compile_example_release_g++9: - stage: build - script: - - mkdir build - - ./doc/sample_extractor.p6 doc - - cd build - - CXX=g++-9 CC=gcc-9 cmake -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=Release .. - - make -j2 VERBOSE=1 +compile_install_debug_g++9: + variables: + CXX: "g++-9" + CC: "gcc-9" + BUILD_TYPE: "Release" tags: - g++-9 - cmake - artifacts: - paths: - - build + <<: *compile_install_definition -compile_install_g++8: - stage: build - script: - - mkdir build - - ./doc/sample_extractor.p6 doc - - cd build - - CXX=g++-8 CC=gcc-8 cmake -DlibClaPP_BUILD_TESTS=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_BUILD_COVERAGE=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=Release .. - - make -j2 VERBOSE=1 - - make install - - echo -e '#include<clapp/main_parser.h>\n#include<clapp/build_info.h>\n#include<iostream>\nint main (int argc, char *argv[]) {\nstd::cout << clapp::build_info::project_name << " v" << clapp::build_info::version << std::endl;\n}' | g++-8 `pkg-config --libs --cflags libclapp` -I../third_party/GSL/include/ --std=c++17 -o out -x c++ - +compile_install_debug_g++8: + variables: + CXX: "g++-8" + CC: "gcc-8" + BUILD_TYPE: "Debug" tags: - g++-8 - cmake - artifacts: - paths: - - build + <<: *compile_install_definition -compile_release_g++8: - stage: build - script: - - mkdir build - - ./doc/sample_extractor.p6 doc - - cd build - - CXX=g++-8 CC=gcc-8 cmake -DlibClaPP_BUILD_TESTS=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_BUILD_COVERAGE=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=Release .. - - make -j2 VERBOSE=1 +compile_install_release_g++8: + variables: + CXX: "g++-8" + CC: "gcc-8" + BUILD_TYPE: "Release" tags: - g++-8 - cmake - artifacts: - paths: - - build + <<: *compile_install_definition -test_release_g++8: - stage: test - script: - - cd build - - ./tests/libclapp_tests - - gcovr -r ../ -e ../third_party/ -e ../tests/ -e ../examples/ --gcov-executable="gcov-8" +compile_install_debug_g++7: + variables: + CXX: "g++-7" + CC: "gcc-7" + BUILD_TYPE: "Debug" + tags: + - g++-7 + - cmake + <<: *compile_install_definition + +compile_install_release_g++7: + variables: + CXX: "g++-7" + CC: "gcc-7" + BUILD_TYPE: "Release" + tags: + - g++-7 + - cmake + <<: *compile_install_definition + +compile_install_debug_clang++7: + variables: + CXX: "clang++-7" + CC: "clang-7" + BUILD_TYPE: "Debug" + tags: + - clang++-7 + - cmake + <<: *compile_install_definition + +compile_install_release_clang++7: + variables: + CXX: "clang++-7" + CC: "clang-7" + BUILD_TYPE: "Release" + tags: + - clang++-7 + - cmake + <<: *compile_install_definition + +compile_install_debug_clang++8: + variables: + CXX: "clang++-8" + CC: "clang-8" + BUILD_TYPE: "Debug" + tags: + - clang++-8 + - cmake + <<: *compile_install_definition + +compile_install_release_clang++8: + variables: + CXX: "clang++-8" + CC: "clang-8" + BUILD_TYPE: "Release" + tags: + - clang++-8 + - cmake + <<: *compile_install_definition + +compile_install_debug_clang++9: + variables: + CXX: "clang++-9" + CC: "clang-9" + BUILD_TYPE: "Debug" + tags: + - clang++-9 + - cmake + <<: *compile_install_definition + +compile_install_release_clang++9: + variables: + CXX: "clang++-9" + CC: "clang-9" + BUILD_TYPE: "Release" + tags: + - clang++-9 + - cmake + <<: *compile_install_definition + +compile_release_g++9: + variables: + CXX: "g++-9" + CC: "gcc-9" + BUILD_TYPE: "Release" + GENERATOR: "Unix Makefiles" + tags: + - g++-9 + - cmake + <<: *compile_definition + +test_gcov_release_g++9: + variables: + GCOV_EXECUTABLE: "gcov-9" + tags: + - g++-9 + - cmake + needs: + - job: compile_release_g++9 + artifacts: true + <<: *test_gcov_definition + +compile_debug_g++9: + variables: + CXX: "g++-9" + CC: "gcc-9" + BUILD_TYPE: "Debug" + GENERATOR: "Unix Makefiles" + tags: + - g++-9 + - cmake + <<: *compile_definition + +test_gcov_debug_g++9: + variables: + GCOV_EXECUTABLE: "gcov-9" + tags: + - g++-9 + - cmake + needs: + - job: compile_debug_g++9 + artifacts: true + coverage: '/^TOTAL.*\s+(\d+%)$/' + <<: *test_gcov_definition + +compile_release_g++8: + variables: + CXX: "g++-8" + CC: "gcc-8" + BUILD_TYPE: "Release" + GENERATOR: "Unix Makefiles" tags: - g++-8 - cmake - dependencies: - - compile_release_g++8 + <<: *compile_definition -compile_example_release_g++8: - stage: build - script: - - mkdir build - - ./doc/sample_extractor.p6 doc - - cd build - - CXX=g++-8 CC=gcc-8 cmake -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=Release .. - - make -j2 VERBOSE=1 +test_gcov_release_g++8: + variables: + GCOV_EXECUTABLE: "gcov-8" tags: - g++-8 - cmake - artifacts: - paths: - - build + needs: + - job: compile_release_g++8 + artifacts: true + <<: *test_gcov_definition compile_debug_g++8: - stage: build - script: - - mkdir build - - ./doc/sample_extractor.p6 doc - - cd build - - CXX=g++-8 CC=gcc-8 cmake -DlibClaPP_BUILD_TESTS=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_BUILD_COVERAGE=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=Debug .. - - make -j2 VERBOSE=1 + variables: + CXX: "g++-8" + CC: "gcc-8" + BUILD_TYPE: "Debug" + GENERATOR: "Unix Makefiles" tags: - g++-8 - cmake - artifacts: - paths: - - build + <<: *compile_definition -test_debug_g++8: - stage: test - script: - - cd build - - ./tests/libclapp_tests - - gcovr -r ../ -e ../third_party/ -e ../tests/ -e ../examples/ --gcov-executable="gcov-8" +test_gcov_debug_g++8: + variables: + GCOV_EXECUTABLE: "gcov-8" tags: - g++-8 - cmake + needs: + - job: compile_debug_g++8 + artifacts: true coverage: '/^TOTAL.*\s+(\d+%)$/' - dependencies: - - compile_debug_g++8 + <<: *test_gcov_definition -compile_install_g++7: - stage: build - script: - - mkdir build - - ./doc/sample_extractor.p6 doc - - cd build - - CXX=g++-7 CC=gcc-7 cmake -DlibClaPP_BUILD_TESTS=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_BUILD_COVERAGE=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=Release .. - - make -j2 VERBOSE=1 - - make install - - echo -e '#include<clapp/main_parser.h>\n#include<clapp/build_info.h>\n#include<iostream>\nint main (int argc, char *argv[]) {\nstd::cout << clapp::build_info::project_name << " v" << clapp::build_info::version << std::endl;\n}' | g++-7 `pkg-config --libs --cflags libclapp` -I../third_party/GSL/include/ --std=c++17 -o out -x c++ - +compile_release_g++7: + variables: + CXX: "g++-7" + CC: "gcc-7" + BUILD_TYPE: "Release" + GENERATOR: "Unix Makefiles" tags: - g++-7 - cmake - artifacts: - paths: - - build + <<: *compile_definition -compile_release_g++7: - stage: build - script: - - mkdir build - - ./doc/sample_extractor.p6 doc - - cd build - - CXX=g++-7 CC=gcc-7 cmake -DlibClaPP_BUILD_TESTS=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_BUILD_COVERAGE=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=Release .. - - make -j2 VERBOSE=1 +test_gcov_release_g++7: + variables: + GCOV_EXECUTABLE: "gcov-7" tags: - g++-7 - cmake - artifacts: - paths: - - build + needs: + - job: compile_release_g++7 + artifacts: true + <<: *test_gcov_definition -test_release_g++7: - stage: test - script: - - cd build - - ./tests/libclapp_tests - - gcovr -r ../ -e ../third_party/ -e ../tests/ -e ../examples/ --gcov-executable="gcov-7" +compile_debug_g++7: + variables: + CXX: "g++-7" + CC: "gcc-7" + BUILD_TYPE: "Debug" + GENERATOR: "Unix Makefiles" tags: - g++-7 - cmake - dependencies: - - compile_release_g++7 + <<: *compile_definition -compile_debug_g++7: - stage: build - script: - - mkdir build - - ./doc/sample_extractor.p6 doc - - cd build - - CXX=g++-7 CC=gcc-7 cmake -DlibClaPP_BUILD_TESTS=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_BUILD_COVERAGE=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=Debug .. - - make -j2 VERBOSE=1 +test_gcov_debug_g++7: + variables: + GCOV_EXECUTABLE: "gcov-7" tags: - g++-7 - cmake - artifacts: - paths: - - build + needs: + - job: compile_debug_g++7 + artifacts: true + coverage: '/^TOTAL.*\s+(\d+%)$/' + <<: *test_gcov_definition -test_debug_g++7: - stage: test - script: - - cd build - - ./tests/libclapp_tests - - gcovr -r ../ -e ../third_party/ -e ../tests/ -e ../examples/ --gcov-executable="gcov-7" +compile_release_clang++7: + variables: + CXX: "clang++-7" + CC: "clang-7" + BUILD_TYPE: "Release" + GENERATOR: "Unix Makefiles" tags: - - g++-7 + - clang++-7 + - cmake + <<: *compile_definition + +test_gcov_release_clang++7: + variables: + GCOV_EXECUTABLE: "llvm-cov-7 gcov" + tags: + - clang++-7 - cmake + needs: + - job: compile_release_clang++7 + artifacts: true + <<: *test_gcov_definition + +compile_debug_clang++7: + variables: + CXX: "clang++-7" + CC: "clang-7" + BUILD_TYPE: "Debug" + GENERATOR: "Unix Makefiles" + tags: + - clang++-7 + - cmake + <<: *compile_definition + +test_gcov_debug_clang++7: + variables: + GCOV_EXECUTABLE: "llvm-cov-7 gcov" + tags: + - clang++-7 + - cmake + needs: + - job: compile_debug_clang++7 + artifacts: true coverage: '/^TOTAL.*\s+(\d+%)$/' - dependencies: - - compile_debug_g++7 + <<: *test_gcov_definition -compile_example_release_g++7: - stage: build - script: - - mkdir build - - ./doc/sample_extractor.p6 doc - - cd build - - CXX=g++-7 CC=gcc-7 cmake -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=Release .. - - make -j2 VERBOSE=1 +compile_release_clang++8: + variables: + CXX: "clang++-8" + CC: "clang-8" + BUILD_TYPE: "Release" + GENERATOR: "Unix Makefiles" tags: - - g++-7 + - clang++-8 - cmake - artifacts: - paths: - - build + <<: *compile_definition -compile_install_clang++9: - stage: build - script: - - mkdir build - - ./doc/sample_extractor.p6 doc - - cd build - - CXX=clang++-9 CC=clang-9 cmake -DlibClaPP_BUILD_TESTS=On -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_BUILD_COVERAGE=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=Release .. - - make -j2 VERBOSE=1 - - make install - - echo -e '#include<clapp/main_parser.h>\n#include<clapp/build_info.h>\n#include<iostream>\nint main (int argc, char *argv[]) {\nstd::cout << clapp::build_info::project_name << " v" << clapp::build_info::version << std::endl;\n}' | clang++-9 `pkg-config --libs --cflags libclapp` -I../third_party/GSL/include/ --std=c++17 -o out -x c++ - +test_gcov_release_clang++8: + variables: + GCOV_EXECUTABLE: "llvm-cov-8 gcov" + tags: + - clang++-8 + - cmake + needs: + - job: compile_release_clang++8 + artifacts: true + <<: *test_gcov_definition + +compile_debug_clang++8: + variables: + CXX: "clang++-8" + CC: "clang-8" + BUILD_TYPE: "Debug" + GENERATOR: "Unix Makefiles" + tags: + - clang++-8 + - cmake + <<: *compile_definition + +test_gcov_debug_clang++8: + variables: + GCOV_EXECUTABLE: "llvm-cov-8 gcov" + tags: + - clang++-8 + - cmake + needs: + - job: compile_debug_clang++8 + artifacts: true + coverage: '/^TOTAL.*\s+(\d+%)$/' + <<: *test_gcov_definition + +compile_release_clang++9: + variables: + CXX: "clang++-9" + CC: "clang-9" + BUILD_TYPE: "Release" + GENERATOR: "Unix Makefiles" tags: - clang++-9 - cmake - artifacts: - paths: - - build + <<: *compile_definition + +test_gcov_release_clang++9: + variables: + GCOV_EXECUTABLE: "llvm-cov-9 gcov" + tags: + - clang++-9 + - cmake + needs: + - job: compile_release_clang++9 + artifacts: true + <<: *test_gcov_definition compile_debug_clang++9: - stage: build - script: - - mkdir build - - ./doc/sample_extractor.p6 doc - - cd build - - CXX=clang++-9 CC=clang-9 cmake -DlibClaPP_BUILD_TESTS=On -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_BUILD_COVERAGE=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=Debug .. - - make -j2 VERBOSE=1 + variables: + CXX: "clang++-9" + CC: "clang-9" + BUILD_TYPE: "Debug" + GENERATOR: "Unix Makefiles" tags: - clang++-9 - cmake - artifacts: - paths: - - build + <<: *compile_definition -test_debug_clang++9: - stage: test - script: - - cd build - - ./tests/libclapp_tests - - gcovr -r ../ -e ../third_party/ -e ../tests/ -e ../examples/ --gcov-executable="llvm-cov-9 gcov" +test_gcov_debug_clang++9: + variables: + GCOV_EXECUTABLE: "llvm-cov-9 gcov" tags: - clang++-9 - cmake + needs: + - job: compile_debug_clang++9 + artifacts: true coverage: '/^TOTAL.*\s+(\d+%)$/' - dependencies: - - compile_debug_clang++9 + <<: *test_gcov_definition -clang-tidy-9: - stage: build - script: - - mkdir build - - ./doc/sample_extractor.p6 doc - - cd build - - CXX=clang++-9 CC=clang-9 cmake -DCLANG_TIDY=clang-tidy-9 -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_BUILD_TESTS=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DlibClaPP_BUILD_COVERAGE=On -DCMAKE_BUILD_TYPE=Debug -DlibClaPP_CLANG_TIDY=On .. - - make VERBOSE=1 +compile_debug_clang-tidy-7: + variables: + CXX: "clang++-7" + CC: "clang-7" + CLANG_TIDY: "clang-tidy-7" + BUILD_TYPE: "Debug" + GENERATOR: "Unix Makefiles" + tags: + - clang++-7 + - cmake + <<: *compile_clang_tidy_definition + +compile_debug_clang-tidy-8: + variables: + CXX: "clang++-8" + CC: "clang-8" + CLANG_TIDY: "clang-tidy-8" + BUILD_TYPE: "Debug" + GENERATOR: "Unix Makefiles" tags: - clang++-8 - cmake - artifacts: - paths: - - build + <<: *compile_clang_tidy_definition -compile_release_clang++9: - stage: build - script: - - mkdir build - - ./doc/sample_extractor.p6 doc - - cd build - - CXX=clang++-9 CC=clang-9 cmake -DlibClaPP_BUILD_TESTS=On -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_BUILD_COVERAGE=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=Release .. - - make -j2 VERBOSE=1 +compile_debug_clang-tidy-9: + variables: + CXX: "clang++-9" + CC: "clang-9" + CLANG_TIDY: "clang-tidy-9" + BUILD_TYPE: "Debug" + GENERATOR: "Unix Makefiles" tags: - clang++-9 - cmake - artifacts: - paths: - - build + <<: *compile_clang_tidy_definition -test_release_clang++9: - stage: test - script: - - cd build - - ./tests/libclapp_tests - - gcovr -r ../ -e ../third_party/ -e ../tests/ -e ../examples/ --gcov-executable="llvm-cov-9 gcov" +compile_install_debug_ninja_clang++9: + variables: + CXX: "clang++-9" + CC: "clang-9" + BUILD_TYPE: "Debug" + GENERATOR: "Ninja" tags: - clang++-9 - cmake - dependencies: - - compile_release_clang++9 + <<: *compile_install_definition -compile_example_release_clang++9: - stage: build - script: - - mkdir build - - ./doc/sample_extractor.p6 doc - - cd build - - CXX=clang++-9 CC=clang-9 cmake -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=Release .. - - make -j2 VERBOSE=1 +compile_install_release_ninja_clang++9: + variables: + CXX: "clang++-9" + CC: "clang-9" + BUILD_TYPE: "Release" + GENERATOR: "Ninja" tags: - clang++-9 - cmake - artifacts: - paths: - - build + <<: *compile_install_definition -compile_install_clang++8: - stage: build - script: - - mkdir build - - ./doc/sample_extractor.p6 doc - - cd build - - CXX=clang++-8 CC=clang-8 cmake -DlibClaPP_BUILD_TESTS=On -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_BUILD_COVERAGE=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=Release .. - - make -j2 VERBOSE=1 - - make install - - echo -e '#include<clapp/main_parser.h>\n#include<clapp/build_info.h>\n#include<iostream>\nint main (int argc, char *argv[]) {\nstd::cout << clapp::build_info::project_name << " v" << clapp::build_info::version << std::endl;\n}' | clang++-8 `pkg-config --libs --cflags libclapp` -I../third_party/GSL/include/ --std=c++17 -o out -x c++ - +compile_release_ninja_g++9: + variables: + CXX: "g++-9" + CC: "gcc-9" + BUILD_TYPE: "Release" + GENERATOR: "Ninja" tags: - - clang++-8 + - g++-9 - cmake - artifacts: - paths: - - build + <<: *compile_definition -compile_debug_clang++8: - stage: build - script: - - mkdir build - - ./doc/sample_extractor.p6 doc - - cd build - - CXX=clang++-8 CC=clang-8 cmake -DlibClaPP_BUILD_TESTS=On -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_BUILD_COVERAGE=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=Debug .. - - make -j2 VERBOSE=1 +test_gcov_release_ninja_g++9: + variables: + GCOV_EXECUTABLE: "gcov-9" tags: - - clang++-8 + - g++-9 - cmake - artifacts: - paths: - - build + needs: + - job: compile_release_ninja_g++9 + artifacts: true + <<: *test_gcov_definition -test_debug_clang++8: - stage: test - script: - - cd build - - ./tests/libclapp_tests - - gcovr -r ../ -e ../third_party/ -e ../tests/ -e ../examples/ --gcov-executable="llvm-cov-8 gcov" +compile_debug_ninja_g++9: + variables: + CXX: "g++-9" + CC: "gcc-9" + BUILD_TYPE: "Debug" + GENERATOR: "Ninja" tags: - - clang++-8 + - g++-9 - cmake + <<: *compile_definition + +test_gcov_debug_ninja_g++9: + variables: + GCOV_EXECUTABLE: "gcov-9" + tags: + - g++-9 + - cmake + needs: + - job: compile_debug_ninja_g++9 + artifacts: true coverage: '/^TOTAL.*\s+(\d+%)$/' - dependencies: - - compile_debug_clang++8 + <<: *test_gcov_definition + +#TODO: address sanitizer currently gives false positives on arm64 +#compile_release_address_sanitizer_g++9: +# variables: +# CXX: "g++-9" +# CC: "gcc-9" +# BUILD_TYPE: "Release" +# GENERATOR: "Unix Makefiles" +# ADDITIONAL_OPTION: "-DlibClaPP_ENABLE_SANITIZER_ADDRESS=On" +# tags: +# - g++-9 +# - cmake +# <<: *compile_definition +# +#test_release_address_sanitizer_g++9: +# tags: +# - g++-9 +# - cmake +# needs: +# - job: compile_release_address_sanitizer_g++9 +# artifacts: true +# <<: *test_definition +# +#compile_debug_address_sanitizer_g++9: +# variables: +# CXX: "g++-9" +# CC: "gcc-9" +# BUILD_TYPE: "Debug" +# GENERATOR: "Unix Makefiles" +# ADDITIONAL_OPTION: "-DlibClaPP_ENABLE_SANITIZER_ADDRESS=On" +# tags: +# - g++-9 +# - cmake +# <<: *compile_definition +# +#test_debug_address_sanitizer_g++9: +# tags: +# - g++-9 +# - cmake +# needs: +# - job: compile_debug_address_sanitizer_g++9 +# artifacts: true +# coverage: '/^TOTAL.*\s+(\d+%)$/' +# <<: *test_definition + +compile_release_undefined_behavior_sanitizer_g++9: + variables: + CXX: "g++-9" + CC: "gcc-9" + BUILD_TYPE: "Release" + GENERATOR: "Unix Makefiles" + ADDITIONAL_OPTION: "-DlibClaPP_ENABLE_SANITIZER_UNDEFINED_BEHAVIOR=On" + tags: + - g++-9 + - cmake + <<: *compile_definition -clang-tidy-8: - stage: build - script: - - mkdir build - - ./doc/sample_extractor.p6 doc - - cd build - - CXX=clang++-8 CC=clang-8 cmake -DCLANG_TIDY=clang-tidy-8 -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_BUILD_TESTS=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DlibClaPP_BUILD_COVERAGE=On -DCMAKE_BUILD_TYPE=Debug -DlibClaPP_CLANG_TIDY=On .. - - make VERBOSE=1 +test_release_undefined_behavior_sanitizer_g++9: tags: - - clang++-8 + - g++-9 - cmake - artifacts: - paths: - - build + needs: + - job: compile_release_undefined_behavior_sanitizer_g++9 + artifacts: true + <<: *test_definition -compile_release_clang++8: - stage: build - script: - - mkdir build - - ./doc/sample_extractor.p6 doc - - cd build - - CXX=clang++-8 CC=clang-8 cmake -DlibClaPP_BUILD_TESTS=On -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_BUILD_COVERAGE=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=Release .. - - make -j2 VERBOSE=1 +compile_debug_undefined_behavior_sanitizer_g++9: + variables: + CXX: "g++-9" + CC: "gcc-9" + BUILD_TYPE: "Debug" + GENERATOR: "Unix Makefiles" + ADDITIONAL_OPTION: "-DlibClaPP_ENABLE_SANITIZER_UNDEFINED_BEHAVIOR=On" tags: - - clang++-8 + - g++-9 - cmake - artifacts: - paths: - - build + <<: *compile_definition -test_release_clang++8: - stage: test - script: - - cd build - - ./tests/libclapp_tests - - gcovr -r ../ -e ../third_party/ -e ../tests/ -e ../examples/ --gcov-executable="llvm-cov-8 gcov" +test_debug_undefined_behavior_sanitizer_g++9: tags: - - clang++-8 + - g++-9 - cmake - dependencies: - - compile_release_clang++8 + needs: + - job: compile_debug_undefined_behavior_sanitizer_g++9 + artifacts: true + coverage: '/^TOTAL.*\s+(\d+%)$/' + <<: *test_definition -compile_example_release_clang++8: - stage: build - script: - - mkdir build - - ./doc/sample_extractor.p6 doc - - cd build - - CXX=clang++-8 CC=clang-8 cmake -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=Release .. - - make -j2 VERBOSE=1 +compile_release_address_sanitizer_clang++9: + variables: + CXX: "clang++-9" + CC: "clang-9" + BUILD_TYPE: "Release" + GENERATOR: "Unix Makefiles" + ADDITIONAL_OPTION: "-DlibClaPP_ENABLE_SANITIZER_ADDRESS=On" tags: - - clang++-8 + - clang++-9 - cmake - artifacts: - paths: - - build + <<: *compile_definition -compile_install_clang++7: - stage: build - script: - - mkdir build - - ./doc/sample_extractor.p6 doc - - cd build - - CXX=clang++-7 CC=clang-7 cmake -DlibClaPP_BUILD_TESTS=On -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_BUILD_COVERAGE=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=Release .. - - make -j2 VERBOSE=1 - - make install - - echo -e '#include<clapp/main_parser.h>\n#include<clapp/build_info.h>\n#include<iostream>\nint main (int argc, char *argv[]) {\nstd::cout << clapp::build_info::project_name << " v" << clapp::build_info::version << std::endl;\n}' | clang++-7 `pkg-config --libs --cflags libclapp` -I../third_party/GSL/include/ --std=c++17 -o out -x c++ - +test_release_address_sanitizer_clang++9: tags: - - clang++-7 + - clang++-9 - cmake - artifacts: - paths: - - build + needs: + - job: compile_release_address_sanitizer_clang++9 + artifacts: true + <<: *test_definition -compile_debug_clang++7: - stage: build - script: - - mkdir build - - ./doc/sample_extractor.p6 doc - - cd build - - CXX=clang++-7 CC=clang-7 cmake -DlibClaPP_BUILD_TESTS=On -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DlibClaPP_BUILD_COVERAGE=On -DCMAKE_BUILD_TYPE=Debug .. - - make -j2 VERBOSE=1 +compile_debug_address_sanitizer_clang++9: + variables: + CXX: "clang++-9" + CC: "clang-9" + BUILD_TYPE: "Debug" + GENERATOR: "Unix Makefiles" + ADDITIONAL_OPTION: "-DlibClaPP_ENABLE_SANITIZER_ADDRESS=On" tags: - - clang++-7 + - clang++-9 - cmake - artifacts: - paths: - - build + <<: *compile_definition -test_debug_clang++7: - stage: test - script: - - cd build - - ./tests/libclapp_tests - - gcovr -r ../ -e ../third_party/ -e ../tests/ -e ../examples/ --gcov-executable="llvm-cov-7 gcov" +test_debug_address_sanitizer_clang++9: tags: - - clang++-7 + - clang++-9 - cmake + needs: + - job: compile_debug_address_sanitizer_clang++9 + artifacts: true coverage: '/^TOTAL.*\s+(\d+%)$/' - dependencies: - - compile_debug_clang++7 + <<: *test_definition -clang-tidy-7: - stage: build - script: - - mkdir build - - ./doc/sample_extractor.p6 doc - - cd build - - CXX=clang++-7 CC=clang-7 cmake -DCLANG_TIDY=clang-tidy-7 -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_BUILD_TESTS=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DlibClaPP_BUILD_COVERAGE=On -DCMAKE_BUILD_TYPE=Debug -DlibClaPP_CLANG_TIDY=On .. - - make VERBOSE=1 +compile_release_undefined_behavior_sanitizer_clang++9: + variables: + CXX: "clang++-9" + CC: "clang-9" + BUILD_TYPE: "Release" + GENERATOR: "Unix Makefiles" + ADDITIONAL_OPTION: "-DlibClaPP_ENABLE_SANITIZER_UNDEFINED_BEHAVIOR=On" tags: - - clang++-7 + - clang++-9 - cmake - artifacts: - paths: - - build + <<: *compile_definition -compile_release_clang++7: - stage: build - script: - - mkdir build - - ./doc/sample_extractor.p6 doc - - cd build - - CXX=clang++-7 CC=clang-7 cmake -DlibClaPP_BUILD_TESTS=On -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_BUILD_COVERAGE=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=Release .. - - make -j2 VERBOSE=1 +test_release_undefined_behavior_sanitizer_clang++9: tags: - - clang++-7 + - clang++-9 - cmake - artifacts: - paths: - - build + needs: + - job: compile_release_undefined_behavior_sanitizer_clang++9 + artifacts: true + <<: *test_definition -test_release_clang++7: - stage: test - script: - - cd build - - ./tests/libclapp_tests - - gcovr -r ../ -e ../third_party/ -e ../tests/ -e ../examples/ --gcov-executable="llvm-cov-7 gcov" +compile_debug_undefined_behavior_sanitizer_clang++9: + variables: + CXX: "clang++-9" + CC: "clang-9" + BUILD_TYPE: "Debug" + GENERATOR: "Unix Makefiles" + ADDITIONAL_OPTION: "-DlibClaPP_ENABLE_SANITIZER_UNDEFINED_BEHAVIOR=On" tags: - - clang++-7 + - clang++-9 - cmake - dependencies: - - compile_release_clang++7 + <<: *compile_definition -compile_example_release_clang++7: - stage: build - script: - - mkdir build - - ./doc/sample_extractor.p6 doc - - cd build - - CXX=clang++-7 CC=clang-7 cmake -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=Release .. - - make -j2 VERBOSE=1 +test_debug_undefined_behavior_sanitizer_clang++9: tags: - - clang++-7 + - clang++-9 - cmake - artifacts: - paths: - - build + needs: + - job: compile_debug_undefined_behavior_sanitizer_clang++9 + artifacts: true + coverage: '/^TOTAL.*\s+(\d+%)$/' + <<: *test_definition diff --git a/CMakeLists.txt b/CMakeLists.txt index a9f856ef..2af91c75 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.8.0) -set(CMAKE_VERBOSE_MAKEFILE ON) -set(libClaPP_PROJECT_NAME "libClaPP") -project("${libClaPP_PROJECT_NAME}") +project("libClaPP" VERSION 0.2.0) include(CTest) include(CheckCXXCompilerFlag) include(GNUInstallDirs) @@ -17,7 +15,7 @@ set(CMAKE_CXX_FLAGS_DEBUG "-ggdb -O0") set(CMAKE_CXX_FLAGS_RELEASE "-O2 -DNDEBUG") if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - list(APPEND libClaPP_TEST_COMPILE_OPTIONS -Wall -Wextra -Wunreachable-code -Wfloat-equal -Wunused-parameter -Werror -pedantic-errors -Wcast-align -Wcast-qual -Wpointer-arith -Wwrite-strings -Wstrict-overflow=1 -Wformat=2 -Wlogical-op -Wduplicated-cond -Wduplicated-branches -Wdouble-promotion -Wold-style-cast -Wshadow -Wwrite-strings -Wuninitialized -Wconversion -Wunused -Wno-missing-braces -Wnarrowing -Wconversion-null -Wuseless-cast -Wsign-conversion -Wrestrict -Wnull-dereference) + list(APPEND libClaPP_TEST_COMPILE_OPTIONS -Wall -Wextra -Wunreachable-code -Wfloat-equal -Werror -pedantic-errors -Wcast-align -Wcast-qual -Wpointer-arith -Wwrite-strings -Wstrict-overflow=2 -Wformat=2 -Wlogical-op -Wduplicated-cond -Wduplicated-branches -Wdouble-promotion -Wold-style-cast -Wshadow -Wwrite-strings -Wuninitialized -Wconversion -Wunused -Wno-missing-braces -Wnarrowing -Wconversion-null -Wuseless-cast -Wsign-conversion -Wrestrict -Wnull-dereference -Wmisleading-indentation -Wnon-virtual-dtor -Woverloaded-virtual) set(libClaPP_COMPILE_OPTIONS ${libClaPP_TEST_COMPILE_OPTIONS}) list(APPEND libClaPP_COMPILE_OPTIONS -Weffc++) elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") @@ -26,21 +24,33 @@ elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") list(APPEND libClaPP_TEST_COMPILE_OPTION -Wno-global-constructors) endif() -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) - CHECK_CXX_COMPILER_FLAG("-lstdc++fs" COMPILER_SUPPORTS_FILESYSTEM) if(COMPILER_SUPPORTS_FILESYSTEM) set(libClaPP_FS_LINKER_FLAG "-lstdc++fs") endif() +find_package(PkgConfig REQUIRED) +string(TIMESTAMP libClaPP_CONFIG_TIME "%Y-%m-%d %H:%M:%S") +set (THIRD_PARTY_DIR "${PROJECT_SOURCE_DIR}/third_party") + option(libClaPP_CLANG_TIDY "Clang-Tidy." Off) +option(libClaPP_CPPCHECK "CPPCheck." Off) option(libClaPP_BUILD_TESTS "Build tests." Off) option(libClaPP_BUILD_EXAMPLES "Build examples." Off) option(libClaPP_BUILD_DOC_CODE "Build doc code." Off) option(libClaPP_BUILD_COVERAGE "Cretae gcov build." Off) option(libClaPP_SUBMODULE_DEPENDENCIES "Use dependencies from git submodules." Off) +option(libClaPP_ENABLE_IPO "Enable Iterprocedural Optimization, aka Link Time Optimization (LTO)" Off) + +if(libClaPP_ENABLE_IPO) + include(CheckIPOSupported) + check_ipo_supported(RESULT result OUTPUT output) + if(result) + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) + else() + message(SEND_ERROR "IPO is not supported: ${output}") + endif() +endif() if(libClaPP_BUILD_COVERAGE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage") @@ -61,18 +71,27 @@ if(libClaPP_CLANG_TIDY) if(CLANG_TIDY) message(STATUS "clang-tidy found: ${CLANG_TIDY}") - set(CLANG_TIDY_ARGS "${CLANG_TIDY}" "-header-filter=src/include;-warnings-as-errors=*;-checks=*,-google-runtime-references,-fuchsia-default-arguments,-fuchsia-overloaded-operator,-cert-dcl50-cpp,-clang-diagnostic-unused-command-line-argument") + set(CLANG_TIDY_ARGS "${CLANG_TIDY}" "-header-filter=src/include;-warnings-as-errors=*;-checks=*,-google-runtime-references,-fuchsia-default-arguments,-fuchsia-overloaded-operator,-cert-dcl50-cpp,-clang-diagnostic-unused-command-line-argument,-modernize-use-trailing-return-type,-fuchsia-default-arguments-calls") endif() endif() -find_package(PkgConfig REQUIRED) +if(libClaPP_CPPCHECK) + if(CMAKE_VERSION VERSION_GREATER 3.10) + find_program(CPPCHECK cppcheck) + if(CPPCHECK) + set(CMAKE_CXX_CPPCHECK ${CPPCHECK} --enable=all --suppress=*:${CMAKE_CURRENT_SOURCE_DIR}/src/include/clapp/filesystem.h:27 --suppress=*:${THIRD_PARTY_DIR}/* --inconclusive -v -i${THIRD_PARTY_DIR}) + else() + message(SEND_ERROR "CPPCheck requested but executable not found.") + endif() + else() + message(SEND_ERROR "CPPCheck requested but current CMAKE is not compatible.") + endif() +endif() -set (project_VERSION_MAJOR 0) -set (project_VERSION_MINOR 1) -set (project_VERSION_PATCH_LEVEL 0) -string(TIMESTAMP project_CONFIG_TIME "%Y-%m-%d %H:%M:%S") +add_library(ClaPP_OPTIONS INTERFACE) +target_compile_features(ClaPP_OPTIONS INTERFACE cxx_std_17) -set (THIRD_PARTY_DIR "${PROJECT_SOURCE_DIR}/third_party") +include(cmake/sanitizer.cmake) add_library(libClaPP_GSL INTERFACE) if(libClaPP_SUBMODULE_DEPENDENCIES) @@ -112,6 +131,7 @@ else() endif() endif() +enable_testing() add_subdirectory(src) if(libClaPP_BUILD_EXAMPLES) diff --git a/README.md b/README.md index 405d568d..a5e03577 100644 --- a/README.md +++ b/README.md @@ -8,56 +8,69 @@ libClaPP: ========= -[](../commits/master/) -[](../commits/master/) +[](https://git.libclapp.org/libclapp/clapp/-/commits/master) +[](https://git.libclapp.org/libclapp/clapp/-/commits/master) libClaPP is an open source command line argument processing library for C++. It supports the processing of GNU options (long and short options) as well as positional arguments. +It is a strongly typed library that can be used to parse the command line arguments into the correct type, +validate the arguments using custom constraints and automatically generates a help message with respect +to the types and the constraints. Build: ------ ### Dependencies: -libClaPP only depends on the [GSL](https://github.com/microsoft/GSL). -If you want to build the unit tests, also [google test](https://github.com/google/googletest) is required. +libClaPP only depends on the [GSL](https://github.com/microsoft/GSL), but this library is shipped as git-submodule dependency within this git repository. -### Build the library -Since all dependencies are already included in this repository as submodules, you may prefer to use the shipped dependencies via the CMake option `libClaPP_SUBMODULE_DEPENDENCIES` enabled: +If the unit tests of this libray should be built, also [google test](https://github.com/google/googletest) is required (is also shipped as git-submodule dependency). +If the listings in the [doc/](doc/)-folder should be extracted and build too, a Raku-(Perl 6)-interpreter is required. A very common Raku interpreter is [Rakudo](https://rakudo.org/), which can be installed on Debian/Ubuntu by executing `apt install rakudo`. + +### Build the library: +Since all build dependencies are already included in this repository as submodules, you may prefer to use the shipped dependencies via the CMake option `libClaPP_SUBMODULE_DEPENDENCIES` enabled: git clone --recurse-submodules https://git.libclapp.org/libclapp/clapp.git mkdir build cd build cmake .. -DlibClaPP_SUBMODULE_DEPENDENCIES=On - make + cmake --build . +#### Build with system libraries: But if all dependencies are installed on your system, the following steps are sufficient. git clone https://git.libclapp.org/libclapp/clapp.git mkdir build cd build cmake .. - make + cmake --build . -Build the library in debug mode with submodules, unitests and check code coverage: +#### Build the library in debug mode with submodules, unitests and check code coverage: git clone --recurse-submodules https://git.libclapp.org/libclapp/clapp.git mkdir build cd build cmake -DlibClaPP_BUILD_TESTS=On -DlibClaPP_BUILD_COVERAGE=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=Debug .. - make - ./tests/libclapp_tests + cmake --build . + ctest -V -R libclapp_tests gcovr -r ../ -e ../third_party/ -e ../tests/ -e ../examples/ -Build the library in debug mode with submodules, unitests, examples and check code coverage: +#### Build the library in debug mode with submodules, unitests, examples and check code coverage: git clone --recurse-submodules https://git.libclapp.org/libclapp/clapp.git mkdir build cd build cmake -DlibClaPP_BUILD_TESTS=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_BUILD_COVERAGE=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=Debug .. - make - ./tests/libclapp_tests + cmake --build . + ctest -V -R libclapp_tests gcovr -r ../ -e ../third_party/ -e ../tests/ -e ../examples/ + ctest -V -E libclapp_tests + +#### Build with gcc/clang sanitizers: +LibClaPP can be built with the following sanitizers: Address sanitizer, memory sanitizer, undefined behavior sanitizer or a thread sanitizer. +To enable these sanitizers, one of the following options must be set: `libClaPP_ENABLE_SANITIZER_ADDRESS`, `libClaPP_ENABLE_SANITIZER_MEMORY`, +`libClaPP_ENABLE_SANITIZER_UNDEFINED_BEHAVIOR`, `libClaPP_ENABLE_SANITIZER_THREAD`. +For easy switching between different sanitizers, the tool `ccmake` may be used instead of pure `cmake`. ### Install the library @@ -69,23 +82,16 @@ To uninstall the library, remove all files that were installed: xargs rm < install_manifest.txt -Terminology: -------------- -In this project the following terms are used: - -* Option: An option can be either a short option or a long option. - A short option consists of a single `-` followed by single character (e.g. `-h`). - A long option consists of two `--` followed by an arbitrary string (e.g. `--help`). -* Parameter: Some options may require additional parameters (.e.g. `--file=/path/to/file`). -* Argument: An argument is the short form for positional argument. - Thus, arguments higly depend on the position where they occur. - Unlinke to options that may occur anywhere else. -* Sub-Parser: A subparser is always implicitly the last argument of a the current parser. - Once this argument is given, a completely new parsing instance is started. +Documentation: +-------------- +A short documentation of libClaPP is available in [doc/doc.md](doc/doc.md). Example: -------- Some examples can be found in the [examples](examples) folder. +Additionally, [doc/doc.md](doc/doc.md) also contains a few examples. + +For a quick start take a look at [examples/short_example.cpp](examples/short_example.cpp). Coverage: --------- @@ -97,3 +103,12 @@ Clang-Tidy: ----------- CXX=clang++-7 CC=clang-7 cmake -DCLANG_TIDY=clang-tidy-7 -DlibClaPP_BUILD_TESTS=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DlibClaPP_BUILD_COVERAGE=On -DCMAKE_BUILD_TYPE=Debug -DlibClaPP_CLANG_TIDY=On .. + +Clang-Format: +------------- + +This project uses a clang-format style based on the Google style (`clang-format-7 -style=Google -dump-config`) but modified a bit (see [.clang-format](.clang-format)). + +To format the whole project, use the following command: + + clang-format-7 -i examples/*.cpp examples/*.h examples/*.hpp src/clapp/*.cpp src/include/clapp/*.hpp src/include/clapp/*.h tests/*.cpp tests/*.h tests/*.hpp diff --git a/cmake/git.cmake b/cmake/git.cmake index 341a32ed..0ea9b40f 100644 --- a/cmake/git.cmake +++ b/cmake/git.cmake @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.8.0) -message(STATUS "Resolving GIT Version") +message(STATUS "Resolving GIT Version for libClaPP") find_package(Git) @@ -19,7 +19,7 @@ endif() message(STATUS "trying git `${GIT_EXECUTABLE}`:") execute_process( - COMMAND ${GIT_EXECUTABLE} -C ${CMAKE_SOURCE_DIR} rev-parse --abbrev-ref HEAD + COMMAND ${GIT_EXECUTABLE} -C ${libClaPP_SOURCE_DIR} rev-parse --abbrev-ref HEAD WORKING_DIRECTORY "${local_dir}" OUTPUT_VARIABLE libClaPP_GIT_BRANCH ERROR_QUIET @@ -27,15 +27,24 @@ execute_process( RESULT_VARIABLE libClaPP_result_branch ) execute_process( - COMMAND ${GIT_EXECUTABLE} -C ${CMAKE_SOURCE_DIR} rev-parse --short HEAD + COMMAND ${GIT_EXECUTABLE} -C ${libClaPP_SOURCE_DIR} describe --dirty --broken --always WORKING_DIRECTORY "${local_dir}" OUTPUT_VARIABLE libClaPP_GIT_HASH ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE RESULT_VARIABLE libClaPP_result_hash ) -if(NOT ((libClaPP_result_hash EQUAL 0) AND (libClaPP_result_branch EQUAL 0))) +execute_process( + COMMAND ${GIT_EXECUTABLE} -C ${libClaPP_SOURCE_DIR} describe --dirty --broken --always --tags + WORKING_DIRECTORY "${local_dir}" + OUTPUT_VARIABLE libClaPP_GIT_DESCRIPTION + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE libClaPP_result_description +) +if(NOT ((libClaPP_result_hash EQUAL 0) AND (libClaPP_result_branch EQUAL 0) AND (libClaPP_result_description EQUAL 0))) set(libClaPP_GIT_HASH "unknown") set(libClaPP_GIT_BRANCH "unknown") + set(libClaPP_GIT_DESCRIPTION "unknown") endif() -message(STATUS "GIT branch: ${libClaPP_GIT_BRANCH}, GIT hash: ${libClaPP_GIT_HASH}") +message(STATUS "GIT branch: ${libClaPP_GIT_BRANCH}, GIT hash: ${libClaPP_GIT_HASH}, GIT description: ${libClaPP_GIT_DESCRIPTION}") diff --git a/cmake/sanitizer.cmake b/cmake/sanitizer.cmake new file mode 100644 index 00000000..104fbd1f --- /dev/null +++ b/cmake/sanitizer.cmake @@ -0,0 +1,37 @@ +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + set(libClaPP_SANITIZERS "") + + option(libClaPP_ENABLE_SANITIZER_ADDRESS "Address sanitizer" FALSE) + if(libClaPP_ENABLE_SANITIZER_ADDRESS) + list(APPEND libClaPP_SANITIZERS "address") + endif() + + option(libClaPP_ENABLE_SANITIZER_MEMORY "Memory sanitizer" FALSE) + if(libClaPP_ENABLE_SANITIZER_MEMORY) + list(APPEND libClaPP_SANITIZERS "memory") + endif() + + option(libClaPP_ENABLE_SANITIZER_UNDEFINED_BEHAVIOR "Undefined behavior sanitizer" FALSE) + if(libClaPP_ENABLE_SANITIZER_UNDEFINED_BEHAVIOR) + list(APPEND libClaPP_SANITIZERS "undefined") + endif() + + option(libClaPP_ENABLE_SANITIZER_THREAD "Thread sanitizer" FALSE) + if(libClaPP_ENABLE_SANITIZER_THREAD) + list(APPEND libClaPP_SANITIZERS "thread") + endif() + + list(JOIN libClaPP_SANITIZERS "," libClaPP_LIST_OF_SANITIZERS) +endif() + +if(libClaPP_LIST_OF_SANITIZERS) + message(STATUS "Using the following sanitizers: ${libClaPP_LIST_OF_SANITIZERS}") + if(NOT "${libClaPP_LIST_OF_SANITIZERS}" STREQUAL "") + target_compile_options(ClaPP_OPTIONS + INTERFACE -fsanitize=${libClaPP_LIST_OF_SANITIZERS}) + target_link_libraries(ClaPP_OPTIONS + INTERFACE -fsanitize=${libClaPP_LIST_OF_SANITIZERS}) + endif() +else() + message(STATUS "Using no sanitizers") +endif() diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 1303f269..49a669c5 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -1,6 +1,24 @@ +if(CLANG_TIDY) + set(CLANG_TIDY_DOC_ARGS "${CLANG_TIDY_ARGS},-misc-non-private-member-variables-in-classes,-cppcoreguidelines-avoid-magic-numbers,-readability-magic-numbers") +endif() + +find_program(RAKU_EXECUTABLE NAMES raku perl6) + +add_custom_command( OUTPUT simple_sub_parser.cpp simple_main_parser.cpp DEPENDS doc.md + COMMAND ${RAKU_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/sample_extractor.p6 ${CMAKE_CURRENT_SOURCE_DIR}) + add_executable(libclapp_doc_simple_main_parser simple_main_parser.cpp) target_link_libraries(libclapp_doc_simple_main_parser clapp) target_compile_options(libclapp_doc_simple_main_parser PRIVATE ${libClaPP_COMPILE_OPTIONS}) if(CLANG_TIDY) - set_target_properties(libclapp_doc_simple_main_parser PROPERTIES CXX_CLANG_TIDY "${CLANG_TIDY_ARGS}") + set_target_properties(libclapp_doc_simple_main_parser PROPERTIES CXX_CLANG_TIDY "${CLANG_TIDY_DOC_ARGS}") +endif() +add_test(NAME libclapp_doc_simple_main_parser_test COMMAND ${RAKU_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/sample_executor.p6 simple_main_parser.out) + +add_executable(libclapp_doc_simple_sub_parser simple_sub_parser.cpp) +target_link_libraries(libclapp_doc_simple_sub_parser clapp) +target_compile_options(libclapp_doc_simple_sub_parser PRIVATE ${libClaPP_COMPILE_OPTIONS}) +if(CLANG_TIDY) + set_target_properties(libclapp_doc_simple_sub_parser PROPERTIES CXX_CLANG_TIDY "${CLANG_TIDY_DOC_ARGS}") endif() +add_test(NAME libclapp_doc_simple_sub_parser_test COMMAND ${RAKU_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/sample_executor.p6 simple_sub_parser.out) diff --git a/doc/doc.md b/doc/doc.md index 606b5d46..79a1cbf1 100644 --- a/doc/doc.md +++ b/doc/doc.md @@ -1,11 +1,162 @@ -Value types for arguments or option parameters: -=============================================== +Introduction: +============= + +In libClaPP the following Terminology is used: + +Terminology: +------------- +In this project the following terms are used: + +* Option: An option can be either a short option or a long option. + A short option consists of a single `-` followed by single character (e.g. `-h`). + A long option consists of two `--` followed by an arbitrary string (e.g. `--help`). +* Parameter: Some options may require additional parameters (.e.g. `--file=/path/to/file`). +* Argument: An argument is the short form for positional argument. + Thus, arguments higly depend on the position where they occur. + Unlinke to options that may occur anywhere else. +* Sub-Parser: A subparser is always implicitly the last argument of a the current parser. + Once this argument is given, a completely new parsing instance is started. + +Outlook: +-------- +This document provides an overview of the features of libClaPP. + +The first section starts with the individual value types (for options and arguments). +This includes an introduction to the supported CLI-options and CLI-arguments. +Also, a complete collection of shipped types is given. Including a description, how +custom types can be used. +Finally, additional options for options, arguments or their parameters are described (conaining +constraints or additional help information). + +The next section concludes with the different parser types: main-parser and sub-parser. +This section also contains some complete examples that show, how a ClaPP-based parser looks like. + +Examples: +--------- +This document already contains some short examples that show the basic usage of this library. +However, some further examples can be found in the [../examples](../examples) folder. + + +Value types for arguments or options with or without parameters: +================================================================ This library supports different types for positional arguments and option parameters. The following subsection introduces all supported types and how additional types can be added. In the subsection below, some argument or option parameter restrictions are introduced. -Supported argument or option parameter types: ---------------------------------------------- +CLI-Options: +------------ + +### CLI-Option construction: + +In general, the constructors of all shipped options have these signatures: + +```c++ +ABSTRACT_OPTION(basic_parser_t& parser, LONG_OPTION long_option, SHORT_OPTION short_option, const std::string& description, ADDITIONAL_PARAMS... params); +ABSTRACT_OPTION(basic_parser_t& parser, LONG_OPTION long_option, const std::string& description, ADDITIONAL_PARAMS... params); +ABSTRACT_OPTION(basic_parser_t& parser, SHORT_OPTION short_option, const std::string& description, ADDITIONAL_PARAMS... params); +``` + +where `LONG_OPTION`, `SHORT_OPTION` and `ADDITIONAL_PARAMS` are placeholders for the following types: +* `LONG_OPTION` can either be a std::string, a c-string or a std::vector<std::string>. +* `SHORT_OPTION` can either be a char or a std::vector<char>. +* `ADDITIONAL_PARAMS` are the variadic types that involve additional parameters for the option (For a complete collection of additional parameters see [Additional parameters for arguments or options](#additional-parameters-for-arguments-or-options)). + +Currently, there exist different types of options: +* non-parameter options: These options do not support any parameters. I.e. the CLI parser only checks if the option is given or not. One example is the common `--help|-h` option. +* parameter options: Some options require parameters. I.e. options that take numbers, strings or paths. The parameters must be given directly after the option either separated with `=` or with ` `. +Examples are `--string-opt='parameter'` or `--string-opt 'parameter'`. +* vector-parameter options: These are required, if parameter options can be given multiple times. In this case, the parsed parameters are stored in a `std::vector`. E.g. `--path /tmp/xxx --path=/tmp/yyy`. + +As a default, all options are optional. If an option is required, the additional parameter `clapp::purpose_t::mandatory` must be given to the constructor. + +A complete colletion of supported options is available in [Complete collection of supported argument or option parameter types](#complete-collection-of-supported-argument-or-option-parameter-types). + +Example constructor calls for some different options are the following lines: + +```c++ +clapp::option::uint8_param_option_t{parser_inst, "--long-uint8-param", "a description for an unit8-param", clapp::purpose_t::mandatory}; +clapp::option::sec_param_option_t{parser_inst, "-s", "number of seconds"} +clapp::option::vector_path_param_option_t{parser_inst, "--path", '-p', "a path the the required file", clapp::value::path_exists_t{}}; +clapp::option::vector_path_param_option_t{parser_inst, {"--file", "--f"}, '-f', "a path to the required file", clapp::value::path_exists_t{}}; +``` + +### CLI-Option usage: +In general, all shipped CLI-options can be used in similar ways, as all support the following methods: + +```c++ +template<typename T> +T value() const; +explicit operator bool() const; +bool has_value() const; +bool given() const noexcept; +``` + +The method `value()` can be used to retrieve the parsed value from a CLI-option. +If the CLI-parser could not parse the option value from the command line arguments (i.e. the CLI-option contains no value), +a call to `value()` would throw a `clapp::exception::value_undefined_t`-exception. +To check if `value()` would throw without calling `value()` directly, the methods `has_value()` or `operator bool()` +can be used. Both return `false`, if a call to `value()` would throw and `true` if a call to `value()` would not throw. + +If the CLI option was constructed with an additional `clapp::value::default_value_t`-parameter, a call to `T value()` will +never throw, as `value()` would return the default value. +To distinguish between default values and given values, the method `given()` can be used: +`given()` returns `false`, if `value()` would return the default value from the additional `clapp::value::default_value_t`-parameter +and it returns `true`, if `value()` would return a value that is parsed from the CLI arguments. + +Positional CLI-Arguments: +------------------------- + +In general, the constructors of all shipped (positional) arguments have these signature: + +```c++ +ABSTRACT_ARGUMENT(basic_parser_t& parser, const std::string& argument, const std::string& description, ADDITIONAL_PARAMS... params); +``` + +where `ADDITIONAL_PARAMS` are placeholders for variadic types that involve additional parameters for the argument (For a complete collection of additional parameters see [Additional parameters for arguments or options](#additional-parameters-for-arguments-or-options)). + +Currently, there exist two types of positional arguments: +* regular arguments: are positional arguments that are either mandatory or optional. Note, that mandatory arguments must be registered before optional arguments. Otherwise, positional arguments can't be assigned uniquely. +* variadic arguments: sometimes, it is required to give a variable number of arguments. In this case, variadic position argument types can be used. Similar to the optional arguments before, it is required to define all mandatory arguments before variadic arguments are registered. Also note, that optional arguments and variadic arguments can't be used together. In order to make sure, that the arguments can be parsed uniquely. + +As a default, all arguments are mandatory. If an argument is optional, the additional parameter `clapp::purpose_t::optional` must be given to the constructor. + +A complete colletion of supported arguments is available in [Complete collection of supported argument or option parameter types](#complete-collection-of-supported-argument-or-option-parameter-types). + + +Example constructor calls for arguments are the following lines: + +```c++ +clapp::string_argument_t string_arg{parser_inst, "string-arg", "String argument"}; +clapp::int32_argument_t int_arg{parser_inst, "int-arg", "Int argument", clapp::purpose_t::optional}; +clapp::variadic_string_argument_t variadic_string_arg{parser_inst, "variadic-string-arg", "Variadic String argument"}; +``` + +### CLI-Argument usage: +In general, all shipped CLI-arguments can be used in similar ways, as all support the following methods: + +```c++ +template<typename T> +T value() const; +explicit operator bool() const; +bool has_value() const; +bool given() const noexcept; +``` + +The method `value()` can be used to retrieve the parsed value from a CLI-argument. +If the CLI-parser could not parse the argument value from the command line arguments (i.e. the CLI-argument contains no value), +a call to `value()` would throw a `clapp::exception::value_undefined_t`-exception. +To check if `value()` would throw without calling `value()` directly, the methods `has_value()` or `operator bool()` +can be used. Both return `false`, if a call to `value()` would throw and `true` if a call to `value()` would not throw. + +If the CLI argument was constructed with an additional `clapp::value::default_value_t`-parameter, a call to `T value()` will +never throw, as `value()` would return the default value. +To distinguish between default values and given values, the method `given()` can be used: +`given()` returns `false`, if `value()` would return the default value from the additional `clapp::value::default_value_t`-parameter +and it returns `true`, if `value()` would return a value that is parsed from the CLI arguments. + + +Complete collection of supported argument or option parameter types: +-------------------------------------------------------------------- ### String values: The most basic types for CLI options are string types (`std::string`), as all values in the `argv` parameter can be converted implicitly to (`std::string`). @@ -30,9 +181,10 @@ The following integral types are supported: * `std::uint64_t` As input format for these values the following formats can be used: -* `[+-][1-9][0-9]*`: Decimal numbers -* `[+-]0[0-9]+`: Octal numbers -* `[+-]0x[0-9]+`: Hexadecial numbers +* `[+-]?[1-9][0-9]*`: Decimal numbers +* `[+-]?0[0-7]+`: Octal numbers +* `[+-]?0[Xx][0-9a-fA-F]+`: Hexadecial numbers + If one of these values contains a `.`, only the decimals before this `.` are used. Subsequent decimals are truncted. @@ -77,11 +229,12 @@ If integral values should be used as option parameters, the following types can * `clapp::option::vector_int32_param_option_t` * `clapp::option::vector_int64_param_option_t` + ### Filesystem path values: It is also possible to pass regular filesystem paths as argument or option parameter. The underlying type is the standardized filesystem type `std::filesystem::path`. -If filesystem path values should be used for positional arguments, the following types can be used: +If filesystem path values should be used as positional arguments, the following types can be used: * `clapp::argument::path_argument_t` * `clapp::argument::variadic_path_argument_t` @@ -89,30 +242,88 @@ If filesystem path values should be used as option parameters, the following typ * `clapp::option::path_param_option_t` * `clapp::option::vector_path_param_option_t` +### Floating point values: +Besides the classic integer values, it is also possible to use floating point values as arguments or options. +The underlying types are the standardized types `double` or `float`. + +If floating point values should be used as positional arguments, the following types can be used: +* `clapp::argument::double_argument_t` +* `clapp::argument::float_argument_t` +* `clapp::argument::variadic_double_argument_t` +* `clapp::argument::variadic_float_argument_t` + +If floating point values should be used as otion parameters, the following types can be used: +* `clapp::option::double_param_option_t` +* `clapp::option::float_param_option_t` +* `clapp::option::vector_double_param_option_t` +* `clapp::option::vector_float_param_option_t` + +As input format for these values the following formats can be used: +* `[+-]?[0-9]+(\.[0-9]*)?([eE][+-]?[0-9]+)?`: Decimal numbers (with optional decimal exponent base 10) +* `[+-]?0[xX][0-9a-fA-F]+(\.[0-9]*)?([pP][+-]?[0-9a-fA-F]+)?`: Hexadecial numbers (with optional decimal exponent base 2) +* `[+-]?[Ii][Nn][Ff]([Ii][Nn][Ii][Tt][Yy])?`: INF or INFINITY case insensitive with optional sign. +* `[+-]?[Nn][Aa][Nn]?`: NAN case insensitive with optional sign. + +### Chrono values: +It is also possible to use crhono types as arguments or options. +The underlying types are the standardized types `std::chrono::hours`...`std::chrono::nanoseconds`. + +If chrono values should be used as positional arguments, the following types can be used: +* `clapp::argument::ns_argument_t` +* `clapp::argument::us_argument_t` +* `clapp::argument::ms_argument_t` +* `clapp::argument::sec_argument_t` +* `clapp::argument::min_argument_t` +* `clapp::argument::hours_argument_t` +* `clapp::argument::variadic_ns_argument_t` +* `clapp::argument::variadic_us_argument_t` +* `clapp::argument::variadic_ms_argument_t` +* `clapp::argument::variadic_sec_argument_t` +* `clapp::argument::variadic_min_argument_t` +* `clapp::argument::variadic_hours_argument_t` + +If chrono values should be used as option parameters, the following types can be used: +* `clapp::option::ns_param_option_t` +* `clapp::option::us_param_option_t` +* `clapp::option::ms_param_option_t` +* `clapp::option::sec_param_option_t` +* `clapp::option::min_param_option_t` +* `clapp::option::hours_param_option_t` +* `clapp::option::vector_ns_param_option_t` +* `clapp::option::vector_us_param_option_t` +* `clapp::option::vector_ms_param_option_t` +* `clapp::option::vector_sec_param_option_t` +* `clapp::option::vector_min_param_option_t` +* `clapp::option::vector_hours_param_option_t` + ### Custom types: Sometimes it is required to use custom types as argument or option parameter. For example the following enum declaration: - enum class my_type_t {value1, value2, value3}; +```c++ +enum class my_type_t {value1, value2, value3}; +``` In order to allow this (or other types) as argument or option parameter, first it is required to create a new template specialization for the function `template<typename T> T clapp::value::convert_value<T>(const std::string_view param)`: - template<> - my_type_t clapp::value::convert_value<my_type_t>(const std::string_view param) { - if(param == "value1") { - return my_type_t::value1; - } else if(param == "value2") { - return my_type_t::value2; - } else if(param == "value3") { - return my_type_t::value3; - } else { - throw std::runtime_error("Invalid value for my_type_t."); - } +```c++ +template<> +my_type_t clapp::value::convert_value<my_type_t>(const std::string_view param) { + if(param == "value1") { + return my_type_t::value1; + } else if(param == "value2") { + return my_type_t::value2; + } else if(param == "value3") { + return my_type_t::value3; + } else { + throw std::runtime_error("Invalid value for my_type_t."); } +} +``` As you can see in this example, it is perfectly fine to throw an exception, if the parsed value is invalid.. -Restrictions for arguments or option parameters ------------------------------------------------ +Additional parameters for arguments or options +---------------------------------------------- It is possible to define restrictions for arguments and option parameters. This restrictions can be given to the argument or option parameter types in an arbitrary order as one of the variadic parameters. @@ -124,8 +335,10 @@ As a default (i.e. no mandatory is given), each option is optional! Some example options are: - clapp::option::int32_param_option_t int_opt{ptr_to_parser, "option", 'o', "Description for mandatory option.", clapp::purpose_t::mandatory}; - clapp::option::bool_option_t int_opt{ptr_to_parser, "yes", 'y', "Description for optional option.", clapp::purpose_t::optional}; +```c++ +clapp::option::int32_param_option_t int_opt{ptr_to_parser, "option", 'o', "Description for mandatory option.", clapp::purpose_t::mandatory}; +clapp::option::bool_option_t int_opt{ptr_to_parser, "yes", 'y', "Description for optional option.", clapp::purpose_t::optional}; +``` Arguments can also be mandatory or optional: * Mandatory arguments must always be given. This is also reflected in the help message: `<argument>`. @@ -134,8 +347,10 @@ As a default (i.e. no optional is given), each argument is mandatory! Some example arguments are: - clapp::argument::int32_argument_t int_arg{ptr_to_parser, "argument", "Description for mandatory argument.", clapp::purpose_t::mandatory}; - clapp::argument::bool_option_t int_opt{ptr_to_parser, "argument", "Description for optional argument.", clapp::purpose_t::optional}; +```c++ +clapp::argument::int32_argument_t int_arg{ptr_to_parser, "argument", "Description for mandatory argument.", clapp::purpose_t::mandatory}; +clapp::argument::bool_option_t int_opt{ptr_to_parser, "argument", "Description for optional argument.", clapp::purpose_t::optional}; +``` ### Default value: In order to provide a default value for arguments or option parameters, there exists the class `clapp::value::default_value_t`. @@ -147,8 +362,10 @@ A default value is also reflected in the help message, as `(default-value: <valu Examples are: - clapp::option::int32_option_t int_opt{ptr_to_parser, "int-option", 'i', "Description for optional option.", clapp::purpose_t::optional, clapp::value::default_value_t{10}}; - clapp::option::string_argument_t string_arg{ptr_to_parser, "string-argument", "Description for optional argument.", clapp::purpose_t::optional, clapp::value::default_value_t{"default-string"}}; +```c++ +clapp::option::int32_option_t int_opt{ptr_to_parser, "int-option", 'i', "Description for optional option.", clapp::purpose_t::optional, clapp::value::default_value_t{10}}; +clapp::option::string_argument_t string_arg{ptr_to_parser, "string-argument", "Description for optional argument.", clapp::purpose_t::optional, clapp::value::default_value_t{"default-string"}}; +``` ### Min/Max value: In order to provide a range check for arguments or option parameters, there exists the class `clapp::value::min_max_value_t`. @@ -159,8 +376,10 @@ A min/max value is also reflected in the help message, as `(constraint: [<min>,< Examples are: - clapp::option::int32_option_t int_opt{ptr_to_parser, "int-option", 'i', "Description for int-option.", clapp::value::min_max_value_t{10, 20}}; - clapp::option::int32_argument_t int_arg{ptr_to_parser, "int-argument", "Description for int-argument.", clapp::value::min_max_value_t{20, 40}}; +```c++ +clapp::option::int32_option_t int_opt{ptr_to_parser, "int-option", 'i', "Description for int-option.", clapp::value::min_max_value_t{10, 20}}; +clapp::option::int32_argument_t int_arg{ptr_to_parser, "int-argument", "Description for int-argument.", clapp::value::min_max_value_t{20, 40}}; +``` ### Path exists: In order to provide a check for filesystem-path arguments or option parameters, there exists the class `clapp::value::path_exists_t`. @@ -173,8 +392,10 @@ Note: this class may only be used for filesystem path values. Examples are: - clapp::option::path_param_option_t file_opt{ptr_to_parser, "filename-option", 'f', "Description for filename option.", clapp::value::path_exists_t{}}; - clapp::option::path_argument_t file_arg{ptr_to_parser, "filenem-argument", "Description for filename argument.", clapp::value::path_exists_t{}}; +```c++ +clapp::option::path_param_option_t file_opt{ptr_to_parser, "filename-option", 'f', "Description for filename option.", clapp::value::path_exists_t{}}; +clapp::option::path_argument_t file_arg{ptr_to_parser, "filenem-argument", "Description for filename argument.", clapp::value::path_exists_t{}}; +``` ### Custom restrictions: Of course, it is possible to create own restriction classes. @@ -192,14 +413,93 @@ Parser: Main Parser: ------------ The main parser is the key element of the libClaPP CLI parsing library. -Typically, it is a derivied class with the base class `clapp::parser::basic_main_parser_t`. -One of the benefit of the libClaPP library is that all arguments and options can be registered +Typically, it is a derivied class of the base class `clapp::parser::basic_main_parser_t`. +One of the benefits of the libClaPP library is that all arguments and options can be registered as class members. -Thus, by simply passing around a reference (or a pointer) to the main parser, all parsed options -or arguments are condensed in a class instance. +Thus, by simply passing around a reference (or a pointer) to this main parser, all parsed +arguments or option can be be accessed easily. + +### Main parser construction: +The main-parser base class `clapp::parser::basic_main_parser_t` contains only one constructor. +This constructor has no arguments, but since it is a base-class, you can extend it easily by +your derived class. See the listing below for an example. + +### Main parser usage: +The main-parser base class `clapp::parser::basic_main_parser_t` is shipped with several useful methods. +Most of them are listed here, but for a complete collection take a look at +[src/include/clapp/main_parser.h](src/include/clapp/main_parser.h). +Since the main-parser base class `clapp::parser::basic_main_parser_t` is itself derived from +`clapp::parser::basic_parser_t`, you may also look at +[src/include/clapp/parser.h](src/include/clapp/parser.h) +for a complete collection of derived methods. + +```c++ +void parse(int argc, const char* const* argv); +void parse(const arg_t& arg); +void parse_and_validate(int argc, const char* const* argv); +void validate() const; +void validate_recursive() const; +explicit operator bool() const; +std::string get_executable() const; +std::string gen_help_msg(std::size_t rec_depth) const; +std::size_t get_num_processed_arguments() const; +const basic_parser_t& get_active_parser() const; +``` +#### The `parse()` methods: +The `parse()` methods can be used to parse the command line arguments. +These functions may throw exceptions of type `clapp::clapp_exception_t` (or a derived type of this type). +For example, if invalid or unknown options are given, or given values could not be converted to the required type. +But these functions only try to parse the values. +Thus, in order to check if all mandatory options and arguments are given, the methods `validate()`, `validate_recursive()` or `parse_and_validate()` may be used. + +#### The `validate()` method: +The `validate()` method can be used to check the requirements of the parsed result. +Thus, it checks if all mandatory arguments or options were given and ensures that all constraints are met. +Note: this method only validates all arguments or options of the current parser instance. Thus, if subparsers are used, the `validate()` method ignores all subparsers. If you want to validate the main-parser and all sub-parsers, use `validate_recursive()`. +If the validation fails, it throws an exception of type `clapp::clapp_exception_t` (or a derived type of this type). + +#### The `validate_recursive()` method: +This method ensures that all mandatory arguments or options were given and ensures that all constraints are met. (Similar to the `validate()` method in the `validate()` section). +The only difference is, that it validates all subparsers too. +This is done in a recursive way. +Thus, it validates the current parser and subparsers that were selected by the cli-parameters. + +#### The `parse_and_validate()` method: +This method is a combination of the `parse()`- and `validate_recursive()`-methods. +Thus, it first parses the cli-arguments and if the parsing succeeds, it validates the parsed results. + +#### The `operator bool()` method: +For main-parsers, this method always returns `true`, if an executable is set. +If an executable is set, it can be requested by calling `get_executable()`. + +#### The `get_executable()` method: +This method returns the name of the executable that is used in the cli-arguments. +If it is not set, the exception `no_executable_exception_t` is thrown. + +#### The `gen_help_msg()` method: +This method generates the usage message of the parser class. +This output is the same as it would be generated by the `help_option_t`-option. +`gen_help_msg()` requires an additional parameter `rec_depth`: +It controls the depth of the help message. +I.e. the number of encapsulated (sub-)parsers. +A number of 0 does not include any subparsers and would only include the main parser. + +#### The `get_num_processed_arguments()` method: +This method reurns how many arguments were given via the cli-interface. + +#### The `get_active_parser()` method: +This method returns a reference to the selected (sub-)parser. +If no sub-parser was selected by the cli-arguments, this method returns a reference +to this current main-parser instance. If a sub-parser was selected it would return a reference to +this sub-parser. + +### Example code listing for a main parser: +The following code-listing illustrates a very basic example of a main parser with an option and a +string argument: [//]:#begin_cpp_listing_simple_main_parser ```c++ +#include <cassert> #include <clapp/argument.h> #include <clapp/main_parser.h> #include <clapp/option.h> @@ -216,7 +516,7 @@ class cli_parser_t : public clapp::basic_main_parser_t { clapp::string_argument_t string_arg{*this, "string-arg", "String argument"}; - //explicitly delete copy/move-ctors and assignmet operators + //delete copy/move-ctors and assignmet operators (CppCoreGuideline C.21): explicit cli_parser_t(const cli_parser_t &) = delete; explicit cli_parser_t(cli_parser_t &&) noexcept = delete; cli_parser_t &operator=(const cli_parser_t &) = delete; @@ -228,12 +528,332 @@ cli_parser_t::~cli_parser_t() = default; int main(int argc, char *argv[]) { try { cli_parser_t cp{argc, argv}; // parses and validates cli-arguments - - Expects(cp.string_arg); // parser ensures mandatory arguments are given + Ensures(cp.string_arg); // parser ensures mandatory arguments are given std::cout << "string-arg: " << cp.string_arg.value() << std::endl; } catch (std::exception &e) { std::cout << "Caught Exception: " << e.what() << std::endl; + return EXIT_FAILURE; } + return EXIT_SUCCESS; } ``` [//]:#end_cpp_listing_simple_main_parser + +Note: The previous listing explicitly deletes the copy/move assignment operators and the copy/move constructors. +Depending on your code style, this may not be necessary, as `clapp::basic_main_parser_t` already deletes the copy/move assignment operators and the copy/move constructors. +But if you want to be conformant to [CppCoreGuideline C.21](https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#c21-if-you-define-or-delete-any-default-operation-define-or-delete-them-all), you should declare them. + +### Example cli-outputs when using the previous listing: +If the previous example listing is executed, you get the following output: +[//]:#begin_calls_simple_main_parser +```bash +# Print the help message: +$ ./libclapp_doc_simple_main_parser -h # this is the same as with option `--help` +Usage: +./libclapp_doc_simple_main_parser <string-arg> [-h|--help] + + Mandatory Arguments: + string-arg String argument + + Optional Options: + -h|--help Show help options. + +# Give mandatory argument: +$ ./libclapp_doc_simple_main_parser my-string +string-arg: my-string + +# Give no mandatory argument throws: +$ ./libclapp_doc_simple_main_parser +Caught Exception: Mandatory argument 'string-arg' not given. + +``` +[//]:#end_calls_simple_main_parser + +Sub Parser: +----------- +The sub-parser is another key element of the libClaPP CLI parsing library. +Typically, it is a derivied class of the base class `clapp::parser::basic_sub_parser_t`. + +In particular, if you want to have different cli-interface modes, a sub-parser may be what you want: +A sub parser is similar to a main-parser, as it also contains different arguments and options, +but it is typically invoked by the surrounding main parser, if a special keyword is given in the CLI +arguments. +Thus, typically there is no need to invoke a sub-parser directly. + +Typically, a sub parser can be used to switch between different modes of a program, that need different +arguments or options. +A typical example is the git command: By calling `git log` or `git commit` you get different cli options and arguments each. +In the context of libclapp it is required to define a sub parser that handles the `log` options and arguments +and another sub parser that handles the `commit` options and arguments. + +For example, if it is required that a binary supports 3 modi: a default mode and two additional modes: mode1 and mode2. +The default mode is active, if none of the additional modes was activated. Both modes can define other arguments and options: + +Note: if sup-parsers are used, these sub-parsers always take the role of the last argument. +Thus, if at least one sub-parser is registered, no variadic or optional arguments can be used. +Furthermore, if a sub-parser is registered before an argument, the arument position will always be before registered sub-parsers. + +Note: sub-parsers can be stacked. I.e. a sub-parser can itself contain other sub-parsers which iself can contain additional sub-parsers. + +### Sub parser construction: +The sub-parser base class `clapp::parser::basic_sub_parser_t` contains only one constructor: + +```c++ +basic_sub_parser_t(basic_parser_t& parser, const std::string& sub_parser_name, const std::string& description); +``` + +Similar to the option and argument constructors, the first argument is a reference to the surrounding parser (i.e. a main parser or other sub-parsers. +The other two arguments are the name of the sub-parser and a description of the sub-parser. +See the listing below for an example how a sub-parser can be created. + +### Sub parser usage: +The sub-parser base class `clapp::parser::basic_sub_parser_t` is shipped with several useful methods. +Most of them are listed here, but for a complete collection take a look at +[src/include/clapp/sub_parser.h](src/include/clapp/sub_parser.h). +Since the sub-parser base class `clapp::parser::basic_sub_parser_t` is itself derived from +`clapp::parser::basic_parser_t`, you may also look at +[src/include/clapp/parser.h](src/include/clapp/parser.h) +for a complete collection of derived methods. + +```c++ +void validate() const; +void validate_recursive() const; +bool is_active() const; +explicit operator bool() const; +std::string get_sub_parser_name() const; +std::size_t get_num_processed_arguments() const; +const basic_parser_t& get_active_parser() const; +``` + +#### The `validate()` method: +The `validate()` method is similar to `basic_main_parser::validate()` and thus can be used to check the requirements of the parsed result. +Thus, it checks if all mandatory arguments or options were given and ensures that all constraints are met. +Note: this method only validates all arguments or options of the current parser instance. Thus, if further subparsers are used, the `validate()` method ignores all subparsers. If you want to validate this sub-parser and all furhter sub-parsers, use `validate_recursive()`. +If the validation fails, it throws an exception of type `clapp::clapp_exception_t` (or a derived type of this type). + +#### The `validate_recursive()` method: +This method is similar to `basic_main_parser::validate_recursive()` and thus ensures that all mandatory arguments or options were given and ensures that all constraints are met. (Similar to the `validate()` method in the `validate()` section). +The only difference is, that it validates all subparsers too. +This is done in a recursive way. +Thus, it validates the current parser and subparsers that were selected by the cli-parameters. + +#### The `bool is_active()` method: +This method always returns `true`, if the sub-parser is selected by the cli-arguments. +If other sub-parsers are selected (that are not members of this sub-parser), this method returns `false`. + +#### The `operator bool()` method: +For sub-parsers this method always returns `true`, if the sub-parser is selected by the cli-arguments. +(See also method `is_active()`.) + +#### The `get_sub_parser_name()` method: +This method returns the name of the sub-parser. + +#### The `get_num_processed_arguments()` method: +This method reurns how many arguments were given via the cli-interface. + +#### The `get_active_parser()` method: +This method returns a reference to the selected sub-parser (if it is a member of this sub-parser). +If no sub-parser-member was selected by the cli-arguments, this method returns a reference +to this sub-parser instance. If a sub-parser was selected it would return a reference to +this sub-parser. + +### Example code listing for sub-parsers: + +[//]:#begin_cpp_listing_simple_sub_parser +```c++ +#include <cassert> +#include <clapp/argument.h> +#include <clapp/main_parser.h> +#include <clapp/option.h> +#include <clapp/sub_parser.h> + +class cli_parser_t : public clapp::basic_main_parser_t { + public: + cli_parser_t(int argc, const char *const *argv) { + parse_and_validate(argc, argv); + } + + ~cli_parser_t() override; + + clapp::help_option_t help{*this, "help", 'h', "Show help options."}; + + clapp::int32_param_option_t int_opt{*this, 'i', "Int option"}; + + class mode1_parser_t : public clapp::basic_sub_parser_t { + public: + using clapp::basic_sub_parser_t::basic_sub_parser_t; + + ~mode1_parser_t() override; + + clapp::help_option_t help{*this, "help", 'h', "Show help options."}; + + clapp::string_param_option_t string{*this, 's', "String param option."}; + + //delete copy/move-ctors and assignmet operators (CppCoreGuideline C.21): + explicit mode1_parser_t(const mode1_parser_t &) = delete; + explicit mode1_parser_t(mode1_parser_t &&) noexcept = delete; + mode1_parser_t &operator=(const mode1_parser_t &) = delete; + mode1_parser_t &operator=(mode1_parser_t &&) noexcept = delete; + }; + + class mode2_parser_t : public clapp::basic_sub_parser_t { + public: + using clapp::basic_sub_parser_t::basic_sub_parser_t; + + ~mode2_parser_t() override; + + clapp::help_option_t help{*this, "help", 'h', "Show help options."}; + + clapp::string_argument_t string_arg{ + *this, "string-arg", "String argument"}; + + //delete copy/move-ctors and assignmet operators (CppCoreGuideline C.21): + explicit mode2_parser_t(const mode2_parser_t &) = delete; + explicit mode2_parser_t(mode2_parser_t &&) noexcept = delete; + mode2_parser_t &operator=(const mode2_parser_t &) = delete; + mode2_parser_t &operator=(mode2_parser_t &&) noexcept = delete; + }; + + mode1_parser_t mode1{*this, "mode1", "mode1 sub-parser."}; + mode2_parser_t mode2{*this, "mode2", "mode2 sub-parser."}; + + //delete copy/move-ctors and assignmet operators (CppCoreGuideline C.21): + explicit cli_parser_t(const cli_parser_t &) = delete; + explicit cli_parser_t(cli_parser_t &&) noexcept = delete; + cli_parser_t &operator=(const cli_parser_t &) = delete; + cli_parser_t &operator=(cli_parser_t &&) noexcept = delete; +}; + +cli_parser_t::~cli_parser_t() = default; +cli_parser_t::mode1_parser_t::~mode1_parser_t() = default; +cli_parser_t::mode2_parser_t::~mode2_parser_t() = default; + +int main(int argc, char *argv[]) { + try { + cli_parser_t cp{argc, argv}; // parses and validates cli-arguments + + if(cp.mode1) { + std::cout << "mode1: "; + if(cp.mode1.string) { + std::cout << "string: '" << cp.mode1.string.value() << "' "; + } + if(cp.int_opt){ + std::cout << "int-opt: '" << cp.int_opt.value() << "' "; + } + std::cout << std::endl; + } else if(cp.mode2) { + std::cout << "mode2: "; + Ensures(cp.mode2.string_arg); // parser ensures mandatory arguments are given + std::cout << "string_arg: '" << cp.mode2.string_arg.value() << "'" << std::endl; + } else { + std::cout << "default mode: "; + if(cp.int_opt){ + std::cout << "int-opt: '" << cp.int_opt.value() << "'"; + } + std::cout << std::endl; + } + } catch (std::exception &e) { + std::cout << "Caught Exception: " << e.what() << std::endl; + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} +``` +[//]:#end_cpp_listing_simple_sub_parser +The mode1-subparser is configured to inherit all options and arguments from the main-parser, +while the mode2-subparser doesn't inherit anything from the main-parser. +This behavior can be controlled by the fourth (optional) bool argument that is given to the +subparser-constructors: Giving `true` or no argument enables this feature. +While giving `false` disables the interitation from the parent parser. + +### Example cli-outputs when using the sub-parser listing: +[//]:#begin_calls_simple_sub_parser +```bash +# Print the overall help message: +$ ./libclapp_doc_simple_sub_parser --help +Usage: +./libclapp_doc_simple_sub_parser [-h|--help] [-i=<arg>] mode1 [-h|--help] [-s=<arg>] +./libclapp_doc_simple_sub_parser [-h|--help] [-i=<arg>] mode2 <string-arg> [-h|--help] +./libclapp_doc_simple_sub_parser [-h|--help] [-i=<arg>] + + Subparser: + mode1 mode1 sub-parser. + Optional Options: + -h|--help Show help options. + -s=<arg> String param option. + + + mode2 mode2 sub-parser. + Mandatory Arguments: + string-arg String argument + + Optional Options: + -h|--help Show help options. + + + Optional Options: + -h|--help Show help options. + -i=<arg> Int option + +# Print the help message of mode1: +$ ./libclapp_doc_simple_sub_parser mode1 -h +Usage: +./libclapp_doc_simple_sub_parser [-h|--help] [-i=<arg>] mode1 [-h|--help] [-s=<arg>] + + Optional Options: + -h|--help Show help options. + -s=<arg> String param option. + +# Print the help message of mode2: +$ ./libclapp_doc_simple_sub_parser mode2 --help +Usage: +./libclapp_doc_simple_sub_parser [-h|--help] [-i=<arg>] mode2 <string-arg> [-h|--help] + + Mandatory Arguments: + string-arg String argument + + Optional Options: + -h|--help Show help options. + +# Give no sub-parser: +$ ./libclapp_doc_simple_sub_parser +default mode: + +# Give no sub-parser, but optional int-option `-i`: +$ ./libclapp_doc_simple_sub_parser -i 12 #decimal +default mode: int-opt: '12' + +# Give invalid sub-parser: +$ ./libclapp_doc_simple_sub_parser mode +Caught Exception: Unknown argument/sub-parser 'mode'. + +# Give sub-parser `mode1`: +$ ./libclapp_doc_simple_sub_parser mode1 +mode1: + +# Give sub-parser `mode1` and optional int-option `-i`: +$ ./libclapp_doc_simple_sub_parser -i 011 mode1 #octal +mode1: int-opt: '9' + +# Give sub-parser `mode1` and optional string-option `-s`: +$ ./libclapp_doc_simple_sub_parser mode1 -s str +mode1: string: 'str' + +# Give sub-parser `mode1` and optional string-option `-s` and int-option `-i`: +$ ./libclapp_doc_simple_sub_parser -i=0x10 mode1 -s=str #hex +mode1: string: 'str' int-opt: '16' + +# Give sub-parser `mode2` without mandatory string-argument `string-arg`: +$ ./libclapp_doc_simple_sub_parser mode2 +Caught Exception: Mandatory argument 'string-arg' not given. + +# Give sub-parser `mode2` with mandatory string-argument `string-arg`: +$ ./libclapp_doc_simple_sub_parser mode2 argument +mode2: string_arg: 'argument' + +# Give sub-parser `mode2` with mandatory string-argument `string-arg` and invalid option `-i`: +$ ./libclapp_doc_simple_sub_parser mode2 argument -i 1 +Caught Exception: Invalid (sub-)parser option '-i' + +``` +[//]:#end_calls_simple_sub_parser diff --git a/doc/sample_executor.p6 b/doc/sample_executor.p6 new file mode 100755 index 00000000..f53fa0f9 --- /dev/null +++ b/doc/sample_executor.p6 @@ -0,0 +1,39 @@ +#!/usr/bin/perl6 +use v6; +use Test; + +sub exec(Str $comment, Str $cmd, Str $expected_output) { + my $output = qqx{$cmd}; + is($output, $expected_output.substr(0, *-1), "testing '" ~ $comment ~ "': '" ~ $cmd ~ "'"); +} + +sub MAIN($out_file) { + my Str $output; + my Str $cmd; + my Str $comment; + + say "Executing '" ~ $out_file ~ "'"; + my $contents = $out_file.IO.slurp; + + for $contents.lines -> $line { + if $line ~~ /^^\#' '$<comment>=[.+]$$/ { + if $output and $cmd and $comment { + exec($comment, $cmd, $output); + $output = Nil; + $cmd = Nil; + $comment = Nil; + } + $comment = $<comment>.Str; + } elsif $line ~~ /^^\$' '$<cmd>=[.+]$$/ { + $cmd = $<cmd>.Str; + } else { + $output ~= $line ~ "\n"; + } + } + + if $output and $cmd and $comment { + exec($comment, $cmd, $output); + } + + done-testing; +} diff --git a/doc/sample_extractor.p6 b/doc/sample_extractor.p6 index dd71a33c..a9db0398 100755 --- a/doc/sample_extractor.p6 +++ b/doc/sample_extractor.p6 @@ -1,28 +1,53 @@ #!/usr/bin/perl6 +use v6; sub MAIN($doc_dir) { - chdir $doc_dir; for dir(test => /^ .* \. cpp$/) -> $file { say "Removing " ~ $file; unlink($file); } + + for dir(test => /^ .* \. out$/) -> $file { + say "Removing " ~ $file; + unlink($file); + } my Str $listing_name; - - for 'doc.md'.IO.lines -> $line { + my Str $calls_name; + + my Str $file = $doc_dir ~ '/doc.md'; + say "Extracting from '" ~ $file ~ "'."; + + for $file.IO.lines -> $line { if $listing_name and $line ~~ /^^\[\/\/\]\:\#end_cpp_listing_$<name>=[\w+]$$/ { $listing_name eq $<name>.Str or die "Unexpected listing name. Expected: '" ~ $listing_name ~ "' received: '" ~ $<name>.Str ~ "'."; + say "Created " ~ $listing_name ~ ".cpp"; $listing_name=Nil; } + if $calls_name and $line ~~ /^^\[\/\/\]\:\#end_calls_$<name>=[\w+]$$/ { + $calls_name eq $<name>.Str + or die "Unexpected calls name. Expected: '" + ~ $calls_name ~ "' received: '" ~ $<name>.Str ~ "'."; + say "Created " ~ $calls_name ~ ".out"; + $calls_name=Nil; + } if $listing_name { unless $line ~~ /^^\`\`\`/ { spurt $listing_name ~ ".cpp", $line ~ "\n", :append; } } + if $calls_name { + unless $line ~~ /^^\`\`\`/ { + spurt $calls_name ~ ".out", $line ~ "\n", :append; + } + } if !$listing_name and $line ~~ /^^\[\/\/\]\:\#begin_cpp_listing_$<name>=[\w+]$$/ { $listing_name = $<name>.Str; } + if !$calls_name and $line ~~ /^^\[\/\/\]\:\#begin_calls_$<name>=[\w+]$$/ { + $calls_name = $<name>.Str; + } } } diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 3c8ceabb..35238ec0 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,25 +1,35 @@ +if(CLANG_TIDY) + set(CLANG_TIDY_EX_ARGS "${CLANG_TIDY_ARGS},-misc-non-private-member-variables-in-classes,-cppcoreguidelines-avoid-magic-numbers,-readability-magic-numbers") +endif() + if(libClaPP_FS_LINKER_FLAG) - add_executable(libclapp_example_full full_example.cpp) - target_link_libraries(libclapp_example_full clapp ${libClaPP_FS_LINKER_FLAG}) - target_compile_options(libclapp_example_full PRIVATE ${libClaPP_COMPILE_OPTIONS}) + add_executable(libclapp_example_large large_example.cpp) + target_link_libraries(libclapp_example_large clapp ${libClaPP_FS_LINKER_FLAG}) + target_compile_options(libclapp_example_large PRIVATE ${libClaPP_COMPILE_OPTIONS}) if(CLANG_TIDY) - set_target_properties(libclapp_example_full PROPERTIES CXX_CLANG_TIDY "${CLANG_TIDY_ARGS}") + set_target_properties(libclapp_example_large PROPERTIES CXX_CLANG_TIDY "${CLANG_TIDY_EX_ARGS}") endif() - install(TARGETS libclapp_example_full DESTINATION ${CMAKE_INSTALL_BINDIR}) + install(TARGETS libclapp_example_large DESTINATION ${CMAKE_INSTALL_BINDIR}) + configure_file(test_large_example.sh test_large_example.sh COPYONLY) + add_test(NAME large_example_tests COMMAND test_large_example.sh WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) endif() add_executable(libclapp_example_short short_example.cpp) target_link_libraries(libclapp_example_short clapp) target_compile_options(libclapp_example_short PRIVATE ${libClaPP_COMPILE_OPTIONS}) if(CLANG_TIDY) - set_target_properties(libclapp_example_short PROPERTIES CXX_CLANG_TIDY "${CLANG_TIDY_ARGS}") + set_target_properties(libclapp_example_short PROPERTIES CXX_CLANG_TIDY "${CLANG_TIDY_EX_ARGS}") endif() install(TARGETS libclapp_example_short DESTINATION ${CMAKE_INSTALL_BINDIR}) +configure_file(test_short_example.sh test_short_example.sh COPYONLY) +add_test(NAME short_example_tests COMMAND test_short_example.sh WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) add_executable(libclapp_example_sub_parser sub_parser_example.cpp) target_link_libraries(libclapp_example_sub_parser clapp) target_compile_options(libclapp_example_sub_parser PRIVATE ${libClaPP_COMPILE_OPTIONS}) if(CLANG_TIDY) - set_target_properties(libclapp_example_sub_parser PROPERTIES CXX_CLANG_TIDY "${CLANG_TIDY_ARGS}") + set_target_properties(libclapp_example_sub_parser PROPERTIES CXX_CLANG_TIDY "${CLANG_TIDY_EX_ARGS}") endif() install(TARGETS libclapp_example_sub_parser DESTINATION ${CMAKE_INSTALL_BINDIR}) +configure_file(test_sub_parser_example.sh test_sub_parser_example.sh COPYONLY) +add_test(NAME sub_parser_example_tests COMMAND test_sub_parser_example.sh WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) diff --git a/examples/large_example.cpp b/examples/large_example.cpp new file mode 100644 index 00000000..f94e4111 --- /dev/null +++ b/examples/large_example.cpp @@ -0,0 +1,396 @@ +#include <clapp/argument.h> +#include <clapp/build_info.h> +#include <clapp/main_parser.h> +#include <clapp/option.h> +#include <clapp/sub_parser.h> +#include <clapp/value.h> +#include <iostream> + +using string_argument_t = clapp::basic_argument_t<std::string>; +using variadic_string_argument_t = + clapp::basic_variadic_argument_t<std::string>; +using int_argument_t = clapp::basic_argument_t<std::int32_t>; +using variadic_int_argument_t = clapp::basic_variadic_argument_t<std::int32_t>; + +enum class entry_t { entry1, entry2 }; + +class entry_value_constraint_t { + public: + [[nodiscard]] static std::string append_restriction_text() { + return "possible values: entry1, entry2"; + } +}; + +using string_param_t = clapp::basic_param_option_t<std::string>; + +using string_vector_param_t = clapp::basic_vector_param_option_t<std::string>; + +using int32_param_t = clapp::basic_param_option_t<std::int32_t>; + +using entry_param_t = clapp::basic_param_option_t<entry_t>; + +using entry_argument_t = clapp::basic_argument_t<entry_t>; + +struct no_action_t {}; + +struct restriction_t { + [[nodiscard]] static std::string append_restriction_text() { + return "restriction"; + } +}; + +class cli_parser_t : public clapp::basic_main_parser_t { + public: + cli_parser_t() = default; + + cli_parser_t(int argc, const char *const *argv) : cli_parser_t{} { + parse(argc, argv); + } + + explicit cli_parser_t(const cli_parser_t &) = delete; + explicit cli_parser_t(cli_parser_t &&) noexcept = delete; + cli_parser_t &operator=(const cli_parser_t &) = delete; + cli_parser_t &operator=(cli_parser_t &&) noexcept = delete; + + ~cli_parser_t() override; + + class cmd1_parser_t : public clapp::basic_sub_parser_t { + public: + using clapp::basic_sub_parser_t::basic_sub_parser_t; + + explicit cmd1_parser_t(const cmd1_parser_t &) = delete; + explicit cmd1_parser_t(cmd1_parser_t &&) noexcept = delete; + cmd1_parser_t &operator=(const cmd1_parser_t &) = delete; + cmd1_parser_t &operator=(cmd1_parser_t &&) noexcept = delete; + + ~cmd1_parser_t() override; + clapp::bool_option_t help{*this, "help", 'h', "Show help options."}; + clapp::bool_option_t short_bool{*this, 'b', "Short bool option.", + purpose_t::mandatory}; + string_param_t string{*this, 's', "String param option."}; + + entry_argument_t entry_arg{*this, "entry-arg", "Entry argument", + entry_value_constraint_t{}}; + + int_argument_t int_arg{*this, "int-arg", "Int argument", + clapp::min_max_value_t<std::int32_t>{5, 10}}; + string_argument_t string_arg_x{ + *this, "string-arg-x", "String argument x", purpose_t::optional, + clapp::default_value_t<std::string>{"abaa"}}; + }; + + class cmd2_parser_t : public clapp::basic_sub_parser_t { + public: + using clapp::basic_sub_parser_t::basic_sub_parser_t; + + explicit cmd2_parser_t(const cmd2_parser_t &) = delete; + explicit cmd2_parser_t(cmd2_parser_t &&) noexcept = delete; + cmd2_parser_t &operator=(const cmd2_parser_t &) = delete; + cmd2_parser_t &operator=(cmd2_parser_t &&) noexcept = delete; + + ~cmd2_parser_t() override; + clapp::bool_option_t help{*this, "help", 'h', "Show help options."}; + string_argument_t string_arg_x{ + *this, "string-arg-x", "String argument x", purpose_t::optional, + clapp::default_value_t<std::string>{"default-string-arg-x"}}; + variadic_int_argument_t int_arg{ + *this, "variadic-int-arg", "Int argument", purpose_t::optional, + clapp::min_max_value_t<std::int32_t>{5, 10}}; + // string_argument_t string_arg_y{*this, "string-arg-y", "String + // argument x", purpose_t::optional};//TODO: ensure that this throws + }; + + cmd1_parser_t cmd1{*this, "cmd1", "First usable command."}; + cmd2_parser_t cmd2{*this, "cmd2", "Second usable command."}; + + string_argument_t string_arg{*this, "string-arg", "String argument"}; + // string_argument_t string_arg_2{*this, "string-arg-2", "String argument + // 2", purpose_t::optional};//TODO: ensure that this throws... + + clapp::help_option_t help{*this, "help", 'h', "Show help options."}; + clapp::bool_option_t short_bool{*this, 'b', "Short bool option."}; + clapp::bool_option_t long_bool{*this, "long-bool", "Long bool option."}; + clapp::bool_option_t restricted_bool{ + *this, "restricted", 'r', "restricted bool option.", + restriction_t{}, no_action_t{}}; + clapp::bool_option_t mandatory_bool{ + *this, 'o', "Mandatory short bool option.", purpose_t::mandatory}; + clapp::bool_option_t mandatory_restricted_bool{ + *this, + "mandatory-restricted", + 'm', + "Mandatory restricted bool option.", + purpose_t::mandatory, + restriction_t{}}; + + clapp::hours_param_option_t hours{ + *this, "hours", "hours option.", + clapp::default_value_t<std::chrono::hours>{std::chrono::hours{100}}}; + clapp::min_param_option_t minutes{ + *this, "minutes", "minutes option.", + clapp::min_max_value_t<std::chrono::minutes>{std::chrono::minutes{0}, + std::chrono::minutes{7}}}; + clapp::sec_param_option_t seconds{*this, "seconds", "seconds option."}; + clapp::ms_param_option_t milliseconds{*this, "milliseconds", + "milliseconds option."}; + clapp::us_param_option_t microseconds{*this, "microseconds", + "microseconds option."}; + clapp::ns_param_option_t nanoseconds{*this, "nanoseconds", + "nanoseconds option."}; + + clapp::count_option_t verbose{*this, + "verbose", + 'v', + "Verbose option.", + clapp::min_max_value_t<std::size_t>{0, 7}, + clapp::default_value_t<std::size_t>{2}}; + clapp::count_option_t count{*this, 'c', "Count option ", + purpose_t::mandatory}; + + string_param_t string_param{*this, "string", 's', "String option 1."}; + + string_vector_param_t string_vector_param{*this, "string-vector", + "String vector param."}; + +#ifdef CLAPP_FS_AVAIL + clapp::path_param_option_t test_file{*this, "test-file", "Test File.", + clapp::path_exists_t {}}; +#endif + + entry_param_t entry_param{*this, + "entry", + 'e', + "Entry option 1. ", + entry_value_constraint_t{}, + purpose_t::mandatory}; + + int32_param_t mandatory_int{*this, "mandatory-int", "Mandatory Int option.", + purpose_t::mandatory}; + int32_param_t default_int{*this, "default-int", "Default Int option.", + clapp::default_value_t<std::int32_t>{10}}; + int32_param_t optional_int{*this, "optional-int", "Optional Int option."}; + + int32_param_t constrained_int{ + *this, "constrained-int", + 'f', "Constrained default Int option.", + 15, clapp::min_max_value_t<std::int32_t>{10, 20}}; +}; + +std::ostream &operator<<(std::ostream &out, const entry_t &e); + +template <> +entry_t clapp::value::convert_value<entry_t>(std::string_view param); + +void process_cmd1(const cli_parser_t::cmd1_parser_t &cmd1); + +void process_cmd2(const cli_parser_t::cmd2_parser_t &cmd2); + +cli_parser_t::~cli_parser_t() = default; +cli_parser_t::cmd1_parser_t::~cmd1_parser_t() = default; +cli_parser_t::cmd2_parser_t::~cmd2_parser_t() = default; + +std::ostream &operator<<(std::ostream &out, const entry_t &e) { + if (e == entry_t::entry1) { + out << "entry1"; + } else if (e == entry_t::entry2) { + out << "entry2"; + } + return out; +} + +template <> +entry_t clapp::value::convert_value<entry_t>(const std::string_view param) { + if (param == "entry1") { + return entry_t::entry1; + } + if (param == "entry2") { + return entry_t::entry2; + } + throw std::runtime_error("Invalid enum param type."); +} + +void process_cmd1(const cli_parser_t::cmd1_parser_t &cmd1) { + if (cmd1.help.value()) { + std::cout << "Usage: \n" + << cmd1.get_sub_parser_name() << ' ' << cmd1.gen_help_msg(0); + return; + } + + cmd1.validate(); + + if (cmd1.string) { + std::cout << "string: " << cmd1.string.value() << "\n"; + } else { + std::cout << "string: not given\n"; + } + + if (cmd1.int_arg) { + std::cout << "int-arg: " << cmd1.int_arg.value() << "\n"; + } else { + std::cout << "int-arg: not given\n"; + } + + if (cmd1.string_arg_x) { + std::cout << "string-arg-x: " << cmd1.string_arg_x.value() << "\n"; + } else { + std::cout << "string-arg-x: not given\n"; + } + + if (cmd1.entry_arg) { + std::cout << "entry-arg: " << cmd1.entry_arg.value() << "\n"; + } else { + std::cout << "entry-arg: not given\n"; + } +} + +void process_cmd2(const cli_parser_t::cmd2_parser_t &cmd2) { + if (cmd2.help.value()) { + std::cout << "Usage: \n" + << cmd2.get_sub_parser_name() << ' ' << cmd2.gen_help_msg(0); + return; + } + + cmd2.validate(); + + if (cmd2.string_arg_x) { + std::cout << "string-arg-x: " << cmd2.string_arg_x.value() << "\n"; + } else { + std::cout << "string-arg-x: not given\n"; + } + + if (cmd2.int_arg) { + std::cout << "int_arg (size: " << cmd2.int_arg.value().size() << "): "; + for (auto &val : cmd2.int_arg.value()) { + std::cout << val << ", "; + } + std::cout << "\n"; + } else { + std::cout << "int_arg: not given\n"; + } +} + +int main(int argc, char *argv[]) { + try { + std::cout << clapp::build_info::build_info_string << std::endl; + // create parser and parse the args + // cli_parser_t cp; + // cp.parse(argc, argv); + // or do it all in one. + cli_parser_t cp{argc, argv}; + + if (!cp) { + std::cout << "Parsing failed!" << std::endl; + return EXIT_FAILURE; + } + + if (cp.help.value()) { + std::cout << "Usage: \n" + << cp.get_executable() << ' ' << cp.gen_help_msg(0); + return EXIT_SUCCESS; + } + + cp.validate(); + + if (cp.string_arg) { + std::cout << "string-arg: " << cp.string_arg.value() << "\n"; + } else { + std::cout << "string-arg: not given\n"; + } + + if (cp.verbose.given()) { + std::cout << "verbose: " << cp.verbose.value() << "\n"; + } else { + std::cout << "verbose: not given\n"; + } + + if (cp.string_param) { + std::cout << "string_param: '" << cp.string_param.value() << "'\n"; + } else { + std::cout << "string_param: not given\n"; + } + + if (cp.string_vector_param) { + std::cout << "string_vector_param (size: " + << cp.string_vector_param.value().size() << "): "; + for (auto &val : cp.string_vector_param.value()) { + std::cout << val << ", "; + } + std::cout << "\n"; + + } else { + std::cout << "string_vector_param: not given\n"; + } + + if (cp.short_bool.given()) { + std::cout << "short_bool: given\n"; + } else { + std::cout << "short_bool: not given\n"; + } + + if (cp.restricted_bool.given()) { + std::cout << "restricted_bool: given\n"; + } else { + std::cout << "restricted_bool: not given\n"; + } + + if (cp.long_bool.given()) { + std::cout << "long_bool: " << cp.long_bool.value() << "\n"; + } else { + std::cout << "long_bool: not given\n"; + } + + if (cp.count.given()) { + std::cout << "count: " << cp.count.value() << "\n"; + } else { + std::cout << "count: not given\n"; + } + +#ifdef CLAPP_FS_AVAIL + if (cp.test_file) { + std::cout << "test-file: " << cp.test_file.value() << "\n"; + } else { + std::cout << "test-file: not given\n"; + } +#else + std::cout << "without fs\n"; +#endif + + if (cp.constrained_int) { + std::cout << "constrained_int: " << cp.constrained_int.value() + << "\n"; + } else { + std::cout << "constrained_int: not given\n"; + } + std::cout << "mandatory_bool: " << cp.mandatory_bool.value() << "\n"; + + std::cout << "mandatory_int: " << cp.mandatory_int.value() << "\n"; + std::cout << "default_int: " << cp.default_int.value() << "\n"; + + if (cp.optional_int) { + std::cout << "optional_int: " << cp.optional_int.value() << "\n"; + } + + std::cout << "entry_param: '" << cp.entry_param.value() << "'\n"; + + if (cp.cmd1) { + std::cout << "cmd1 given" << std::endl; + process_cmd1(cp.cmd1); + } else { + std::cout << "cmd1 not given" << std::endl; + } + + if (cp.cmd2) { + std::cout << "cmd2 given" << std::endl; + process_cmd2(cp.cmd2); + } else { + std::cout << "cmd2 not given" << std::endl; + } + } catch (clapp::exception::clapp_exception_t &e) { + std::cout << "Caught ClaPP-Exception: " << e.what() << std::endl; + return EXIT_FAILURE; + } catch (std::exception &e) { + std::cout << "Caught Exception: " << e.what() << std::endl; + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} diff --git a/examples/short_example.cpp b/examples/short_example.cpp index 2b18a70d..df119f38 100644 --- a/examples/short_example.cpp +++ b/examples/short_example.cpp @@ -7,8 +7,7 @@ [[noreturn]] void print_version_and_exit(); [[noreturn]] void print_version_and_exit() { - std::cout << clapp::build_info::project_name << " " - << clapp::build_info::version << "" << std::endl; + std::cout << clapp::build_info::build_info_string << std::endl; exit(EXIT_SUCCESS); } @@ -101,7 +100,10 @@ int main(int argc, char *argv[]) { } } catch (clapp::exception::clapp_exception_t &e) { std::cout << "Caught ClaPP-Exception: " << e.what() << std::endl; + return EXIT_FAILURE; } catch (std::exception &e) { std::cout << "Caught Exception: " << e.what() << std::endl; + return EXIT_FAILURE; } + return EXIT_SUCCESS; } diff --git a/examples/sub_parser_example.cpp b/examples/sub_parser_example.cpp index a4d59e90..5876ca17 100644 --- a/examples/sub_parser_example.cpp +++ b/examples/sub_parser_example.cpp @@ -8,8 +8,7 @@ [[noreturn]] void print_version_and_exit(); [[noreturn]] void print_version_and_exit() { - std::cout << clapp::build_info::project_name << " " - << clapp::build_info::version << "" << std::endl; + std::cout << clapp::build_info::build_info_string << std::endl; exit(EXIT_SUCCESS); } @@ -56,7 +55,6 @@ class cli_parser_t : public clapp::basic_main_parser_t { ~first_parser_t() override; - // if a help message for subparser is required, define it... clapp::help_option_t help{*this, "help", 'h', "Show help options."}; clapp::bool_option_t short_bool{*this, 'b', "Short bool option.", @@ -85,7 +83,6 @@ class cli_parser_t : public clapp::basic_main_parser_t { ~second_parser_t() override; - // if a help message for subparser is required, define it... clapp::help_option_t help{*this, "help", 'h', "Show help options."}; clapp::int32_argument_t int_arg{ @@ -186,7 +183,10 @@ int main(int argc, char *argv[]) { } } catch (clapp::exception::clapp_exception_t &e) { std::cout << "Caught ClaPP-Exception: " << e.what() << std::endl; + return EXIT_FAILURE; } catch (std::exception &e) { std::cout << "Caught Exception: " << e.what() << std::endl; + return EXIT_FAILURE; } + return EXIT_SUCCESS; } diff --git a/examples/test_large_example.sh b/examples/test_large_example.sh new file mode 100755 index 00000000..eecb5e7b --- /dev/null +++ b/examples/test_large_example.sh @@ -0,0 +1,122 @@ +#!/usr/bin/env bats + +FIRST_LINE_REGEX='^libClaPP\ v.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3} [a-zA-Z]+\-Build: ' +CLAPP_EXCEPTION_REGEX='^Caught\ ClaPP-Exception:.*$' + +@test "large-example: no arguments/options given throws" { + run ./libclapp_example_large + [ "$status" -eq 1 ] + [[ "${lines[0]}" =~ $FIRST_LINE_REGEX ]] + [[ "${lines[1]}" =~ $CLAPP_EXCEPTION_REGEX ]] +} + +@test "large-example: show help with long option --help" { + run ./libclapp_example_large --help + [ "$status" -eq 0 ] + [[ "${lines[0]}" =~ $FIRST_LINE_REGEX ]] + [ "${lines[1]}" = "Usage:" ] +} + +@test "large-example: show help with short option -h" { + run ./libclapp_example_large -h + [ "$status" -eq 0 ] + [[ "${lines[0]}" =~ $FIRST_LINE_REGEX ]] + [ "${lines[1]}" = "Usage:" ] +} + +@test "large-example: give all mandatory options/arguments" { + run ./libclapp_example_large str -o -m -c -e entry1 --mandatory-int 1 + [ "$status" -eq 0 ] + [[ "${lines[0]}" =~ $FIRST_LINE_REGEX ]] + [ "${lines[1]}" = "string-arg: str" ] + [ "${lines[2]}" = "verbose: not given" ] + [ "${lines[3]}" = "string_param: not given" ] + [ "${lines[4]}" = "string_vector_param: not given" ] + [ "${lines[5]}" = "short_bool: not given" ] + [ "${lines[6]}" = "restricted_bool: not given" ] + [ "${lines[7]}" = "long_bool: not given" ] + [ "${lines[8]}" = "count: 1" ] + [ "${lines[9]}" = "test-file: not given" ] + [ "${lines[10]}" = "constrained_int: not given" ] + [ "${lines[11]}" = "mandatory_bool: 1" ] + [ "${lines[12]}" = "mandatory_int: 1" ] + [ "${lines[13]}" = "default_int: 10" ] + [ "${lines[14]}" = "entry_param: 'entry1'" ] + [ "${lines[15]}" = "cmd1 not given" ] + [ "${lines[16]}" = "cmd2 not given" ] +} + +@test "large-example: give mandatory and optional options/arguments" { + run ./libclapp_example_large str -o -m -c -e entry2 --mandatory-int 3 -b --long-bool -r --hours=10 --minutes 6 --seconds 8 --milliseconds 10 --microseconds 123 --nanoseconds 100 -v --verbose -s string --string-vector vec1 --string-vector=vec2 --test-file=/tmp --default-int=12 --optional-int 1 -f 15 + [ "$status" -eq 0 ] + [[ "${lines[0]}" =~ $FIRST_LINE_REGEX ]] + [ "${lines[1]}" = "string-arg: str" ] + [ "${lines[2]}" = "verbose: 4" ] + [ "${lines[3]}" = "string_param: 'string'" ] + [ "${lines[4]}" = "string_vector_param (size: 2): vec1, vec2, " ] + [ "${lines[5]}" = "short_bool: given" ] + [ "${lines[6]}" = "restricted_bool: given" ] + [ "${lines[7]}" = "long_bool: 1" ] + [ "${lines[8]}" = "count: 1" ] + [ "${lines[9]}" = "test-file: \"/tmp\"" ] + [ "${lines[10]}" = "constrained_int: 15" ] + [ "${lines[11]}" = "mandatory_bool: 1" ] + [ "${lines[12]}" = "mandatory_int: 3" ] + [ "${lines[13]}" = "default_int: 12" ] + [ "${lines[14]}" = "optional_int: 1" ] + [ "${lines[15]}" = "entry_param: 'entry2'" ] + [ "${lines[16]}" = "cmd1 not given" ] + [ "${lines[17]}" = "cmd2 not given" ] +} + +@test "large-example: give mandatory and optional options/arguments as well as cmd1" { + run ./libclapp_example_large str -o -m -c -e entry2 --mandatory-int 3 -b --long-bool -r --hours=10 --minutes 6 --seconds 8 --milliseconds 10 --microseconds 123 --nanoseconds 100 --verbose -s string --string-vector vec1 --string-vector=vec2 --test-file=/tmp --default-int=12 --optional-int 1 -f 15 cmd1 -s 1 -b entry1 7 + [ "$status" -eq 0 ] + [[ "${lines[0]}" =~ $FIRST_LINE_REGEX ]] + [ "${lines[1]}" = "string-arg: str" ] + [ "${lines[2]}" = "verbose: 3" ] + [ "${lines[3]}" = "string_param: 'string'" ] + [ "${lines[4]}" = "string_vector_param (size: 2): vec1, vec2, " ] + [ "${lines[5]}" = "short_bool: given" ] + [ "${lines[6]}" = "restricted_bool: given" ] + [ "${lines[7]}" = "long_bool: 1" ] + [ "${lines[8]}" = "count: 1" ] + [ "${lines[9]}" = "test-file: \"/tmp\"" ] + [ "${lines[10]}" = "constrained_int: 15" ] + [ "${lines[11]}" = "mandatory_bool: 1" ] + [ "${lines[12]}" = "mandatory_int: 3" ] + [ "${lines[13]}" = "default_int: 12" ] + [ "${lines[14]}" = "optional_int: 1" ] + [ "${lines[15]}" = "entry_param: 'entry2'" ] + [ "${lines[16]}" = "cmd1 given" ] + [ "${lines[17]}" = "string: 1" ] + [ "${lines[18]}" = "int-arg: 7" ] + [ "${lines[19]}" = "string-arg-x: abaa" ] + [ "${lines[20]}" = "entry-arg: entry1" ] + [ "${lines[21]}" = "cmd2 not given" ] +} + +@test "large-example: give mandatory and optional options/arguments as well as cmd2" { + run ./libclapp_example_large str -o -m -c -e entry2 --mandatory-int 3 -b --long-bool -r --hours=10 --minutes 6 --seconds 8 --milliseconds 10 --microseconds 123 --nanoseconds 100 -v --verbose -s string --string-vector vec1 --string-vector=vec2 --test-file=/tmp --default-int=12 --optional-int 1 -f 15 cmd2 string1 5 6 7 + [ "$status" -eq 0 ] + [[ "${lines[0]}" =~ $FIRST_LINE_REGEX ]] + [ "${lines[1]}" = "string-arg: str" ] + [ "${lines[2]}" = "verbose: 4" ] + [ "${lines[3]}" = "string_param: 'string'" ] + [ "${lines[4]}" = "string_vector_param (size: 2): vec1, vec2, " ] + [ "${lines[5]}" = "short_bool: given" ] + [ "${lines[6]}" = "restricted_bool: given" ] + [ "${lines[7]}" = "long_bool: 1" ] + [ "${lines[8]}" = "count: 1" ] + [ "${lines[9]}" = "test-file: \"/tmp\"" ] + [ "${lines[10]}" = "constrained_int: 15" ] + [ "${lines[11]}" = "mandatory_bool: 1" ] + [ "${lines[12]}" = "mandatory_int: 3" ] + [ "${lines[13]}" = "default_int: 12" ] + [ "${lines[14]}" = "optional_int: 1" ] + [ "${lines[15]}" = "entry_param: 'entry2'" ] + [ "${lines[16]}" = "cmd1 not given" ] + [ "${lines[17]}" = "cmd2 given" ] + [ "${lines[18]}" = "string-arg-x: string1" ] + [ "${lines[19]}" = "int_arg (size: 3): 5, 6, 7, " ] +} diff --git a/examples/test_short_example.sh b/examples/test_short_example.sh new file mode 100755 index 00000000..4a74b937 --- /dev/null +++ b/examples/test_short_example.sh @@ -0,0 +1,110 @@ +#!/usr/bin/env bats + +VERSION_REGEX='^libClaPP\ v.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3} [a-zA-Z]+\-Build: ' +CLAPP_EXCEPTION_REGEX='^Caught\ ClaPP-Exception:.*$' + +@test "short-example: no arguments/options given throws" { + run ./libclapp_example_short + [ "$status" -eq 1 ] + [[ "${lines[0]}" =~ $CLAPP_EXCEPTION_REGEX ]] +} + +@test "short-example: show help with long option --help" { + run ./libclapp_example_short --help + [ "$status" -eq 0 ] + [ "${lines[0]}" = "Usage:" ] +} + +@test "short-example: show help with short option -h" { + run ./libclapp_example_short -h + [ "$status" -eq 0 ] + [ "${lines[0]}" = "Usage:" ] +} + +@test "short-example: show help with short option -?" { + run ./libclapp_example_short -? + [ "$status" -eq 0 ] + [ "${lines[0]}" = "Usage:" ] +} + +@test "short-example: show version with long option --version" { + run ./libclapp_example_short --version + [ "$status" -eq 0 ] + [[ "${lines[0]}" =~ $VERSION_REGEX ]] +} + +@test "short-example: show version with long option --vers" { + run ./libclapp_example_short --vers + [ "$status" -eq 0 ] + [[ "${lines[0]}" =~ $VERSION_REGEX ]] +} + +@test "short-example: show version with short option -v" { + run ./libclapp_example_short -v + [ "$status" -eq 0 ] + [[ "${lines[0]}" =~ $VERSION_REGEX ]] +} + +@test "short-example: give only mandatory arguments fails" { + run ./libclapp_example_short string 7 + [ "$status" -eq 1 ] + [[ "${lines[0]}" =~ $CLAPP_EXCEPTION_REGEX ]] +} + +@test "short-example: give only mandatory short option -s fails" { + run ./libclapp_example_short -s option_string + [ "$status" -eq 1 ] + [[ "${lines[0]}" =~ $CLAPP_EXCEPTION_REGEX ]] +} + +@test "short-example: give only mandatory short option -1 fails" { + run ./libclapp_example_short -1=option_string + [ "$status" -eq 1 ] + [[ "${lines[0]}" =~ $CLAPP_EXCEPTION_REGEX ]] +} + +@test "short-example: give only mandatory short option --string fails" { + run ./libclapp_example_short --string option_string + [ "$status" -eq 1 ] + [[ "${lines[0]}" =~ $CLAPP_EXCEPTION_REGEX ]] +} + +@test "short-example: give only mandatory arguments and long option succeed" { + run ./libclapp_example_short string 8 --string opt_string + [ "$status" -eq 0 ] + [ "${lines[0]}" = "string-arg: string" ] + [ "${lines[1]}" = "int-arg: 8" ] + [ "${lines[2]}" = "variadic-string-arg: not given" ] + [ "${lines[3]}" = "string_param: 'opt_string'" ] + [ "${lines[4]}" = "string_vector_param: not given" ] +} + +@test "short-example: give only mandatory arguments and short option succeeds" { + run ./libclapp_example_short -s opt_string_short str 7 + [ "$status" -eq 0 ] + [ "${lines[0]}" = "string-arg: str" ] + [ "${lines[1]}" = "int-arg: 7" ] + [ "${lines[2]}" = "variadic-string-arg: not given" ] + [ "${lines[3]}" = "string_param: 'opt_string_short'" ] + [ "${lines[4]}" = "string_vector_param: not given" ] +} + +@test "short-example: give only mandatory arguments and short option with variadic argument succeeds" { + run ./libclapp_example_short s 6 var1 var2 var3 -s opt_string_short + [ "$status" -eq 0 ] + [ "${lines[0]}" = "string-arg: s" ] + [ "${lines[1]}" = "int-arg: 6" ] + [ "${lines[2]}" = "variadic-string-arg (size: 3): var1, var2, var3, " ] + [ "${lines[3]}" = "string_param: 'opt_string_short'" ] + [ "${lines[4]}" = "string_vector_param: not given" ] +} + +@test "short-example: give only mandatory arguments and long option with variadic argument succeeds" { + run ./libclapp_example_short s 6 --string-vector var1 --string-vector var2 --string-vector var3 --string opt_string_long + [ "$status" -eq 0 ] + [ "${lines[0]}" = "string-arg: s" ] + [ "${lines[1]}" = "int-arg: 6" ] + [ "${lines[2]}" = "variadic-string-arg: not given" ] + [ "${lines[3]}" = "string_param: 'opt_string_long'" ] + [ "${lines[4]}" = "string_vector_param (size: 3): var1, var2, var3, " ] +} diff --git a/examples/test_sub_parser_example.sh b/examples/test_sub_parser_example.sh new file mode 100755 index 00000000..7065cb8c --- /dev/null +++ b/examples/test_sub_parser_example.sh @@ -0,0 +1,97 @@ +#!/usr/bin/env bats + +VERSION_REGEX='^libClaPP\ v.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3} [a-zA-Z]+\-Build: ' +CLAPP_EXCEPTION_REGEX='^Caught\ ClaPP-Exception:.*$' + +@test "sub-parser-example: no arguments/options given throws" { + run ./libclapp_example_sub_parser + [ "$status" -eq 0 ] + [ "${lines[0]}" = "verbose: 2" ] + [ "${lines[1]}" = "first parser not active" ] + [ "${lines[2]}" = "second parser not active" ] +} + +@test "sub-parser-example: show help with long option --help" { + run ./libclapp_example_sub_parser --help + [ "$status" -eq 0 ] + [ "${lines[0]}" = "Usage:" ] +} + +@test "sub-parser-example: show help with short option -h" { + run ./libclapp_example_sub_parser -h + [ "$status" -eq 0 ] + [ "${lines[0]}" = "Usage:" ] +} + +@test "sub-parser-example: show version with long option --version" { + run ./libclapp_example_sub_parser --version + [ "$status" -eq 0 ] + [[ "${lines[0]}" =~ $VERSION_REGEX ]] +} + +@test "sub-parser-example: give verbose options multiple times" { + run ./libclapp_example_sub_parser --verbose -v + [ "$status" -eq 0 ] + [ "${lines[0]}" = "verbose: 4" ] + [ "${lines[1]}" = "first parser not active" ] + [ "${lines[2]}" = "second parser not active" ] +} + +@test "sub-parser-example: first: give only mandatory short option -b" { + run ./libclapp_example_sub_parser first -b + [ "$status" -eq 0 ] + [ "${lines[0]}" = "verbose: 2" ] + [ "${lines[1]}" = "first parser active" ] + [ "${lines[2]}" = "short_bool: 1" ] + [ "${lines[3]}" = "string: not given" ] + [ "${lines[4]}" = "string-arg-x: abaa" ] + [ "${lines[5]}" = "double-opt: 10" ] + [ "${lines[6]}" = "second parser not active" ] +} + +@test "sub-parser-example: first: give only mandatory short option -b and optional option -d" { + run ./libclapp_example_sub_parser first -b -d 11 + [ "$status" -eq 0 ] + [ "${lines[0]}" = "verbose: 2" ] + [ "${lines[1]}" = "first parser active" ] + [ "${lines[2]}" = "short_bool: 1" ] + [ "${lines[3]}" = "string: not given" ] + [ "${lines[4]}" = "string-arg-x: abaa" ] + [ "${lines[5]}" = "double-opt: 11" ] + [ "${lines[6]}" = "second parser not active" ] +} + +@test "sub-parser-example: first: give only mandatory short option -b and optional option -s" { + run ./libclapp_example_sub_parser first -b -s string + [ "$status" -eq 0 ] + [ "${lines[0]}" = "verbose: 2" ] + [ "${lines[1]}" = "first parser active" ] + [ "${lines[2]}" = "short_bool: 1" ] + [ "${lines[3]}" = "string: string" ] + [ "${lines[4]}" = "string-arg-x: abaa" ] + [ "${lines[5]}" = "double-opt: 10" ] + [ "${lines[6]}" = "second parser not active" ] +} + +@test "sub-parser-example: second: give only mandatory args" { + run ./libclapp_example_sub_parser second 9 121 + [ "$status" -eq 0 ] + [ "${lines[0]}" = "verbose: 2" ] + [ "${lines[1]}" = "first parser not active" ] + [ "${lines[2]}" = "second parser active" ] + [ "${lines[3]}" = "int-arg: 9" ] + [ "${lines[4]}" = "double-arg: 121" ] + [ "${lines[5]}" = "string-arg-x: default-string-arg-x" ] + [ "${lines[6]}" = "variadic-int-arg: not given" ] +} +@test "sub-parser-example: second: give only mandatory args and optional args" { + run ./libclapp_example_sub_parser second 9 121 string-arg 6 7 + [ "$status" -eq 0 ] + [ "${lines[0]}" = "verbose: 2" ] + [ "${lines[1]}" = "first parser not active" ] + [ "${lines[2]}" = "second parser active" ] + [ "${lines[3]}" = "int-arg: 9" ] + [ "${lines[4]}" = "double-arg: 121" ] + [ "${lines[5]}" = "string-arg-x: string-arg" ] + [ "${lines[6]}" = "variadic-int-arg (size: 2): 6, 7, " ] +} diff --git a/src/clapp/CMakeLists.txt b/src/clapp/CMakeLists.txt index 291ce40a..cbf5a46f 100644 --- a/src/clapp/CMakeLists.txt +++ b/src/clapp/CMakeLists.txt @@ -3,9 +3,9 @@ set(libClaPP_INCLUDE_DIRS $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../inclu $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/../include>) target_include_directories(clapp PRIVATE ${libClaPP_INCLUDE_DIRS} SYSTEM INTERFACE ${libClaPP_INCLUDE_DIRS} $<INSTALL_INTERFACE:../include>) -target_link_libraries(clapp PUBLIC libClaPP_GSL ${libClaPP_FS_LINKER_FLAG}) +target_link_libraries(clapp PUBLIC ClaPP_OPTIONS libClaPP_GSL ${libClaPP_FS_LINKER_FLAG}) target_compile_options(clapp PRIVATE ${libClaPP_COMPILE_OPTIONS}) -set_target_properties(clapp PROPERTIES VERSION "${project_VERSION_MAJOR}.${project_VERSION_MINOR}" POSITION_INDEPENDENT_CODE ON) +set_target_properties(clapp PROPERTIES VERSION "${libClaPP_VERSION}" POSITION_INDEPENDENT_CODE ON) if(CLANG_TIDY) set_target_properties(clapp PROPERTIES CXX_CLANG_TIDY "${CLANG_TIDY_ARGS}") endif() diff --git a/src/clapp/exception.cpp b/src/clapp/exception.cpp index 78332ccd..c75fe818 100644 --- a/src/clapp/exception.cpp +++ b/src/clapp/exception.cpp @@ -160,10 +160,10 @@ clapp::exception::parser_exception_t::~parser_exception_t() noexcept = default; clapp::exception::sub_parser_exception_t::sub_parser_exception_t( const char* message) - : clapp_exception_t(message) {} + : parser_exception_t(message) {} clapp::exception::sub_parser_exception_t::sub_parser_exception_t( const std::string& message) - : clapp_exception_t(message) {} + : parser_exception_t(message) {} clapp::exception::sub_parser_exception_t::sub_parser_exception_t( const sub_parser_exception_t&) = default; clapp::exception::sub_parser_exception_t& @@ -176,3 +176,41 @@ clapp::exception::sub_parser_exception_t::operator=( sub_parser_exception_t&&) noexcept = default; clapp::exception::sub_parser_exception_t::~sub_parser_exception_t() noexcept = default; + +clapp::exception::main_parser_exception_t::main_parser_exception_t( + const char* message) + : parser_exception_t(message) {} +clapp::exception::main_parser_exception_t::main_parser_exception_t( + const std::string& message) + : parser_exception_t(message) {} +clapp::exception::main_parser_exception_t::main_parser_exception_t( + const main_parser_exception_t&) = default; +clapp::exception::main_parser_exception_t& +clapp::exception::main_parser_exception_t::operator=( + const main_parser_exception_t&) = default; +clapp::exception::main_parser_exception_t::main_parser_exception_t( + main_parser_exception_t&&) noexcept = default; +clapp::exception::main_parser_exception_t& +clapp::exception::main_parser_exception_t::operator=( + main_parser_exception_t&&) noexcept = default; +clapp::exception::main_parser_exception_t::~main_parser_exception_t() noexcept = + default; + +clapp::exception::no_executable_exception_t::no_executable_exception_t( + const char* message) + : main_parser_exception_t(message) {} +clapp::exception::no_executable_exception_t::no_executable_exception_t( + const std::string& message) + : main_parser_exception_t(message) {} +clapp::exception::no_executable_exception_t::no_executable_exception_t( + const no_executable_exception_t&) = default; +clapp::exception::no_executable_exception_t& +clapp::exception::no_executable_exception_t::operator=( + const no_executable_exception_t&) = default; +clapp::exception::no_executable_exception_t::no_executable_exception_t( + no_executable_exception_t&&) noexcept = default; +clapp::exception::no_executable_exception_t& +clapp::exception::no_executable_exception_t::operator=( + no_executable_exception_t&&) noexcept = default; +clapp::exception::no_executable_exception_t:: + ~no_executable_exception_t() noexcept = default; diff --git a/src/clapp/main_parser.cpp b/src/clapp/main_parser.cpp index 722f8ae3..94cf3b82 100644 --- a/src/clapp/main_parser.cpp +++ b/src/clapp/main_parser.cpp @@ -13,6 +13,7 @@ // SOFTWARE. /////////////////////////////////////////////////////////////////////////////// +#include <clapp/exception.h> #include <clapp/main_parser.h> clapp::parser::basic_main_parser_t::basic_main_parser_t() = default; @@ -24,7 +25,10 @@ clapp::parser::basic_main_parser_t::operator bool() const { } std::string clapp::parser::basic_main_parser_t::get_executable() const { - return executable.value(); + if (executable.has_value()) { + return executable.value(); + } + throw no_executable_exception_t{"The parser does not know the executable."}; } void clapp::parser::basic_main_parser_t::parse(int argc, @@ -45,6 +49,16 @@ void clapp::parser::basic_main_parser_t::parse_and_validate( validate_recursive(); } -std::string clapp::parser::basic_main_parser_t::gen_help_prefix() const { - return basic_parser_t::gen_help_prefix() + get_executable(); +std::string clapp::parser::basic_main_parser_t::gen_short_line_prefix() const { + return get_executable() + gen_short_line(); +} + +void clapp::parser::basic_main_parser_t::set_max_option_string_size( + const std::size_t max_option_size) { + max_option_string_size = max_option_size; +} + +std::size_t clapp::parser::basic_main_parser_t::get_max_option_string_size() + const { + return max_option_string_size; } diff --git a/src/clapp/parser.cpp b/src/clapp/parser.cpp index 7abf014d..988897fc 100644 --- a/src/clapp/parser.cpp +++ b/src/clapp/parser.cpp @@ -76,8 +76,8 @@ clapp::parser::basic_parser_t::get_optional_argument_descriptions() { } void clapp::parser::basic_parser_t::reg(reg_sub_parser_conf_t&& config) { - if (max_option_string_size < config.sub_parser_name.size()) { - max_option_string_size = config.sub_parser_name.size(); + if (get_max_option_string_size() < config.sub_parser_name.size()) { + set_max_option_string_size(config.sub_parser_name.size()); } if (!get_optional_argument_descriptions().empty()) { @@ -105,124 +105,205 @@ void clapp::parser::basic_parser_t::reg(reg_sub_parser_conf_t&& config) { } get_sub_parser_descriptions().push_back( - {config.sub_parser_name, std::move(config.description)}); + {config.sub_parser_name, std::move(config.description), config.parser}); get_sub_parsers().emplace(std::move(config.sub_parser_name), config.parser); } -std::string clapp::parser::basic_parser_t::gen_help_prefix() const { - return "Usage: \n"; +std::string clapp::parser::basic_parser_t::gen_usage_prefix() { + return "Usage:\n"; } -std::string clapp::parser::basic_parser_t::gen_help_msg() const { - std::string arg_line; - std::string sub_parser_desc; - std::string mandatory_option_desc; - std::string optional_option_desc; - std::string mandatory_argument_desc; - std::string optional_argument_desc; - if (!mandatory_option_descriptions.empty()) { - mandatory_option_desc = "\nMandatory Options:\n"; - for (const option_description_container_t& desc_cont : - mandatory_option_descriptions) { - mandatory_option_desc += - " " + desc_cont.option_string + - std::string( - max_option_string_size + 1 - desc_cont.option_string.size(), - ' ') + - desc_cont.description + "\n"; - arg_line += desc_cont.option_string; - if (desc_cont.option_type == - clapp::basic_parser_t::option_type_t::vector) { - arg_line += "..."; - } - arg_line += " "; +clapp::parser::basic_parser_t::help_contents_t +clapp::parser::basic_parser_t::gen_detailed_help_contents() const { + help_contents_t ret; + for (const argument_description_container_t& desc_cont : + mandatory_argument_descriptions) { + ret.mandatory_arguments.emplace_back( + help_line_t{" " + desc_cont.argument_string + + std::string(get_max_option_string_size() + 1 - + desc_cont.argument_string.size(), + ' '), + desc_cont.description}); + } + + for (const option_description_container_t& desc_cont : + mandatory_option_descriptions) { + ret.mandatory_options.emplace_back( + help_line_t{" " + desc_cont.option_string + + std::string(get_max_option_string_size() + 1 - + desc_cont.option_string.size(), + ' '), + desc_cont.description}); + } + + for (const argument_description_container_t& desc_cont : + optional_argument_descriptions) { + ret.optional_arguments.emplace_back( + help_line_t{" " + desc_cont.argument_string + + std::string(get_max_option_string_size() + 1 - + desc_cont.argument_string.size(), + ' '), + desc_cont.description}); + } + + for (const option_description_container_t& desc_cont : + optional_option_descriptions) { + ret.optional_options.emplace_back( + help_line_t{" " + desc_cont.option_string + + std::string(get_max_option_string_size() + 1 - + desc_cont.option_string.size(), + ' '), + desc_cont.description}); + } + + for (const sub_parser_description_container_t& desc_cont : + sub_parser_descriptions) { + ret.sub_parser.emplace( + desc_cont.sub_parser_string, + sub_parser_line_t{ + " " + desc_cont.sub_parser_string + + std::string(get_max_option_string_size() + 1 - + desc_cont.sub_parser_string.size(), + ' '), + desc_cont.description, desc_cont.parser}); + } + + return ret; +} + +std::string clapp::parser::basic_parser_t::gen_short_line() const { + std::string short_line; + for (const argument_description_container_t& desc_cont : + mandatory_argument_descriptions) { + short_line += " <" + desc_cont.argument_string + ">"; + if (desc_cont.argument_type == + clapp::basic_parser_t::argument_type_t::variadic) { + short_line += "..."; } } - if (!optional_option_descriptions.empty()) { - optional_option_desc = "\nOptional Options:\n"; - for (const option_description_container_t& desc_cont : - optional_option_descriptions) { - optional_option_desc += - " " + desc_cont.option_string + - std::string( - max_option_string_size + 1 - desc_cont.option_string.size(), - ' ') + - desc_cont.description + "\n"; - arg_line += "[" + desc_cont.option_string; - if (desc_cont.option_type == - clapp::basic_parser_t::option_type_t::vector) { - arg_line += "..."; - } - arg_line += "] "; + for (const option_description_container_t& desc_cont : + mandatory_option_descriptions) { + short_line += " " + desc_cont.option_string; + if (desc_cont.option_type == + clapp::basic_parser_t::option_type_t::vector) { + short_line += "..."; } } - if (!mandatory_argument_descriptions.empty()) { - mandatory_argument_desc = "\nMandatory Arguments:\n"; - - for (const argument_description_container_t& desc_cont : - mandatory_argument_descriptions) { - mandatory_argument_desc += - " " + desc_cont.argument_string + - std::string(max_option_string_size + 1 - - desc_cont.argument_string.size(), - ' ') + - desc_cont.description + "\n"; - arg_line += "<" + desc_cont.argument_string + ">"; - if (desc_cont.argument_type == - clapp::basic_parser_t::argument_type_t::variadic) { - arg_line += "..."; - } - arg_line += " "; + for (const argument_description_container_t& desc_cont : + optional_argument_descriptions) { + short_line += " [<" + desc_cont.argument_string + ">"; + if (desc_cont.argument_type == + clapp::basic_parser_t::argument_type_t::variadic) { + short_line += "..."; } + short_line += "]"; } - if (!optional_argument_descriptions.empty()) { - optional_argument_desc = "\nOptional Arguments:\n"; + for (const option_description_container_t& desc_cont : + optional_option_descriptions) { + short_line += " [" + desc_cont.option_string; + if (desc_cont.option_type == + clapp::basic_parser_t::option_type_t::vector) { + short_line += "..."; + } + short_line += "]"; + } - for (const argument_description_container_t& desc_cont : - optional_argument_descriptions) { - optional_argument_desc += - " " + desc_cont.argument_string + - std::string(max_option_string_size + 1 - - desc_cont.argument_string.size(), - ' ') + - desc_cont.description + "\n"; + return short_line; +} - arg_line += "[<" + desc_cont.argument_string + ">"; - if (desc_cont.argument_type == - clapp::basic_parser_t::argument_type_t::variadic) { - arg_line += "..."; - } - arg_line += "] "; +std::string clapp::parser::basic_parser_t::gen_opt_arg_lines( + const clapp::parser::basic_parser_t::help_contents_t& help_contents, + const std::size_t num_spaces) { + std::string ret; + if (!help_contents.mandatory_arguments.empty()) { + ret += "\n" + std::string(num_spaces + num_sub_spaces, ' ') + + "Mandatory Arguments:\n"; + for (const help_line_t& line : help_contents.mandatory_arguments) { + ret += std::string(num_spaces + num_sub_spaces, ' ') + line.name + + line.description + '\n'; } } - if (!sub_parser_descriptions.empty()) { - sub_parser_desc = "\nAvailable sub-parsers:\n"; + if (!help_contents.mandatory_options.empty()) { + ret += "\n" + std::string(num_spaces + num_sub_spaces, ' ') + + "Mandatory Options:\n"; + for (const help_line_t& line : help_contents.mandatory_options) { + ret += std::string(num_spaces + num_sub_spaces, ' ') + line.name + + line.description + '\n'; + } + } + + if (!help_contents.optional_arguments.empty()) { + ret += "\n" + std::string(num_spaces + num_sub_spaces, ' ') + + "Optional Arguments:\n"; + for (const help_line_t& line : help_contents.optional_arguments) { + ret += std::string(num_spaces + num_sub_spaces, ' ') + line.name + + line.description + '\n'; + } + } + + if (!help_contents.optional_options.empty()) { + ret += "\n" + std::string(num_spaces + num_sub_spaces, ' ') + + "Optional Options:\n"; + for (const help_line_t& line : help_contents.optional_options) { + ret += std::string(num_spaces + num_sub_spaces, ' ') + line.name + + line.description + '\n'; + } + } + return ret; +} + +std::string clapp::parser::basic_parser_t::gen_help_desc( + const std::size_t num_spaces, const std::size_t rec_depth) const { + std::string ret{}; + clapp::parser::basic_parser_t::help_contents_t help_contents{ + gen_detailed_help_contents()}; + + if (!help_contents.sub_parser.empty()) { + ret += + "\n" + std::string(num_spaces + num_sub_spaces, ' ') + "Subparser:"; + for (auto const& item : help_contents.sub_parser) { + ret += '\n' + std::string(num_spaces + num_sub_spaces, ' ') + + item.second.name + item.second.description; + if (rec_depth > 0) { + ret += item.second.parser.gen_help_desc( + num_spaces + num_sub_spaces * 2, rec_depth - 1) + + '\n'; + } + } + } + ret += gen_opt_arg_lines(help_contents, num_spaces); + return ret; +} +std::string clapp::parser::basic_parser_t::gen_short_lines( + const std::size_t rec_depth) const { + std::string short_lines{}; + if (rec_depth > 0) { for (const sub_parser_description_container_t& desc_cont : sub_parser_descriptions) { - sub_parser_desc += - " " + desc_cont.sub_parser_string + - std::string(max_option_string_size + 1 - - desc_cont.sub_parser_string.size(), - ' ') + - desc_cont.description + "\n"; + short_lines += desc_cont.parser.gen_short_line_prefix() + "\n"; } - arg_line += "<sub-parser> [sub-parser args/opts...] "; } + short_lines += gen_short_line_prefix() + "\n"; + return short_lines; +} - return arg_line + "\n" + mandatory_argument_desc + optional_argument_desc + - sub_parser_desc + mandatory_option_desc + optional_option_desc; +std::string clapp::parser::basic_parser_t::gen_help_msg( + const std::size_t rec_depth) const { + const clapp::parser::basic_parser_t& parser{get_active_parser()}; + return parser.gen_short_lines(rec_depth) + + parser.gen_help_desc(0, rec_depth); } clapp::parser::basic_parser_t::arg_iterator clapp::basic_parser_t::process_parse_result( const arg_iterator it, - const clapp::basic_parser_t::parse_result_t& parse_result) const { + const clapp::basic_parser_t::parse_result_t& parse_result) { if (it == parse_result.it) { if (parse_result.short_option) { std::stringstream ss; @@ -394,9 +475,20 @@ clapp::value::found_func_t clapp::parser::basic_parser_t::gen_func_print_help_and_exit( const int exit_code) const { return found_func_t{[this, exit_code]() { - std::cout << gen_help_prefix() + " " + gen_help_msg(); + constexpr std::size_t max_rec_depth{65535}; + std::cout << gen_usage_prefix() + gen_help_msg(max_rec_depth); exit(exit_code); }}; } +const clapp::parser::basic_parser_t& +clapp::parser::basic_parser_t::get_active_parser() const { + for (auto const& item : sub_parsers) { + if (item.second.is_active()) { + return item.second.get_active_parser(); + } + } + return *this; +} + clapp::parser::basic_parser_t::~basic_parser_t() = default; diff --git a/src/clapp/sub_parser.cpp b/src/clapp/sub_parser.cpp index 244a41fb..88822762 100644 --- a/src/clapp/sub_parser.cpp +++ b/src/clapp/sub_parser.cpp @@ -17,35 +17,37 @@ clapp::parser::basic_sub_parser_t::~basic_sub_parser_t() = default; -clapp::parser::basic_sub_parser_t::operator bool() const { return active; } - -std::string clapp::parser::basic_sub_parser_t::get_sub_parser_name() const { - return sub_parser_name; -} - void clapp::parser::basic_sub_parser_t::sub_parse(arg_iterator begin, arg_iterator end) { active = true; for (arg_iterator it = begin; it != end;) { std::string_view option{*it}; parse_result_t parse_result{parse(option, it, end)}; - if (it == parse_result.it && parse_parent) { - parse_result = parent_parser.parse(option, it, end); - } it = process_parse_result(it, parse_result); } } clapp::parser::basic_sub_parser_t::basic_sub_parser_t( clapp::basic_parser_t& parser, std::string sub_parser_name_arg, - std::string description_arg, bool parse_parent_arg) + std::string description_arg) : parent_parser{parser}, sub_parser_name{std::move(sub_parser_name_arg)}, - description{std::move(description_arg)}, - parse_parent{parse_parent_arg} { + description{std::move(description_arg)} { parser.reg(reg_sub_parser_conf_t{*this, sub_parser_name, description}); } -std::string clapp::parser::basic_sub_parser_t::gen_help_prefix() const { - return parent_parser.gen_help_prefix() + ' ' + sub_parser_name; +std::string clapp::parser::basic_sub_parser_t::gen_short_line_prefix() const { + return parent_parser.gen_short_line_prefix() + " " + sub_parser_name + + gen_short_line(); +} + +void clapp::parser::basic_sub_parser_t::set_max_option_string_size( + const std::size_t max_option_size) { + parent_parser.set_max_option_string_size(max_option_size + + num_sub_spaces * 2); +} + +std::size_t clapp::parser::basic_sub_parser_t::get_max_option_string_size() + const { + return parent_parser.get_max_option_string_size() - num_sub_spaces * 2; } diff --git a/src/clapp/value.cpp b/src/clapp/value.cpp index 6439f00a..b6921379 100644 --- a/src/clapp/value.cpp +++ b/src/clapp/value.cpp @@ -165,7 +165,7 @@ std::string clapp::path_exists_t::append_restriction_text() { } void clapp::path_exists_t::validate(const clapp::fs::path& path, - const std::string& param_name) const { + const std::string& param_name) { if (!clapp::fs::exists(path)) { std::stringstream ss; ss << "CLI value " << path << " for '" << param_name diff --git a/src/include/clapp/argument.h b/src/include/clapp/argument.h index a9468da4..d289dbdc 100644 --- a/src/include/clapp/argument.h +++ b/src/include/clapp/argument.h @@ -36,7 +36,8 @@ using variadic_value_func_t = std::function<std::vector<T>(void)>; template <typename T> struct argument_callbacks_t { - basic_parser_t::argument_func_t af; + using argument_func_t = basic_parser_t::argument_func_t; + argument_func_t af; std::optional<given_func_t> given; std::optional<has_value_func_t> has_value; std::optional<arg_value_func_t<T>> value; @@ -44,7 +45,8 @@ struct argument_callbacks_t { template <typename T> struct variadic_argument_callbacks_t { - basic_parser_t::argument_func_t af; + using argument_func_t = basic_parser_t::argument_func_t; + argument_func_t af; std::optional<given_func_t> given; std::optional<has_value_func_t> has_value; std::optional<variadic_value_func_t<T>> value; @@ -107,6 +109,7 @@ class basic_argument_t { basic_argument_t(basic_parser_t& parser, const std::string& argument_name, const std::string& description, Params&&... parameters); constexpr explicit operator bool() const noexcept; + constexpr bool has_value() const noexcept; T value() const; constexpr bool given() const noexcept; @@ -133,7 +136,8 @@ class basic_variadic_argument_t { const std::string& argument_name, const std::string& description, Params&&... parameters); - constexpr explicit operator bool() const noexcept; + inline explicit operator bool() const noexcept; + inline bool has_value() const noexcept; std::vector<T> value() const; constexpr bool given() const noexcept; diff --git a/src/include/clapp/argument.hpp b/src/include/clapp/argument.hpp index 28b4b8ba..f98629b4 100644 --- a/src/include/clapp/argument.hpp +++ b/src/include/clapp/argument.hpp @@ -113,13 +113,15 @@ ARG_CONF clapp::argument::gen_arg_conf( CALLBACKS&& callbacks, const std::string& argument_name, std::vector<typename arg_params_t<T>::validate_func_t>&& validate_funcs, const std::string& description, basic_parser_t::purpose_t purpose) { + using optional_arg_validate_func_t = + std::optional<basic_parser_t::validate_func_t>; + optional_arg_validate_func_t arg_validate_func{gen_arg_validate_func<T>( + std::move(callbacks.value), std::move(callbacks.has_value), + std::move(callbacks.given), std::move(validate_funcs), argument_name, + purpose)}; + return ARG_CONF{std::move(callbacks.af), argument_name, description, - gen_arg_validate_func<T>(std::move(callbacks.value), - std::move(callbacks.has_value), - std::move(callbacks.given), - std::move(validate_funcs), - argument_name, purpose), - purpose}; + std::move(arg_validate_func), purpose}; } template <typename T, typename ARG_CONF, typename CALLBACKS, typename... Params> @@ -185,6 +187,12 @@ constexpr clapp::argument::basic_argument_t<T>::operator bool() const noexcept { return _value.has_value(); } +template <typename T> +constexpr bool clapp::argument::basic_argument_t<T>::has_value() const + noexcept { + return _value.has_value(); +} + template <typename T> T clapp::argument::basic_argument_t<T>::value() const { if (_value) { @@ -240,9 +248,15 @@ clapp::argument::basic_variadic_argument_t<T>::basic_variadic_argument_t( } template <typename T> -constexpr clapp::argument::basic_variadic_argument_t<T>::operator bool() const +inline clapp::argument::basic_variadic_argument_t<T>::operator bool() const + noexcept { + return !_value.empty(); +} + +template <typename T> +inline bool clapp::argument::basic_variadic_argument_t<T>::has_value() const noexcept { - return _value.size() > 0; + return !_value.empty(); } template <typename T> diff --git a/src/include/clapp/build_info.h.in b/src/include/clapp/build_info.h.in index d092878e..f30e7eeb 100644 --- a/src/include/clapp/build_info.h.in +++ b/src/include/clapp/build_info.h.in @@ -19,22 +19,22 @@ #include <cstddef> #include <cstdint> namespace clapp { - inline namespace build_info { -constexpr const char* project_name = "@libClaPP_PROJECT_NAME@"; -constexpr std::size_t version_major = @project_VERSION_MAJOR@; -constexpr std::size_t version_minor = @project_VERSION_MINOR@; -constexpr std::size_t version_patch_level = @project_VERSION_PATCH_LEVEL@; -constexpr const char* version = - "@project_VERSION_MAJOR@.@project_VERSION_MINOR@" - ".@project_VERSION_PATCH_LEVEL@"; -constexpr const char* git_hash = "@libClaPP_GIT_HASH@"; -constexpr const char* git_branch = "@libClaPP_GIT_BRANCH@"; -constexpr const char* config_time = "@project_CONFIG_TIME@"; -constexpr const char* build_type = "@CMAKE_BUILD_TYPE@"; - +constexpr const char * project_name = "@CMAKE_PROJECT_NAME@"; +constexpr std::size_t version_major = @libClaPP_VERSION_MAJOR@; +constexpr std::size_t version_minor = @libClaPP_VERSION_MINOR@; +constexpr std::size_t version_patch = @libClaPP_VERSION_PATCH@; +constexpr const char *version = "@libClaPP_VERSION@"; +constexpr const char *git_hash = "@libClaPP_GIT_HASH@"; +constexpr const char *git_branch = "@libClaPP_GIT_BRANCH@"; +constexpr const char *git_description = "@libClaPP_GIT_DESCRIPTION@"; +constexpr const char *config_time = "@libClaPP_CONFIG_TIME@"; +constexpr const char *build_type = "@CMAKE_BUILD_TYPE@"; +constexpr const char *build_info_string = "@CMAKE_PROJECT_NAME@ " + "v.@libClaPP_VERSION@ @CMAKE_BUILD_TYPE@-Build: " + "@libClaPP_GIT_BRANCH@:@libClaPP_GIT_HASH@ " + "(@libClaPP_GIT_DESCRIPTION@)"; } // namespace build_info - } // namespace clapp #endif diff --git a/src/include/clapp/exception.h b/src/include/clapp/exception.h index acf65350..2b97da96 100644 --- a/src/include/clapp/exception.h +++ b/src/include/clapp/exception.h @@ -119,7 +119,7 @@ class parser_exception_t : public clapp_exception_t { ~parser_exception_t() noexcept override; }; -class sub_parser_exception_t : public clapp_exception_t { +class sub_parser_exception_t : public parser_exception_t { public: explicit sub_parser_exception_t(const char* message); explicit sub_parser_exception_t(const std::string& message); @@ -130,6 +130,28 @@ class sub_parser_exception_t : public clapp_exception_t { ~sub_parser_exception_t() noexcept override; }; +class main_parser_exception_t : public parser_exception_t { + public: + explicit main_parser_exception_t(const char* message); + explicit main_parser_exception_t(const std::string& message); + main_parser_exception_t(const main_parser_exception_t&); + main_parser_exception_t& operator=(const main_parser_exception_t&); + main_parser_exception_t(main_parser_exception_t&&) noexcept; + main_parser_exception_t& operator=(main_parser_exception_t&&) noexcept; + ~main_parser_exception_t() noexcept override; +}; + +class no_executable_exception_t : public main_parser_exception_t { + public: + explicit no_executable_exception_t(const char* message); + explicit no_executable_exception_t(const std::string& message); + no_executable_exception_t(const no_executable_exception_t&); + no_executable_exception_t& operator=(const no_executable_exception_t&); + no_executable_exception_t(no_executable_exception_t&&) noexcept; + no_executable_exception_t& operator=(no_executable_exception_t&&) noexcept; + ~no_executable_exception_t() noexcept override; +}; + } // namespace exception } // namespace clapp diff --git a/src/include/clapp/main_parser.h b/src/include/clapp/main_parser.h index 01f67144..026e95a3 100644 --- a/src/include/clapp/main_parser.h +++ b/src/include/clapp/main_parser.h @@ -35,9 +35,13 @@ class basic_main_parser_t : public basic_parser_t { explicit operator bool() const; std::string get_executable() const; - std::string gen_help_prefix() const override; + std::string gen_short_line_prefix() const override; + + void set_max_option_string_size(const std::size_t max_option_size) override; + std::size_t get_max_option_string_size() const override; private: + std::size_t max_option_string_size{0}; std::optional<std::string> executable{}; }; diff --git a/src/include/clapp/option.h b/src/include/clapp/option.h index 4aaf8410..38ad2aa5 100644 --- a/src/include/clapp/option.h +++ b/src/include/clapp/option.h @@ -35,8 +35,10 @@ using vector_value_func_t = std::function<std::vector<T>(void)>; template <typename T> struct option_callbacks_t { - basic_parser_t::long_opt_func_t loh; - basic_parser_t::short_opt_func_t soh; + using long_opt_func_t = basic_parser_t::long_opt_func_t; + using short_opt_func_t = basic_parser_t::short_opt_func_t; + long_opt_func_t loh; + short_opt_func_t soh; std::optional<given_func_t> given; std::optional<has_value_func_t> has_value; std::optional<value_func_t<T>> value; @@ -44,8 +46,10 @@ struct option_callbacks_t { template <typename T> struct option_param_callbacks_t { - basic_parser_t::long_opt_param_func_t loh; - basic_parser_t::short_opt_param_func_t soh; + using long_opt_func_t = basic_parser_t::long_opt_param_func_t; + using short_opt_func_t = basic_parser_t::short_opt_param_func_t; + long_opt_func_t loh; + short_opt_func_t soh; std::optional<given_func_t> given; std::optional<has_value_func_t> has_value; std::optional<value_func_t<T>> value; @@ -53,8 +57,10 @@ struct option_param_callbacks_t { template <typename T> struct option_vector_param_callbacks_t { - basic_parser_t::long_opt_param_func_t loh; - basic_parser_t::short_opt_param_func_t soh; + using long_opt_func_t = basic_parser_t::long_opt_param_func_t; + using short_opt_func_t = basic_parser_t::short_opt_param_func_t; + long_opt_func_t loh; + short_opt_func_t soh; std::optional<given_func_t> given; std::optional<has_value_func_t> has_value; std::optional<vector_value_func_t<T>> value; @@ -97,7 +103,8 @@ class basic_param_option_t { basic_param_option_t& operator=(const basic_param_option_t&) = delete; basic_param_option_t& operator=(basic_param_option_t&&) noexcept = delete; - inline explicit operator bool() const; + constexpr explicit operator bool() const noexcept; + constexpr bool has_value() const noexcept; T value() const; constexpr bool given() const noexcept; @@ -130,7 +137,8 @@ class basic_vector_param_option_t { basic_vector_param_option_t& operator=( basic_vector_param_option_t&&) noexcept = delete; - inline explicit operator bool() const; + inline explicit operator bool() const noexcept; + inline bool has_value() const noexcept; std::vector<T> value() const; constexpr bool given() const noexcept; @@ -161,6 +169,8 @@ class basic_option_t { virtual ~basic_option_t(); + constexpr explicit operator bool() const noexcept; + constexpr bool has_value() const noexcept; T value() const; bool given() const; @@ -181,8 +191,6 @@ class bool_option_t : public basic_option_t<bool> { ~bool_option_t() override; - inline explicit operator bool() const; - private: void found_entry(); static callbacks_t create_callbacks(bool_option_t* inst); @@ -215,8 +223,6 @@ class count_option_t : public basic_option_t<std::uint32_t> { ~count_option_t() override; - inline explicit operator bool() const; - private: void found_entry(); static callbacks_t create_callbacks(count_option_t* inst); @@ -329,8 +335,6 @@ using vector_uint64_param_option_t = clapp::basic_vector_param_option_t<std::uint64_t>; using vector_double_param_option_t = clapp::basic_vector_param_option_t<double>; using vector_float_param_option_t = clapp::basic_vector_param_option_t<float>; -using vector_double_param_option_t = clapp::basic_vector_param_option_t<double>; -using vector_float_param_option_t = clapp::basic_vector_param_option_t<float>; using vector_ns_param_option_t = clapp::basic_vector_param_option_t<std::chrono::nanoseconds>; using vector_us_param_option_t = diff --git a/src/include/clapp/option.hpp b/src/include/clapp/option.hpp index b64bf1df..b00fb9ef 100644 --- a/src/include/clapp/option.hpp +++ b/src/include/clapp/option.hpp @@ -181,7 +181,7 @@ clapp::option::opt_conf_container_t<T, OPT_CONF> clapp::option::gen_opt_conf( } return gen_opt_conf1<T, OPT_CONF>( std::forward<CALLBACKS>(callbacks), - std::vector<std::string>{std::string{std::forward<T1>(single_option)}}, + std::vector<std::string>{{std::forward<T1>(single_option)}}, std::vector<char>{}, std::move(description), std::forward<Params>(parameters)...); } @@ -261,13 +261,30 @@ OPT_CONF clapp::option::gen_opt_conf2( const std::string& description, basic_parser_t::purpose_t purpose) { std::string option_string{ OPT_CONF::create_option_string(short_option, long_option)}; - return OPT_CONF{gen_short_option(std::move(callbacks.soh), short_option), - gen_long_option(std::move(callbacks.loh), long_option), - gen_opt_validate_func<T>(std::move(callbacks.value), - std::move(callbacks.has_value), - std::move(callbacks.given), - std::move(validate_funcs), - option_string, purpose), + + using short_opt_func_t = typename std::decay_t<CALLBACKS>::short_opt_func_t; + using short_opt_conf_vec_t = + std::vector<clapp::parser::basic_parser_t::basic_short_opt_conf_t< + short_opt_func_t>>; + short_opt_conf_vec_t short_options{ + gen_short_option(std::move(callbacks.soh), short_option)}; + + using long_opt_func_t = typename std::decay_t<CALLBACKS>::long_opt_func_t; + using long_opt_conf_vec_t = std::vector< + clapp::parser::basic_parser_t::basic_long_opt_conf_t<long_opt_func_t>>; + long_opt_conf_vec_t long_options{ + gen_long_option(std::move(callbacks.loh), long_option)}; + + using optional_validate_func_t = + std::optional<basic_parser_t::validate_func_t>; + optional_validate_func_t opt_validate_func{gen_opt_validate_func<T>( + std::move(callbacks.value), std::move(callbacks.has_value), + std::move(callbacks.given), std::move(validate_funcs), option_string, + purpose)}; + + return OPT_CONF{std::move(short_options), + std::move(long_options), + std::move(opt_validate_func), std::move(option_string), description, purpose}; @@ -313,7 +330,14 @@ void clapp::option::basic_param_option_t<T>::found_entry( } template <typename T> -inline clapp::option::basic_param_option_t<T>::operator bool() const { +constexpr clapp::option::basic_param_option_t<T>::operator bool() const + noexcept { + return _value.has_value(); +} + +template <typename T> +constexpr bool clapp::option::basic_param_option_t<T>::has_value() const + noexcept { return _value.has_value(); } @@ -377,8 +401,15 @@ void clapp::option::basic_vector_param_option_t<T>::found_entry( } template <typename T> -inline clapp::option::basic_vector_param_option_t<T>::operator bool() const { - return _value.size() > 0; +inline clapp::option::basic_vector_param_option_t<T>::operator bool() const + noexcept { + return !_value.empty(); +} + +template <typename T> +inline bool clapp::option::basic_vector_param_option_t<T>::has_value() const + noexcept { + return !_value.empty(); } template <typename T> @@ -421,6 +452,16 @@ bool clapp::option::basic_option_t<T>::given() const { return _given; } +template <typename T> +constexpr clapp::option::basic_option_t<T>::operator bool() const noexcept { + return _value.has_value(); +} + +template <typename T> +constexpr bool clapp::option::basic_option_t<T>::has_value() const noexcept { + return _value.has_value(); +} + template <typename... Params> clapp::option::bool_option_t::bool_option_t(clapp::basic_parser_t& parser, Params... parameters) @@ -458,14 +499,4 @@ clapp::option::count_option_t::count_option_t(clapp::basic_parser_t& parser, } } -inline clapp::option::bool_option_t::operator bool() const { - Expects(_value.has_value()); - return _value.value(); -} - -inline clapp::option::count_option_t::operator bool() const { - Expects(_value.has_value()); - return _value.value() > 0; -} - #endif diff --git a/src/include/clapp/parser.h b/src/include/clapp/parser.h index a0d3c4e0..876287f7 100644 --- a/src/include/clapp/parser.h +++ b/src/include/clapp/parser.h @@ -71,6 +71,25 @@ class basic_parser_t { argument_type_t argument_type; }; + struct help_line_t { + std::string name; + std::string description; + }; + + struct sub_parser_line_t { + std::string name; + std::string description; + basic_sub_parser_t& parser; + }; + + struct help_contents_t { + std::vector<help_line_t> mandatory_arguments{}; + std::vector<help_line_t> optional_arguments{}; + std::vector<help_line_t> mandatory_options{}; + std::vector<help_line_t> optional_options{}; + std::map<std::string, sub_parser_line_t> sub_parser{}; + }; + template <typename short_option_func_t, typename long_option_func_t, option_type_t option_type> struct reg_option_conf_t { @@ -140,6 +159,7 @@ class basic_parser_t { struct sub_parser_description_container_t { std::string sub_parser_string; std::string description; + basic_sub_parser_t& parser; }; struct argument_description_container_t { @@ -183,8 +203,8 @@ class basic_parser_t { std::optional<std::string> long_option; }; - arg_iterator process_parse_result(arg_iterator it, - const parse_result_t& parse_result) const; + static arg_iterator process_parse_result( + arg_iterator it, const parse_result_t& parse_result); void parse(arg_iterator begin, arg_iterator end); parse_result_t parse(std::string_view option, arg_iterator it, arg_iterator end); @@ -193,12 +213,28 @@ class basic_parser_t { void validate_recursive() const; - virtual std::string gen_help_prefix() const; - std::string gen_help_msg() const; + std::string gen_short_line() const; + std::string gen_short_lines(std::size_t rec_depth) const; + virtual std::string gen_short_line_prefix() const = 0; + static std::string gen_usage_prefix(); + virtual help_contents_t gen_detailed_help_contents() const; + static std::string gen_opt_arg_lines(const help_contents_t& help_contents, + const std::size_t num_spaces); + + std::string gen_help_desc(std::size_t num_spaces, + std::size_t rec_depth) const; + std::string gen_help_msg(std::size_t rec_depth) const; value::found_func_t gen_func_print_help_and_exit(int exit_code) const; std::size_t get_num_processed_arguments() const; + virtual void set_max_option_string_size( + const std::size_t max_option_size) = 0; + virtual std::size_t get_max_option_string_size() const = 0; + + inline virtual bool is_active() const noexcept; + const basic_parser_t& get_active_parser() const; + protected: sub_parsers_map_t& get_sub_parsers(); long_options_map_t& get_long_options(); @@ -231,8 +267,10 @@ class basic_parser_t { sub_parser_descriptions_vec_t sub_parser_descriptions{}; argument_descriptions_vec_t mandatory_argument_descriptions{}; argument_descriptions_vec_t optional_argument_descriptions{}; - std::size_t max_option_string_size{0}; std::size_t num_processed_arguments{0}; + + public: + constexpr static std::size_t num_sub_spaces{2u}; }; } // namespace parser diff --git a/src/include/clapp/parser.hpp b/src/include/clapp/parser.hpp index 7cac92a9..11a9dbd0 100644 --- a/src/include/clapp/parser.hpp +++ b/src/include/clapp/parser.hpp @@ -72,8 +72,8 @@ void clapp::parser::basic_parser_t::reg( config.option_string += "=<arg>"; } - if (max_option_string_size < config.option_string.size()) { - max_option_string_size = config.option_string.size(); + if (get_max_option_string_size() < config.option_string.size()) { + set_max_option_string_size(config.option_string.size()); } option_description_container_t desc{std::move(config.option_string), @@ -99,13 +99,13 @@ void clapp::parser::basic_parser_t::reg( if (num_arguments > 0 && get_arguments()[num_arguments - 1].argument_type == argument_type_t::variadic) { std::stringstream ss; - ss << "Can't register argument '" << config.argument_name + ss << "Can't register regular argument '" << config.argument_name << "' when variadic arguments are already registered."; throw clapp::exception::argument_exception_t(ss.str()); } - if (max_option_string_size < config.argument_name.size()) { - max_option_string_size = config.argument_name.size(); + if (get_max_option_string_size() < config.argument_name.size()) { + set_max_option_string_size(config.argument_name.size()); } if (config.purpose == purpose_t::mandatory) { @@ -182,4 +182,8 @@ std::string clapp::parser::basic_parser_t::reg_option_conf_t< return option_string; } +inline bool clapp::parser::basic_parser_t::is_active() const noexcept { + return true; +} + #endif diff --git a/src/include/clapp/sub_parser.h b/src/include/clapp/sub_parser.h index 9d2423ff..77287b17 100644 --- a/src/include/clapp/sub_parser.h +++ b/src/include/clapp/sub_parser.h @@ -24,24 +24,31 @@ inline namespace parser { class basic_sub_parser_t : public basic_parser_t { public: basic_sub_parser_t(basic_parser_t& parser, std::string sub_parser_name_arg, - std::string description_arg, - bool parse_parent_arg = true); + std::string description_arg); ~basic_sub_parser_t() override; - explicit operator bool() const; - std::string get_sub_parser_name() const; + + inline bool is_active() const noexcept override; + constexpr explicit operator bool() const noexcept; + + inline std::string get_sub_parser_name() const; void sub_parse(arg_iterator begin, arg_iterator end); - std::string gen_help_prefix() const override; + + std::string gen_short_line_prefix() const override; + + void set_max_option_string_size(const std::size_t max_option_size) override; + std::size_t get_max_option_string_size() const override; private: basic_parser_t& parent_parser; std::string sub_parser_name; std::string description; bool active{false}; - bool parse_parent; }; } // namespace parser } // namespace clapp +#include <clapp/sub_parser.hpp> + #endif diff --git a/src/include/clapp/sub_parser.hpp b/src/include/clapp/sub_parser.hpp index b3a2c54b..5883f450 100644 --- a/src/include/clapp/sub_parser.hpp +++ b/src/include/clapp/sub_parser.hpp @@ -18,39 +18,17 @@ #include <clapp/sub_parser.h> -clapp::parser::basic_sub_parser_::operator bool() const { return active; } - -std::string clapp::parser::basic_sub_parser_::get_sub_parser_name() const { +inline std::string clapp::parser::basic_sub_parser_t::get_sub_parser_name() + const { return sub_parser_name; } -void clapp::parser::basic_sub_parser_::sub_parse(arg_iterator begin, - arg_iterator end) { - active = true; - for (arg_iterator it = begin; it != end;) { - std::string_view option{*it}; - clapp::basic_parser::parse_result_t parse_result{ - parse(option, it, end)}; - if (it == parse_result.it && parse_parent) { - parse_result = parent_parser.parse(option, it, end); - } - it = process_parse_result(it, parse_result); - } +inline bool clapp::parser::basic_sub_parser_t::is_active() const noexcept { + return active; } -clapp::parser::basic_sub_parser_::basic_sub_parser_( - clapp::basic_parser& parser, const std::string& _sub_parser_name, - const std::string& _description, bool _parse_parent) - : basic_parser{}, - parent_parser{parser}, - sub_parser_name{_sub_parser_name}, - description{_description}, - parse_parent{_parse_parent} { - parser.reg( - reg_sub_parser_conf_t{[this](arg_iterator begin, arg_iterator end) { - return this->sub_parse(begin, end); - }, - sub_parser_name, description}); +constexpr clapp::parser::basic_sub_parser_t::operator bool() const noexcept { + return active; } #endif diff --git a/src/include/clapp/value.h b/src/include/clapp/value.h index 9a3c846a..f57f1029 100644 --- a/src/include/clapp/value.h +++ b/src/include/clapp/value.h @@ -73,9 +73,9 @@ class found_func_t { #ifdef CLAPP_FS_AVAIL class path_exists_t { public: - std::string append_restriction_text(); + static std::string append_restriction_text(); - void validate(const fs::path &path, const std::string ¶m_name) const; + static void validate(const fs::path &path, const std::string ¶m_name); }; #endif diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 74a3ae8a..d5db6696 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,10 +1,10 @@ -set(sources value.cpp argument.cpp option.cpp parser.cpp sub_parser.cpp main_parser.cpp exception.cpp) +set(sources value.cpp argument.cpp option.cpp parser.cpp sub_parser.cpp main_parser.cpp exception.cpp build_info.cpp) add_executable(libclapp_tests ${sources}) target_link_libraries(libclapp_tests libClaPP_gmock_main clapp) target_compile_options(libclapp_tests PRIVATE ${libClaPP_TEST_COMPILE_OPTIONS}) install(TARGETS libclapp_tests DESTINATION ${CMAKE_INSTALL_BINDIR}) if(CLANG_TIDY) - set_target_properties(libclapp_tests PROPERTIES CXX_CLANG_TIDY "${CLANG_TIDY_ARGS},-llvm-include-order,-clang-diagnostic-global-constructors,-hicpp-special-member-functions,-cppcoreguidelines-owning-memory,-cert-err58-cpp,-cppcoreguidelines-special-member-functions,-clang-diagnostic-used-but-marked-unused,-cppcoreguidelines-pro-type-vararg,-clang-diagnostic-covered-switch-default,-hicpp-vararg,-fuchsia-statically-constructed-objects,-cppcoreguidelines-avoid-goto,-hicpp-avoid-goto") + set_target_properties(libclapp_tests PROPERTIES CXX_CLANG_TIDY "${CLANG_TIDY_ARGS},-llvm-include-order,-clang-diagnostic-global-constructors,-hicpp-special-member-functions,-cppcoreguidelines-owning-memory,-cert-err58-cpp,-cppcoreguidelines-special-member-functions,-clang-diagnostic-used-but-marked-unused,-cppcoreguidelines-pro-type-vararg,-clang-diagnostic-covered-switch-default,-hicpp-vararg,-fuchsia-statically-constructed-objects,-cppcoreguidelines-avoid-goto,-hicpp-avoid-goto,-misc-non-private-member-variables-in-classes,-cppcoreguidelines-non-private-member-variables-in-classes,-hicpp-avoid-c-arrays,-modernize-avoid-c-arrays,-cppcoreguidelines-avoid-c-arrays") endif() - +add_test(NAME libclapp_tests COMMAND libclapp_tests) diff --git a/tests/argument.cpp b/tests/argument.cpp index ca4b9997..e3a1952c 100644 --- a/tests/argument.cpp +++ b/tests/argument.cpp @@ -4,6 +4,8 @@ class argument_test_parser_t : public clapp::parser::basic_parser_t { public: + ~argument_test_parser_t() override; + using clapp::parser::basic_parser_t::purpose_t; using clapp::parser::basic_parser_t::argument_descriptions_vec_t; @@ -16,12 +18,29 @@ class argument_test_parser_t : public clapp::parser::basic_parser_t { using clapp::parser::basic_parser_t::get_optional_argument_descriptions; using clapp::parser::basic_parser_t::get_validate_functions; - public: - ~argument_test_parser_t() override; + [[nodiscard]] std::string gen_short_line_prefix() const override; + void set_max_option_string_size(std::size_t max_option_size) override; + [[nodiscard]] std::size_t get_max_option_string_size() const override; + + private: + std::size_t max_option_string_size{0}; }; argument_test_parser_t::~argument_test_parser_t() = default; +std::string argument_test_parser_t::gen_short_line_prefix() const { + return "arg-test-parser" + gen_short_line(); +} + +void argument_test_parser_t::set_max_option_string_size( + const std::size_t max_option_size) { + max_option_string_size = max_option_size; +} + +std::size_t argument_test_parser_t::get_max_option_string_size() const { + return max_option_string_size; +} + class simple_argument_sub_parser_t : public clapp::parser::basic_sub_parser_t { using clapp::parser::basic_sub_parser_t::basic_sub_parser_t; @@ -31,7 +50,7 @@ class simple_argument_sub_parser_t : public clapp::parser::basic_sub_parser_t { simple_argument_sub_parser_t::~simple_argument_sub_parser_t() = default; -TEST(argument, basic_argument_construct_to_short_argument_name_throws) { +TEST(argument, basicArgumentConstructToShortArgumentNameThrows) { const std::string arg_name; const std::string arg_desc{"description"}; argument_test_parser_t tp; @@ -39,9 +58,8 @@ TEST(argument, basic_argument_construct_to_short_argument_name_throws) { clapp::exception::argument_exception_t); } -TEST( - argument, - basic_argument_construct_variadic_argument_and_register_another_argument_throws) { +TEST(argument, + basicArgumentConstructVariadicArgumentAndRegisterAnotherArgumentThrows) { const std::string variadic_arg_name{"variadic"}; const std::string arg_name{"arg"}; const std::string arg_desc{"description"}; @@ -52,9 +70,8 @@ TEST( clapp::exception::argument_exception_t); } -TEST( - argument, - basic_argument_construct_optional_argument_and_register_mandatory_argument_throws) { +TEST(argument, + basicArgumentConstructOptionalArgumentAndRegisterMandatoryArgumentThrows) { const std::string optional_arg_name{"variadic"}; const std::string arg_name{"arg"}; const std::string arg_desc{"description"}; @@ -66,8 +83,7 @@ TEST( clapp::exception::argument_exception_t); } -TEST(argument, - basic_argument_construct_sub_parser_and_register_argument_throws) { +TEST(argument, basicArgumentConstructSubParserAndRegisterArgumentThrows) { const std::string arg_name{"arg"}; const std::string arg_desc{"description"}; argument_test_parser_t tp; @@ -79,7 +95,7 @@ TEST(argument, clapp::exception::argument_exception_t); } -TEST(argument, basic_argument_found_func_long) { +TEST(argument, basicArgumentFoundFuncLong) { const std::string arg_name{"arg"}; const std::string arg_desc{"description"}; @@ -101,7 +117,7 @@ TEST(argument, basic_argument_found_func_long) { ASSERT_THAT(ss.str(), testing::StrEq("this is a test")); } -TEST(argument, basic_argument_construct_simple) { +TEST(argument, basicArgumentConstructSimple) { const std::string arg_name{"argument name"}; const std::string arg_desc{"description"}; argument_test_parser_t tp; @@ -136,7 +152,7 @@ TEST(argument, basic_argument_construct_simple) { ASSERT_NO_THROW(validate_funcs[0]()); } -TEST(argument, basic_argument_construct_mandatory) { +TEST(argument, basicArgumentConstructMandatory) { const std::string arg_name{"argument name"}; const std::string arg_desc{"description"}; argument_test_parser_t tp; @@ -172,7 +188,7 @@ TEST(argument, basic_argument_construct_mandatory) { ASSERT_NO_THROW(validate_funcs[0]()); } -TEST(argument, basic_argument_construct_optional) { +TEST(argument, basicArgumentConstructOptional) { const std::string arg_name{"argument name"}; const std::string arg_desc{"description"}; argument_test_parser_t tp; @@ -206,7 +222,7 @@ TEST(argument, basic_argument_construct_optional) { ASSERT_THAT(arg.value(), testing::Eq(12345)); } -TEST(argument, basic_argument_construct_optional_default_value) { +TEST(argument, basicArgumentConstructOptionalDefaultValue) { const std::string arg_name{"argument name"}; const std::string arg_desc{"description"}; argument_test_parser_t tp; @@ -244,7 +260,7 @@ TEST(argument, basic_argument_construct_optional_default_value) { ASSERT_THAT(arg.value(), testing::Eq(123)); } -TEST(argument, basic_argument_construct_mandatory_default_value) { +TEST(argument, basicArgumentConstructMandatoryDefaultValue) { const std::string arg_name{"argument name"}; const std::string arg_desc{"desciption"}; const std::uint32_t default_value{1123}; @@ -286,10 +302,10 @@ TEST(argument, basic_argument_construct_mandatory_default_value) { args[0].func("0x12341234"); ASSERT_THAT(static_cast<bool>(arg), testing::Eq(true)); ASSERT_THAT(arg.given(), testing::Eq(true)); - ASSERT_THAT(arg.value(), testing::Eq(0x12341234u)); + ASSERT_THAT(arg.value(), testing::Eq(0x12341234U)); } -TEST(argument, basic_argument_construct_optional_default_value_min_max) { +TEST(argument, basicArgumentConstructOptionalDefaultValueMinMax) { const std::string arg_name{"argument name"}; const std::string arg_desc{"desciption"}; argument_test_parser_t tp; @@ -341,7 +357,7 @@ TEST(argument, basic_argument_construct_optional_default_value_min_max) { ASSERT_THROW(validate_funcs[0](), clapp::exception::out_of_range_t); } -TEST(argument, basic_argument_construct_mandatory_path_exists) { +TEST(argument, basicArgumentConstructMandatoryPathExists) { const std::string arg_name1{"arg"}; const std::string arg_desc1{"desc"}; argument_test_parser_t tp; @@ -389,7 +405,7 @@ TEST(argument, basic_argument_construct_mandatory_path_exists) { ASSERT_NO_THROW(validate_funcs[0]()); } -TEST(argument, basic_variadic_argument_found_func_long) { +TEST(argument, basicVariadicArgumentFoundFuncLong) { const std::string arg_name{"arg"}; const std::string arg_desc{"description"}; @@ -417,7 +433,7 @@ TEST(argument, basic_variadic_argument_found_func_long) { ASSERT_THAT(ss.str(), testing::StrEq("this is a testthis is a test")); } -TEST(argument, basic_variadic_argument_construct_simple) { +TEST(argument, basicVariadicArgumentConstructSimple) { const std::string arg_name{"argument name"}; const std::string arg_desc{"desc"}; argument_test_parser_t tp; @@ -467,7 +483,7 @@ TEST(argument, basic_variadic_argument_construct_simple) { ASSERT_NO_THROW(validate_funcs[0]()); } -TEST(argument, basic_variadic_argument_construct_simple_optional) { +TEST(argument, basicVariadicArgumentConstructSimpleOptional) { const std::string arg_name{"argument name"}; const std::string arg_desc{"desc"}; argument_test_parser_t tp; @@ -512,7 +528,7 @@ TEST(argument, basic_variadic_argument_construct_simple_optional) { testing::Eq(std::vector<std::string>{"ccc", "aba"})); } -TEST(argument, basic_variadic_argument_construct_simple_optional_min_max) { +TEST(argument, basicVariadicArgumentConstructSimpleOptionalMinMax) { const std::string arg_name{"argument name"}; const std::string arg_desc{"desc"}; const clapp::value::min_max_value_t<std::int64_t> min_max{-2, 10}; @@ -569,7 +585,7 @@ TEST(argument, basic_variadic_argument_construct_simple_optional_min_max) { ASSERT_THROW(validate_funcs[0](), clapp::exception::out_of_range_t); } -TEST(argument, basic_variadic_argument_construct_simple_default_value_throws) { +TEST(argument, basicVariadicArgumentConstructSimpleDefaultValueThrows) { const std::string arg_name{"argument name"}; const std::string arg_desc{"desc"}; const clapp::value::default_value_t<std::int8_t> default_value{10}; diff --git a/tests/build_info.cpp b/tests/build_info.cpp new file mode 100644 index 00000000..f1c07fcf --- /dev/null +++ b/tests/build_info.cpp @@ -0,0 +1,20 @@ +#include <clapp/build_info.h> +#include <gmock/gmock.h> + +TEST(buildInfo, version) { + std::stringstream ss; + ss << clapp::build_info::version_major << "." + << clapp::build_info::version_minor << "." + << clapp::build_info::version_patch; + ASSERT_THAT(clapp::build_info::version, testing::StrEq(ss.str())); +} + +TEST(buildInfo, buildInfoString) { + std::stringstream ss; + ss << clapp::build_info::project_name << " v." << clapp::build_info::version + << " " << clapp::build_info::build_type + << "-Build: " << clapp::build_info::git_branch << ":" + << clapp::build_info::git_hash << " (" + << clapp::build_info::git_description << ")"; + ASSERT_THAT(clapp::build_info::build_info_string, testing::StrEq(ss.str())); +} diff --git a/tests/exception.cpp b/tests/exception.cpp index 9499696a..8311e358 100644 --- a/tests/exception.cpp +++ b/tests/exception.cpp @@ -1,7 +1,10 @@ #include <clapp/exception.h> #include <gmock/gmock.h> -TEST(exception, clapp_exception_t) { +TEST(exception, clappExceptionT) { + static_assert(std::is_base_of<std::runtime_error, + clapp::exception::clapp_exception_t>::value); + const std::string msg{"message"}; const std::string msg2{"message2"}; @@ -18,7 +21,12 @@ TEST(exception, clapp_exception_t) { ASSERT_THAT(ce4.what(), testing::StrEq(msg)); } -TEST(exception, invalid_value_t) { +TEST(exception, invalidValueT) { + static_assert(std::is_base_of<std::runtime_error, + clapp::exception::invalid_value_t>::value); + static_assert(std::is_base_of<clapp::exception::clapp_exception_t, + clapp::exception::invalid_value_t>::value); + const std::string msg{"message"}; const std::string msg2{"message2"}; @@ -35,7 +43,12 @@ TEST(exception, invalid_value_t) { ASSERT_THAT(ce4.what(), testing::StrEq(msg)); } -TEST(exception, value_undefined_t) { +TEST(exception, valueUndefinedT) { + static_assert(std::is_base_of<std::runtime_error, + clapp::exception::value_undefined_t>::value); + static_assert(std::is_base_of<clapp::exception::clapp_exception_t, + clapp::exception::value_undefined_t>::value); + const std::string msg{"message"}; const std::string msg2{"message2"}; @@ -52,7 +65,12 @@ TEST(exception, value_undefined_t) { ASSERT_THAT(ce4.what(), testing::StrEq(msg)); } -TEST(exception, out_of_range_t) { +TEST(exception, outOfRangeT) { + static_assert(std::is_base_of<std::runtime_error, + clapp::exception::out_of_range_t>::value); + static_assert(std::is_base_of<clapp::exception::clapp_exception_t, + clapp::exception::out_of_range_t>::value); + const std::string msg{"message"}; const std::string msg2{"message2"}; @@ -69,7 +87,14 @@ TEST(exception, out_of_range_t) { ASSERT_THAT(ce4.what(), testing::StrEq(msg)); } -TEST(exception, path_does_not_exist_t) { +TEST(exception, pathDoesNotExistT) { + static_assert( + std::is_base_of<std::runtime_error, + clapp::exception::path_does_not_exist_t>::value); + static_assert( + std::is_base_of<clapp::exception::clapp_exception_t, + clapp::exception::path_does_not_exist_t>::value); + const std::string msg{"message"}; const std::string msg2{"message2"}; @@ -86,7 +111,12 @@ TEST(exception, path_does_not_exist_t) { ASSERT_THAT(ce4.what(), testing::StrEq(msg)); } -TEST(exception, option_exception_t) { +TEST(exception, optionExceptionT) { + static_assert(std::is_base_of<std::runtime_error, + clapp::exception::option_exception_t>::value); + static_assert(std::is_base_of<clapp::exception::clapp_exception_t, + clapp::exception::option_exception_t>::value); + const std::string msg{"message"}; const std::string msg2{"message2"}; @@ -103,7 +133,14 @@ TEST(exception, option_exception_t) { ASSERT_THAT(ce4.what(), testing::StrEq(msg)); } -TEST(exception, option_param_exception_t) { +TEST(exception, optionParamExceptionT) { + static_assert( + std::is_base_of<std::runtime_error, + clapp::exception::option_param_exception_t>::value); + static_assert( + std::is_base_of<clapp::exception::clapp_exception_t, + clapp::exception::option_param_exception_t>::value); + const std::string msg{"message"}; const std::string msg2{"message2"}; @@ -120,7 +157,14 @@ TEST(exception, option_param_exception_t) { ASSERT_THAT(ce4.what(), testing::StrEq(msg)); } -TEST(exception, argument_exception_t) { +TEST(exception, argumentExceptionT) { + static_assert( + std::is_base_of<std::runtime_error, + clapp::exception::argument_exception_t>::value); + static_assert( + std::is_base_of<clapp::exception::clapp_exception_t, + clapp::exception::argument_exception_t>::value); + const std::string msg{"message"}; const std::string msg2{"message2"}; @@ -137,7 +181,12 @@ TEST(exception, argument_exception_t) { ASSERT_THAT(ce4.what(), testing::StrEq(msg)); } -TEST(exception, parser_exception_t) { +TEST(exception, parserExceptionT) { + static_assert(std::is_base_of<std::runtime_error, + clapp::exception::parser_exception_t>::value); + static_assert(std::is_base_of<clapp::exception::clapp_exception_t, + clapp::exception::parser_exception_t>::value); + const std::string msg{"message"}; const std::string msg2{"message2"}; @@ -154,7 +203,17 @@ TEST(exception, parser_exception_t) { ASSERT_THAT(ce4.what(), testing::StrEq(msg)); } -TEST(exception, sub_parser_exception_t) { +TEST(exception, subParserExceptionT) { + static_assert( + std::is_base_of<std::runtime_error, + clapp::exception::sub_parser_exception_t>::value); + static_assert( + std::is_base_of<clapp::exception::clapp_exception_t, + clapp::exception::sub_parser_exception_t>::value); + static_assert( + std::is_base_of<clapp::exception::parser_exception_t, + clapp::exception::sub_parser_exception_t>::value); + const std::string msg{"message"}; const std::string msg2{"message2"}; @@ -170,3 +229,60 @@ TEST(exception, sub_parser_exception_t) { ASSERT_THAT(ce3.what(), testing::StrEq(msg2)); ASSERT_THAT(ce4.what(), testing::StrEq(msg)); } + +TEST(exception, mainParserExceptionT) { + static_assert( + std::is_base_of<std::runtime_error, + clapp::exception::main_parser_exception_t>::value); + static_assert( + std::is_base_of<clapp::exception::clapp_exception_t, + clapp::exception::main_parser_exception_t>::value); + static_assert( + std::is_base_of<clapp::exception::parser_exception_t, + clapp::exception::main_parser_exception_t>::value); + + const std::string msg{"message"}; + const std::string msg2{"message2"}; + + clapp::exception::main_parser_exception_t ce1{msg.c_str()}; + clapp::exception::main_parser_exception_t ce2{msg2}; + + clapp::exception::main_parser_exception_t ce3{ce1}; + clapp::exception::main_parser_exception_t ce4{std::move(ce1)}; + + ce4 = ce3; + ce3 = std::move(ce2); + + ASSERT_THAT(ce3.what(), testing::StrEq(msg2)); + ASSERT_THAT(ce4.what(), testing::StrEq(msg)); +} + +TEST(exception, noExecutableExceptionT) { + static_assert( + std::is_base_of<std::runtime_error, + clapp::exception::no_executable_exception_t>::value); + static_assert( + std::is_base_of<clapp::exception::clapp_exception_t, + clapp::exception::no_executable_exception_t>::value); + static_assert( + std::is_base_of<clapp::exception::parser_exception_t, + clapp::exception::no_executable_exception_t>::value); + static_assert( + std::is_base_of<clapp::exception::main_parser_exception_t, + clapp::exception::no_executable_exception_t>::value); + + const std::string msg{"message"}; + const std::string msg2{"message2"}; + + clapp::exception::no_executable_exception_t ce1{msg.c_str()}; + clapp::exception::no_executable_exception_t ce2{msg2}; + + clapp::exception::no_executable_exception_t ce3{ce1}; + clapp::exception::no_executable_exception_t ce4{std::move(ce1)}; + + ce4 = ce3; + ce3 = std::move(ce2); + + ASSERT_THAT(ce3.what(), testing::StrEq(msg2)); + ASSERT_THAT(ce4.what(), testing::StrEq(msg)); +} diff --git a/tests/main_parser.cpp b/tests/main_parser.cpp index a210b283..46189a62 100644 --- a/tests/main_parser.cpp +++ b/tests/main_parser.cpp @@ -10,26 +10,64 @@ class empty_main_parser_t : public clapp::parser::basic_main_parser_t { empty_main_parser_t::~empty_main_parser_t() = default; -TEST(main_parser, construct_main_parser) { +TEST(mainParser, constructMainParser) { constexpr const char* const argv[]{"main", nullptr}; - empty_main_parser_t tp; + empty_main_parser_t emp; - tp.parse(1, static_cast<const char* const*>(argv)); - ASSERT_THAT(static_cast<bool>(tp), testing::Eq(true)); - ASSERT_THAT(tp.get_executable(), testing::StrEq("main")); + emp.parse(1, static_cast<const char* const*>(argv)); } -TEST(parser, construct_main_parser_and_gen_help_prefix) { +TEST(mainParser, constructMainParserBoolOperator) { + constexpr const char* const argv[]{"main-exec", nullptr}; + empty_main_parser_t emp; + ASSERT_THAT(static_cast<bool>(emp), testing::Eq(false)); + + emp.parse(1, static_cast<const char* const*>(argv)); + ASSERT_THAT(static_cast<bool>(emp), testing::Eq(true)); +} + +TEST(mainParser, constructMainParserGetExecutable) { + constexpr const char* const argv[]{"main-exec", nullptr}; + empty_main_parser_t emp; + ASSERT_THROW(emp.get_executable(), + clapp::exception::no_executable_exception_t); + + emp.parse(1, static_cast<const char* const*>(argv)); + ASSERT_THAT(emp.get_executable(), testing::StrEq("main-exec")); +} + +TEST(mainParser, constructMainParserAndGenHelpPrefix) { constexpr const char* const argv[]{"main", nullptr}; - empty_main_parser_t tp; + empty_main_parser_t emp; - tp.parse(1, static_cast<const char* const*>(argv)); - ASSERT_THAT(tp.gen_help_prefix(), testing::StrEq("Usage: \nmain")); + emp.parse(1, static_cast<const char* const*>(argv)); + ASSERT_THAT(emp.gen_short_line_prefix(), testing::StrEq("main")); } -TEST(parser, construct_main_parser_parse_and_validate) { +TEST(mainParser, constructMainParserParseAndValidate) { constexpr const char* const argv[]{"main", nullptr}; - empty_main_parser_t tp; + empty_main_parser_t emp; + + emp.parse_and_validate(1, static_cast<const char* const*>(argv)); +} + +TEST(mainParser, constructMainParserIsActiveIsTrue) { + empty_main_parser_t emp; + ASSERT_THAT(emp.is_active(), testing::Eq(true)); +} + +TEST(mainParser, constructMainParserGetActiveReturnsThisRef) { + empty_main_parser_t emp; + ASSERT_THAT(&emp.get_active_parser(), testing::Eq(&emp)); +} + +TEST(mainParser, constructMainParserGetMaxOptionStringSize) { + empty_main_parser_t emp; + ASSERT_THAT(emp.get_max_option_string_size(), testing::Eq(0)); +} - tp.parse_and_validate(1, static_cast<const char* const*>(argv)); +TEST(mainParser, constructMainParserSetMaxOptionStringSize) { + empty_main_parser_t emp; + emp.set_max_option_string_size(4); + ASSERT_THAT(emp.get_max_option_string_size(), testing::Eq(4)); } diff --git a/tests/option.cpp b/tests/option.cpp index 62a305f9..06bfdbd1 100644 --- a/tests/option.cpp +++ b/tests/option.cpp @@ -26,10 +26,30 @@ class option_test_parser_t : public clapp::parser::basic_parser_t { using clapp::parser::basic_parser_t::get_optional_option_descriptions; using clapp::parser::basic_parser_t::get_short_options; using clapp::parser::basic_parser_t::get_validate_functions; + + [[nodiscard]] std::string gen_short_line_prefix() const override; + void set_max_option_string_size(std::size_t max_option_size) override; + [[nodiscard]] std::size_t get_max_option_string_size() const override; + + private: + std::size_t max_option_string_size{0}; }; option_test_parser_t::~option_test_parser_t() = default; +std::string option_test_parser_t::gen_short_line_prefix() const { + return "opt-test-parser" + gen_short_line(); +} + +void option_test_parser_t::set_max_option_string_size( + const std::size_t max_option_size) { + max_option_string_size = max_option_size; +} + +std::size_t option_test_parser_t::get_max_option_string_size() const { + return max_option_string_size; +} + class test_option_t : public clapp::option::basic_option_t<std::int32_t> { public: template <typename... Params> @@ -73,7 +93,7 @@ test_option_t::callbacks_t test_option_t::create_callbacks( [inst]() { return inst->value(); }}; } -TEST(option, basic_option_construct_value_throws) { +TEST(option, basicOptionConstructValueThrows) { const std::string long_opt{"long"}; const std::string opt_desc{"description"}; option_test_parser_t tp; @@ -81,7 +101,7 @@ TEST(option, basic_option_construct_value_throws) { ASSERT_THROW(opt.value(), clapp::exception::value_undefined_t); } -TEST(option, bool_option_construct_long_option_twice_throws) { +TEST(option, boolOptionConstructLongOptionTwiceThrows) { const std::string long_opt{"long"}; const std::string opt_desc{"description"}; option_test_parser_t tp; @@ -90,7 +110,7 @@ TEST(option, bool_option_construct_long_option_twice_throws) { clapp::exception::option_exception_t); } -TEST(option, bool_option_construct_short_option_twice_throws) { +TEST(option, boolOptionConstructShortOptionTwiceThrows) { const char short_opt{'s'}; const std::string opt_desc{"description"}; option_test_parser_t tp; @@ -99,7 +119,7 @@ TEST(option, bool_option_construct_short_option_twice_throws) { clapp::exception::option_exception_t); } -TEST(option, bool_option_construct_long) { +TEST(option, boolOptionConstructLongString) { const std::string long_opt_name{"long"}; const std::string opt_desc{"description"}; @@ -134,17 +154,114 @@ TEST(option, bool_option_construct_long) { option_test_parser_t::long_opt_func_t long_opt_func{ std::get<option_test_parser_t::long_opt_func_t>(long_opt_func_variant)}; - ASSERT_THAT(static_cast<bool>(opt), testing::Eq(false)); + ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true)); + ASSERT_THAT(opt.has_value(), testing::Eq(true)); + ASSERT_THAT(opt.given(), testing::Eq(false)); + ASSERT_THAT(opt.value(), testing::Eq(false)); + + long_opt_func(long_opt_name); + ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true)); + ASSERT_THAT(opt.has_value(), testing::Eq(true)); + ASSERT_THAT(opt.given(), testing::Eq(true)); + ASSERT_THAT(opt.value(), testing::Eq(true)); +} + +TEST(option, boolOptionConstructLongCstring) { + constexpr const char* long_opt_name{"long-cstring"}; + constexpr const char* opt_desc{"description"}; + + option_test_parser_t tp; + clapp::option::bool_option_t opt{tp, long_opt_name, opt_desc}; + option_test_parser_t::long_options_map_t long_options{ + tp.get_long_options()}; + option_test_parser_t::short_options_map_t short_options{ + tp.get_short_options()}; + ASSERT_THAT(tp.get_mandatory_option_descriptions().size(), testing::Eq(0)); + option_test_parser_t::option_descriptions_vec_t descs{ + tp.get_optional_option_descriptions()}; + option_test_parser_t::validate_func_vec_t validate_funcs{ + tp.get_validate_functions()}; + ASSERT_THAT(validate_funcs.size(), testing::Eq(0)); + + ASSERT_THAT(descs.size(), testing::Eq(1)); + ASSERT_THAT(descs[0].option_string, + testing::StrEq(std::string{"--"} + long_opt_name)); + ASSERT_THAT(descs[0].description, testing::StrEq(opt_desc)); + ASSERT_THAT(descs[0].option_type, + testing::Eq(option_test_parser_t::option_type_t::scalar)); + + ASSERT_THAT(short_options.size(), testing::Eq(0)); + ASSERT_THAT(long_options.size(), testing::Eq(1)); + ASSERT_THAT(long_options.count(long_opt_name), testing::Eq(1)); + option_test_parser_t::long_opt_variant_t long_opt_func_variant{ + long_options.find(long_opt_name)->second}; + ASSERT_THAT(std::holds_alternative<option_test_parser_t::long_opt_func_t>( + long_opt_func_variant), + testing::Eq(true)); + option_test_parser_t::long_opt_func_t long_opt_func{ + std::get<option_test_parser_t::long_opt_func_t>(long_opt_func_variant)}; + + ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true)); + ASSERT_THAT(opt.has_value(), testing::Eq(true)); ASSERT_THAT(opt.given(), testing::Eq(false)); ASSERT_THAT(opt.value(), testing::Eq(false)); long_opt_func(long_opt_name); ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true)); + ASSERT_THAT(opt.has_value(), testing::Eq(true)); ASSERT_THAT(opt.given(), testing::Eq(true)); ASSERT_THAT(opt.value(), testing::Eq(true)); } -TEST(option, bool_option_construct_short) { +TEST(option, boolOptionConstructLongVec) { + const std::string long_opt_name{"long2"}; + const std::string opt_desc{"description2"}; + + option_test_parser_t tp; + clapp::option::bool_option_t opt{ + tp, std::vector<std::string>{long_opt_name}, opt_desc}; + option_test_parser_t::long_options_map_t long_options{ + tp.get_long_options()}; + option_test_parser_t::short_options_map_t short_options{ + tp.get_short_options()}; + ASSERT_THAT(tp.get_mandatory_option_descriptions().size(), testing::Eq(0)); + option_test_parser_t::option_descriptions_vec_t descs{ + tp.get_optional_option_descriptions()}; + option_test_parser_t::validate_func_vec_t validate_funcs{ + tp.get_validate_functions()}; + ASSERT_THAT(validate_funcs.size(), testing::Eq(0)); + + ASSERT_THAT(descs.size(), testing::Eq(1)); + ASSERT_THAT(descs[0].option_string, + testing::StrEq(std::string{"--"} + long_opt_name)); + ASSERT_THAT(descs[0].description, testing::StrEq(opt_desc)); + ASSERT_THAT(descs[0].option_type, + testing::Eq(option_test_parser_t::option_type_t::scalar)); + + ASSERT_THAT(short_options.size(), testing::Eq(0)); + ASSERT_THAT(long_options.size(), testing::Eq(1)); + ASSERT_THAT(long_options.count(long_opt_name), testing::Eq(1)); + option_test_parser_t::long_opt_variant_t long_opt_func_variant{ + long_options.find(long_opt_name)->second}; + ASSERT_THAT(std::holds_alternative<option_test_parser_t::long_opt_func_t>( + long_opt_func_variant), + testing::Eq(true)); + option_test_parser_t::long_opt_func_t long_opt_func{ + std::get<option_test_parser_t::long_opt_func_t>(long_opt_func_variant)}; + + ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true)); + ASSERT_THAT(opt.has_value(), testing::Eq(true)); + ASSERT_THAT(opt.given(), testing::Eq(false)); + ASSERT_THAT(opt.value(), testing::Eq(false)); + + long_opt_func(long_opt_name); + ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true)); + ASSERT_THAT(opt.has_value(), testing::Eq(true)); + ASSERT_THAT(opt.given(), testing::Eq(true)); + ASSERT_THAT(opt.value(), testing::Eq(true)); +} + +TEST(option, boolOptionConstructShort) { const char short_opt_name{'o'}; const std::string opt_desc{"description"}; @@ -180,17 +297,19 @@ TEST(option, bool_option_construct_short) { std::get<option_test_parser_t::short_opt_func_t>( short_opt_func_variant)}; - ASSERT_THAT(static_cast<bool>(opt), testing::Eq(false)); + ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true)); + ASSERT_THAT(opt.has_value(), testing::Eq(true)); ASSERT_THAT(opt.given(), testing::Eq(false)); ASSERT_THAT(opt.value(), testing::Eq(false)); short_opt_func(short_opt_name); ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true)); + ASSERT_THAT(opt.has_value(), testing::Eq(true)); ASSERT_THAT(opt.given(), testing::Eq(true)); ASSERT_THAT(opt.value(), testing::Eq(true)); } -TEST(option, bool_option_construct_long_and_short) { +TEST(option, boolOptionConstructLongAndShortWithStrings) { const std::string long_opt_name1{"option-name1"}; const char short_opt_name1{'o'}; const std::string opt_desc1{"description1"}; @@ -203,8 +322,113 @@ TEST(option, bool_option_construct_long_and_short) { clapp::option::bool_option_t opt1{tp, long_opt_name1, short_opt_name1, opt_desc1}; - clapp::option::bool_option_t opt2{tp, long_opt_name2, short_opt_name2, - opt_desc2}; + clapp::option::bool_option_t opt2{ + tp, std::vector<std::string>{long_opt_name2}, + std::vector<char>{short_opt_name2}, opt_desc2}; + option_test_parser_t::long_options_map_t long_options{ + tp.get_long_options()}; + option_test_parser_t::short_options_map_t short_options{ + tp.get_short_options()}; + ASSERT_THAT(tp.get_mandatory_option_descriptions().size(), testing::Eq(0)); + option_test_parser_t::option_descriptions_vec_t descs{ + tp.get_optional_option_descriptions()}; + option_test_parser_t::validate_func_vec_t validate_funcs{ + tp.get_validate_functions()}; + ASSERT_THAT(validate_funcs.size(), testing::Eq(0)); + + ASSERT_THAT(descs.size(), testing::Eq(2)); + ASSERT_THAT(descs[0].option_string, + testing::StrEq(std::string{"-"} + short_opt_name1 + "|--" + + long_opt_name1)); + ASSERT_THAT(descs[0].description, testing::StrEq(opt_desc1)); + ASSERT_THAT(descs[0].option_type, + testing::Eq(option_test_parser_t::option_type_t::scalar)); + ASSERT_THAT(descs[1].option_string, + testing::StrEq(std::string{"-"} + short_opt_name2 + "|--" + + long_opt_name2)); + ASSERT_THAT(descs[1].description, testing::StrEq(opt_desc2)); + ASSERT_THAT(descs[1].option_type, + testing::Eq(option_test_parser_t::option_type_t::scalar)); + + ASSERT_THAT(long_options.size(), testing::Eq(2)); + ASSERT_THAT(long_options.count(long_opt_name1), testing::Eq(1)); + ASSERT_THAT(long_options.count(long_opt_name2), testing::Eq(1)); + option_test_parser_t::long_opt_variant_t long_opt_func_variant1{ + long_options.find(long_opt_name1)->second}; + option_test_parser_t::long_opt_variant_t long_opt_func_variant2{ + long_options.find(long_opt_name2)->second}; + ASSERT_THAT(std::holds_alternative<option_test_parser_t::long_opt_func_t>( + long_opt_func_variant1), + testing::Eq(true)); + ASSERT_THAT(std::holds_alternative<option_test_parser_t::long_opt_func_t>( + long_opt_func_variant2), + testing::Eq(true)); + option_test_parser_t::long_opt_func_t long_opt_func1{ + std::get<option_test_parser_t::long_opt_func_t>( + long_opt_func_variant1)}; + option_test_parser_t::long_opt_func_t long_opt_func2{ + std::get<option_test_parser_t::long_opt_func_t>( + long_opt_func_variant2)}; + + ASSERT_THAT(short_options.size(), testing::Eq(2)); + ASSERT_THAT(short_options.count(short_opt_name1), testing::Eq(1)); + ASSERT_THAT(short_options.count(short_opt_name2), testing::Eq(1)); + option_test_parser_t::short_opt_variant_t short_opt_func_variant1{ + short_options.find(short_opt_name1)->second}; + option_test_parser_t::short_opt_variant_t short_opt_func_variant2{ + short_options.find(short_opt_name2)->second}; + ASSERT_THAT(std::holds_alternative<option_test_parser_t::short_opt_func_t>( + short_opt_func_variant1), + testing::Eq(true)); + ASSERT_THAT(std::holds_alternative<option_test_parser_t::short_opt_func_t>( + short_opt_func_variant2), + testing::Eq(true)); + option_test_parser_t::short_opt_func_t short_opt_func1{ + std::get<option_test_parser_t::short_opt_func_t>( + short_opt_func_variant1)}; + option_test_parser_t::short_opt_func_t short_opt_func2{ + std::get<option_test_parser_t::short_opt_func_t>( + short_opt_func_variant2)}; + + ASSERT_THAT(static_cast<bool>(opt1), testing::Eq(true)); + ASSERT_THAT(opt1.has_value(), testing::Eq(true)); + ASSERT_THAT(opt1.given(), testing::Eq(false)); + ASSERT_THAT(opt1.value(), testing::Eq(false)); + + ASSERT_THAT(static_cast<bool>(opt2), testing::Eq(true)); + ASSERT_THAT(opt2.has_value(), testing::Eq(true)); + ASSERT_THAT(opt2.given(), testing::Eq(false)); + ASSERT_THAT(opt2.value(), testing::Eq(false)); + + long_opt_func1(long_opt_name1); + ASSERT_THAT(static_cast<bool>(opt1), testing::Eq(true)); + ASSERT_THAT(opt1.has_value(), testing::Eq(true)); + ASSERT_THAT(opt1.given(), testing::Eq(true)); + ASSERT_THAT(opt1.value(), testing::Eq(true)); + + short_opt_func2(short_opt_name2); + ASSERT_THAT(static_cast<bool>(opt2), testing::Eq(true)); + ASSERT_THAT(opt2.has_value(), testing::Eq(true)); + ASSERT_THAT(opt2.given(), testing::Eq(true)); + ASSERT_THAT(opt2.value(), testing::Eq(true)); +} + +TEST(option, boolOptionConstructLongAndShortWithCStrings) { + constexpr const char* long_opt_name1{"option-name-a"}; + constexpr char short_opt_name1{'a'}; + constexpr const char* opt_desc1{"description1"}; + + constexpr const char* long_opt_name2{"option-name-b"}; + constexpr char short_opt_name2{'b'}; + constexpr const char* opt_desc2{"description2"}; + + option_test_parser_t tp; + + clapp::option::bool_option_t opt1{tp, long_opt_name1, short_opt_name1, + opt_desc1}; + clapp::option::bool_option_t opt2{ + tp, std::vector<std::string>{long_opt_name2}, + std::vector<char>{short_opt_name2}, opt_desc2}; option_test_parser_t::long_options_map_t long_options{ tp.get_long_options()}; option_test_parser_t::short_options_map_t short_options{ @@ -270,26 +494,134 @@ TEST(option, bool_option_construct_long_and_short) { std::get<option_test_parser_t::short_opt_func_t>( short_opt_func_variant2)}; - ASSERT_THAT(static_cast<bool>(opt1), testing::Eq(false)); + ASSERT_THAT(static_cast<bool>(opt1), testing::Eq(true)); + ASSERT_THAT(opt1.has_value(), testing::Eq(true)); ASSERT_THAT(opt1.given(), testing::Eq(false)); ASSERT_THAT(opt1.value(), testing::Eq(false)); - ASSERT_THAT(static_cast<bool>(opt2), testing::Eq(false)); + ASSERT_THAT(static_cast<bool>(opt2), testing::Eq(true)); + ASSERT_THAT(opt2.has_value(), testing::Eq(true)); ASSERT_THAT(opt2.given(), testing::Eq(false)); ASSERT_THAT(opt2.value(), testing::Eq(false)); long_opt_func1(long_opt_name1); ASSERT_THAT(static_cast<bool>(opt1), testing::Eq(true)); + ASSERT_THAT(opt1.has_value(), testing::Eq(true)); ASSERT_THAT(opt1.given(), testing::Eq(true)); ASSERT_THAT(opt1.value(), testing::Eq(true)); short_opt_func2(short_opt_name2); ASSERT_THAT(static_cast<bool>(opt2), testing::Eq(true)); + ASSERT_THAT(opt2.has_value(), testing::Eq(true)); ASSERT_THAT(opt2.given(), testing::Eq(true)); ASSERT_THAT(opt2.value(), testing::Eq(true)); } -TEST(option, bool_option_found_func_long) { +TEST(option, boolOptionConstructLongAndShortWithCStrings2) { + constexpr const char* long_opt_name1{"option-name-x"}; + constexpr char short_opt_name1{'x'}; + constexpr const char* opt_desc1{"description1"}; + + constexpr const char* long_opt_name2{"option-name-y"}; + constexpr char short_opt_name2{'y'}; + constexpr const char* opt_desc2{"description2"}; + + option_test_parser_t tp; + + clapp::option::bool_option_t opt1{tp, + std::vector<std::string>{long_opt_name1}, + short_opt_name1, opt_desc1}; + clapp::option::bool_option_t opt2{ + tp, long_opt_name2, std::vector<char>{short_opt_name2}, opt_desc2}; + option_test_parser_t::long_options_map_t long_options{ + tp.get_long_options()}; + option_test_parser_t::short_options_map_t short_options{ + tp.get_short_options()}; + ASSERT_THAT(tp.get_mandatory_option_descriptions().size(), testing::Eq(0)); + option_test_parser_t::option_descriptions_vec_t descs{ + tp.get_optional_option_descriptions()}; + option_test_parser_t::validate_func_vec_t validate_funcs{ + tp.get_validate_functions()}; + ASSERT_THAT(validate_funcs.size(), testing::Eq(0)); + + ASSERT_THAT(descs.size(), testing::Eq(2)); + ASSERT_THAT(descs[0].option_string, + testing::StrEq(std::string{"-"} + short_opt_name1 + "|--" + + long_opt_name1)); + ASSERT_THAT(descs[0].description, testing::StrEq(opt_desc1)); + ASSERT_THAT(descs[0].option_type, + testing::Eq(option_test_parser_t::option_type_t::scalar)); + ASSERT_THAT(descs[1].option_string, + testing::StrEq(std::string{"-"} + short_opt_name2 + "|--" + + long_opt_name2)); + ASSERT_THAT(descs[1].description, testing::StrEq(opt_desc2)); + ASSERT_THAT(descs[1].option_type, + testing::Eq(option_test_parser_t::option_type_t::scalar)); + + ASSERT_THAT(long_options.size(), testing::Eq(2)); + ASSERT_THAT(long_options.count(long_opt_name1), testing::Eq(1)); + ASSERT_THAT(long_options.count(long_opt_name2), testing::Eq(1)); + option_test_parser_t::long_opt_variant_t long_opt_func_variant1{ + long_options.find(long_opt_name1)->second}; + option_test_parser_t::long_opt_variant_t long_opt_func_variant2{ + long_options.find(long_opt_name2)->second}; + ASSERT_THAT(std::holds_alternative<option_test_parser_t::long_opt_func_t>( + long_opt_func_variant1), + testing::Eq(true)); + ASSERT_THAT(std::holds_alternative<option_test_parser_t::long_opt_func_t>( + long_opt_func_variant2), + testing::Eq(true)); + option_test_parser_t::long_opt_func_t long_opt_func1{ + std::get<option_test_parser_t::long_opt_func_t>( + long_opt_func_variant1)}; + option_test_parser_t::long_opt_func_t long_opt_func2{ + std::get<option_test_parser_t::long_opt_func_t>( + long_opt_func_variant2)}; + + ASSERT_THAT(short_options.size(), testing::Eq(2)); + ASSERT_THAT(short_options.count(short_opt_name1), testing::Eq(1)); + ASSERT_THAT(short_options.count(short_opt_name2), testing::Eq(1)); + option_test_parser_t::short_opt_variant_t short_opt_func_variant1{ + short_options.find(short_opt_name1)->second}; + option_test_parser_t::short_opt_variant_t short_opt_func_variant2{ + short_options.find(short_opt_name2)->second}; + ASSERT_THAT(std::holds_alternative<option_test_parser_t::short_opt_func_t>( + short_opt_func_variant1), + testing::Eq(true)); + ASSERT_THAT(std::holds_alternative<option_test_parser_t::short_opt_func_t>( + short_opt_func_variant2), + testing::Eq(true)); + option_test_parser_t::short_opt_func_t short_opt_func1{ + std::get<option_test_parser_t::short_opt_func_t>( + short_opt_func_variant1)}; + option_test_parser_t::short_opt_func_t short_opt_func2{ + std::get<option_test_parser_t::short_opt_func_t>( + short_opt_func_variant2)}; + + ASSERT_THAT(static_cast<bool>(opt1), testing::Eq(true)); + ASSERT_THAT(opt1.has_value(), testing::Eq(true)); + ASSERT_THAT(opt1.given(), testing::Eq(false)); + ASSERT_THAT(opt1.value(), testing::Eq(false)); + + ASSERT_THAT(static_cast<bool>(opt2), testing::Eq(true)); + ASSERT_THAT(opt2.has_value(), testing::Eq(true)); + ASSERT_THAT(opt2.given(), testing::Eq(false)); + ASSERT_THAT(opt2.value(), testing::Eq(false)); + + long_opt_func1(long_opt_name1); + ASSERT_THAT(static_cast<bool>(opt1), testing::Eq(true)); + ASSERT_THAT(opt1.has_value(), testing::Eq(true)); + ASSERT_THAT(opt1.given(), testing::Eq(true)); + ASSERT_THAT(opt1.value(), testing::Eq(true)); + + short_opt_func2(short_opt_name2); + ASSERT_THAT(static_cast<bool>(opt2), testing::Eq(true)); + ASSERT_THAT(opt2.has_value(), testing::Eq(true)); + ASSERT_THAT(opt2.given(), testing::Eq(true)); + ASSERT_THAT(opt2.value(), testing::Eq(true)); +} + +TEST(option, boolOptionFoundFuncLong) { const std::string long_opt_name{"long"}; const std::string opt_desc{"description"}; @@ -317,7 +649,7 @@ TEST(option, bool_option_found_func_long) { ASSERT_THAT(ss.str(), testing::StrEq("this is a test")); } -TEST(option, bool_option_construct_long_mandatory) { +TEST(option, boolOptionConstructLongMandatory) { const std::string long_opt_name{"long"}; const std::string opt_desc{"description"}; @@ -354,7 +686,8 @@ TEST(option, bool_option_construct_long_mandatory) { option_test_parser_t::long_opt_func_t long_opt_func{ std::get<option_test_parser_t::long_opt_func_t>(long_opt_func_variant)}; - ASSERT_THAT(static_cast<bool>(opt), testing::Eq(false)); + ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true)); + ASSERT_THAT(opt.has_value(), testing::Eq(true)); ASSERT_THAT(opt.given(), testing::Eq(false)); ASSERT_THAT(opt.value(), testing::Eq(false)); ASSERT_THROW(validate_funcs[0](), @@ -362,12 +695,13 @@ TEST(option, bool_option_construct_long_mandatory) { long_opt_func(long_opt_name); ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true)); + ASSERT_THAT(opt.has_value(), testing::Eq(true)); ASSERT_THAT(opt.given(), testing::Eq(true)); ASSERT_THAT(opt.value(), testing::Eq(true)); ASSERT_NO_THROW(validate_funcs[0]()); } -TEST(option, help_option_construct_long) { +TEST(option, helpOptionConstructLong) { const std::string long_opt_name{"long"}; const char short_opt_name{'s'}; const std::string opt_desc{"description"}; @@ -414,25 +748,30 @@ TEST(option, help_option_construct_long) { std::get<option_test_parser_t::short_opt_func_t>( short_opt_func_variant)}; - ASSERT_THAT(static_cast<bool>(opt), testing::Eq(false)); + ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true)); + ASSERT_THAT(opt.has_value(), testing::Eq(true)); ASSERT_THAT(opt.given(), testing::Eq(false)); ASSERT_THAT(opt.value(), testing::Eq(false)); testing::internal::CaptureStdout(); ASSERT_EXIT(long_opt_func(long_opt_name), ::testing::ExitedWithCode(0), ""); - ASSERT_THAT(testing::internal::GetCapturedStdout(), - testing::StrEq("Usage: \n [-s|--long] \n\nOptional Options:\n " - "-s|--long description\n")); + ASSERT_THAT( + testing::internal::GetCapturedStdout(), + testing::StrEq( + "Usage:\nopt-test-parser [-s|--long]\n\n Optional Options:\n " + "-s|--long description\n")); testing::internal::CaptureStdout(); ASSERT_EXIT(short_opt_func(short_opt_name), ::testing::ExitedWithCode(0), ""); - ASSERT_THAT(testing::internal::GetCapturedStdout(), - testing::StrEq("Usage: \n [-s|--long] \n\nOptional Options:\n " - "-s|--long description\n")); + ASSERT_THAT( + testing::internal::GetCapturedStdout(), + testing::StrEq( + "Usage:\nopt-test-parser [-s|--long]\n\n Optional Options:\n " + "-s|--long description\n")); } -TEST(option, count_option_construct_long) { +TEST(option, countOptionConstructLong) { const std::string long_opt_name{"long"}; const std::string opt_desc{"description 2"}; @@ -467,22 +806,25 @@ TEST(option, count_option_construct_long) { option_test_parser_t::long_opt_func_t long_opt_func{ std::get<option_test_parser_t::long_opt_func_t>(long_opt_func_variant)}; - ASSERT_THAT(static_cast<bool>(opt), testing::Eq(false)); + ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true)); + ASSERT_THAT(opt.has_value(), testing::Eq(true)); ASSERT_THAT(opt.given(), testing::Eq(false)); ASSERT_THAT(opt.value(), testing::Eq(0)); long_opt_func(long_opt_name); ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true)); + ASSERT_THAT(opt.has_value(), testing::Eq(true)); ASSERT_THAT(opt.given(), testing::Eq(true)); ASSERT_THAT(opt.value(), testing::Eq(1)); long_opt_func(long_opt_name); ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true)); + ASSERT_THAT(opt.has_value(), testing::Eq(true)); ASSERT_THAT(opt.given(), testing::Eq(true)); ASSERT_THAT(opt.value(), testing::Eq(2)); } -TEST(option, count_option_construct_short) { +TEST(option, countOptionConstructShort) { const char short_opt_name{'i'}; const std::string opt_desc{"description 3"}; @@ -518,7 +860,8 @@ TEST(option, count_option_construct_short) { std::get<option_test_parser_t::short_opt_func_t>( short_opt_func_variant)}; - ASSERT_THAT(static_cast<bool>(opt), testing::Eq(false)); + ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true)); + ASSERT_THAT(opt.has_value(), testing::Eq(true)); ASSERT_THAT(opt.given(), testing::Eq(false)); ASSERT_THAT(opt.value(), testing::Eq(0)); @@ -526,11 +869,12 @@ TEST(option, count_option_construct_short) { short_opt_func(short_opt_name); short_opt_func(short_opt_name); ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true)); + ASSERT_THAT(opt.has_value(), testing::Eq(true)); ASSERT_THAT(opt.given(), testing::Eq(true)); ASSERT_THAT(opt.value(), testing::Eq(3)); } -TEST(option, count_option_construct_long_and_short) { +TEST(option, countOptionConstructLongAndShort) { const std::string long_opt_name1{"option-name1"}; const char short_opt_name1{'o'}; const std::string opt_desc1{"description1"}; @@ -610,17 +954,20 @@ TEST(option, count_option_construct_long_and_short) { std::get<option_test_parser_t::short_opt_func_t>( short_opt_func_variant2)}; - ASSERT_THAT(static_cast<bool>(opt1), testing::Eq(false)); + ASSERT_THAT(static_cast<bool>(opt1), testing::Eq(true)); + ASSERT_THAT(opt1.has_value(), testing::Eq(true)); ASSERT_THAT(opt1.given(), testing::Eq(false)); ASSERT_THAT(opt1.value(), testing::Eq(0)); - ASSERT_THAT(static_cast<bool>(opt2), testing::Eq(false)); + ASSERT_THAT(static_cast<bool>(opt2), testing::Eq(true)); + ASSERT_THAT(opt2.has_value(), testing::Eq(true)); ASSERT_THAT(opt2.given(), testing::Eq(false)); ASSERT_THAT(opt2.value(), testing::Eq(0)); long_opt_func1(long_opt_name1); short_opt_func1(short_opt_name1); ASSERT_THAT(static_cast<bool>(opt1), testing::Eq(true)); + ASSERT_THAT(opt1.has_value(), testing::Eq(true)); ASSERT_THAT(opt1.given(), testing::Eq(true)); ASSERT_THAT(opt1.value(), testing::Eq(2)); @@ -629,16 +976,19 @@ TEST(option, count_option_construct_long_and_short) { long_opt_func2(long_opt_name2); short_opt_func2(short_opt_name2); ASSERT_THAT(static_cast<bool>(opt2), testing::Eq(true)); + ASSERT_THAT(opt2.has_value(), testing::Eq(true)); ASSERT_THAT(opt2.given(), testing::Eq(true)); ASSERT_THAT(opt2.value(), testing::Eq(4)); } -TEST(option, count_option_construct_short_min_max) { +TEST(option, countOptionConstructShortMinMax) { const char short_opt_name{'i'}; const std::string opt_desc{"description 3"}; + constexpr std::uint32_t min_value{0}; + constexpr std::uint32_t max_value{5}; option_test_parser_t tp; - clapp::value::min_max_value_t<std::uint32_t> min_max{0, 5}; + clapp::value::min_max_value_t<std::uint32_t> min_max{min_value, max_value}; clapp::option::count_option_t opt{tp, short_opt_name, opt_desc, min_max}; option_test_parser_t::long_options_map_t long_options{ tp.get_long_options()}; @@ -672,7 +1022,8 @@ TEST(option, count_option_construct_short_min_max) { std::get<option_test_parser_t::short_opt_func_t>( short_opt_func_variant)}; - ASSERT_THAT(static_cast<bool>(opt), testing::Eq(false)); + ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true)); + ASSERT_THAT(opt.has_value(), testing::Eq(true)); ASSERT_THAT(opt.given(), testing::Eq(false)); ASSERT_THAT(opt.value(), testing::Eq(0)); ASSERT_NO_THROW(validate_funcs[0]()); @@ -682,24 +1033,27 @@ TEST(option, count_option_construct_short_min_max) { short_opt_func(short_opt_name); short_opt_func(short_opt_name); ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true)); + ASSERT_THAT(opt.has_value(), testing::Eq(true)); ASSERT_THAT(opt.given(), testing::Eq(true)); ASSERT_THAT(opt.value(), testing::Eq(4)); ASSERT_NO_THROW(validate_funcs[0]()); short_opt_func(short_opt_name); ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true)); + ASSERT_THAT(opt.has_value(), testing::Eq(true)); ASSERT_THAT(opt.given(), testing::Eq(true)); ASSERT_THAT(opt.value(), testing::Eq(5)); ASSERT_NO_THROW(validate_funcs[0]()); short_opt_func(short_opt_name); ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true)); + ASSERT_THAT(opt.has_value(), testing::Eq(true)); ASSERT_THAT(opt.given(), testing::Eq(true)); ASSERT_THAT(opt.value(), testing::Eq(6)); ASSERT_THROW(validate_funcs[0](), clapp::exception::out_of_range_t); } -TEST(option, count_option_construct_long_default) { +TEST(option, countOptionConstructLongDefault) { const std::string long_opt_name{"long"}; const std::string opt_desc{"desc"}; @@ -739,16 +1093,18 @@ TEST(option, count_option_construct_long_default) { std::get<option_test_parser_t::long_opt_func_t>(long_opt_func_variant)}; ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true)); + ASSERT_THAT(opt.has_value(), testing::Eq(true)); ASSERT_THAT(opt.given(), testing::Eq(false)); ASSERT_THAT(opt.value(), testing::Eq(4)); long_opt_func(long_opt_name); ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true)); + ASSERT_THAT(opt.has_value(), testing::Eq(true)); ASSERT_THAT(opt.given(), testing::Eq(true)); ASSERT_THAT(opt.value(), testing::Eq(5)); } -TEST(option, count_option_found_func_short) { +TEST(option, countOptionFoundFuncShort) { const char short_opt_name{'c'}; const std::string opt_desc{"description"}; @@ -774,7 +1130,8 @@ TEST(option, count_option_found_func_short) { std::get<option_test_parser_t::short_opt_func_t>( short_opt_func_variant)}; - ASSERT_THAT(static_cast<bool>(opt), testing::Eq(false)); + ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true)); + ASSERT_THAT(opt.has_value(), testing::Eq(true)); ASSERT_THAT(opt.given(), testing::Eq(false)); ASSERT_THAT(opt.value(), testing::Eq(0)); ASSERT_THAT(ss.str(), testing::StrEq("")); @@ -783,11 +1140,12 @@ TEST(option, count_option_found_func_short) { ASSERT_THAT(ss.str(), testing::StrEq("this is another test")); ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true)); + ASSERT_THAT(opt.has_value(), testing::Eq(true)); ASSERT_THAT(opt.given(), testing::Eq(true)); ASSERT_THAT(opt.value(), testing::Eq(1)); } -TEST(option, count_option_construct_short_mandatory) { +TEST(option, countOptionConstructShortMandatory) { const char short_opt_name{'i'}; const std::string opt_desc{"description 3"}; @@ -825,7 +1183,8 @@ TEST(option, count_option_construct_short_mandatory) { std::get<option_test_parser_t::short_opt_func_t>( short_opt_func_variant)}; - ASSERT_THAT(static_cast<bool>(opt), testing::Eq(false)); + ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true)); + ASSERT_THAT(opt.has_value(), testing::Eq(true)); ASSERT_THAT(opt.given(), testing::Eq(false)); ASSERT_THAT(opt.value(), testing::Eq(0)); ASSERT_THROW(validate_funcs[0](), clapp::exception::option_exception_t); @@ -834,12 +1193,13 @@ TEST(option, count_option_construct_short_mandatory) { short_opt_func(short_opt_name); short_opt_func(short_opt_name); ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true)); + ASSERT_THAT(opt.has_value(), testing::Eq(true)); ASSERT_THAT(opt.given(), testing::Eq(true)); ASSERT_THAT(opt.value(), testing::Eq(3)); ASSERT_NO_THROW(validate_funcs[0]()); } -TEST(option, string_param_option_construct_short) { +TEST(option, stringParamOptionConstructShort) { const char short_opt_name{'s'}; const std::string opt_desc{"string description"}; @@ -877,16 +1237,18 @@ TEST(option, string_param_option_construct_short) { short_opt_func_variant)}; ASSERT_THAT(static_cast<bool>(opt), testing::Eq(false)); + ASSERT_THAT(opt.has_value(), testing::Eq(false)); ASSERT_THAT(opt.given(), testing::Eq(false)); ASSERT_THROW(opt.value(), clapp::exception::value_undefined_t); short_opt_func(short_opt_name, "string"); ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true)); + ASSERT_THAT(opt.has_value(), testing::Eq(true)); ASSERT_THAT(opt.given(), testing::Eq(true)); ASSERT_THAT(opt.value(), testing::StrEq("string")); } -TEST(option, string_param_option_found_func_short) { +TEST(option, stringParamOptionFoundFuncShort) { const char short_opt_name{'s'}; const std::string opt_desc{"description"}; @@ -915,17 +1277,19 @@ TEST(option, string_param_option_found_func_short) { short_opt_func_variant)}; ASSERT_THAT(static_cast<bool>(opt), testing::Eq(false)); + ASSERT_THAT(opt.has_value(), testing::Eq(false)); ASSERT_THAT(opt.given(), testing::Eq(false)); ASSERT_THAT(ss.str(), testing::StrEq("")); short_opt_func(short_opt_name, "param"); ASSERT_THAT(ss.str(), testing::StrEq("this is another test for params")); ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true)); + ASSERT_THAT(opt.has_value(), testing::Eq(true)); ASSERT_THAT(opt.given(), testing::Eq(true)); ASSERT_THAT(opt.value(), testing::StrEq("param")); } -TEST(option, string_param_option_construct_long_mandatory) { +TEST(option, stringParamOptionConstructLongMandatory) { const std::string long_opt_name{"string"}; const std::string opt_desc{"string description"}; @@ -965,18 +1329,20 @@ TEST(option, string_param_option_construct_long_mandatory) { long_opt_func_variant)}; ASSERT_THAT(static_cast<bool>(opt), testing::Eq(false)); + ASSERT_THAT(opt.has_value(), testing::Eq(false)); ASSERT_THAT(opt.given(), testing::Eq(false)); ASSERT_THROW(opt.value(), clapp::exception::value_undefined_t); ASSERT_THROW(validate_funcs[0](), clapp::exception::option_exception_t); long_opt_func(long_opt_name, "string"); ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true)); + ASSERT_THAT(opt.has_value(), testing::Eq(true)); ASSERT_THAT(opt.given(), testing::Eq(true)); ASSERT_THAT(opt.value(), testing::StrEq("string")); ASSERT_NO_THROW(validate_funcs[0]()); } -TEST(option, int_param_option_construct_long_and_short_mandatory) { +TEST(option, intParamOptionConstructLongAndShortMandatory) { const std::string long_opt_name{"int"}; const char short_opt_name{'i'}; const std::string opt_desc{"int description"}; @@ -1047,24 +1413,27 @@ TEST(option, int_param_option_construct_long_and_short_mandatory) { long_opt_func_variant)}; ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true)); + ASSERT_THAT(opt.has_value(), testing::Eq(true)); ASSERT_THAT(opt.given(), testing::Eq(false)); ASSERT_THAT(opt.value(), testing::Eq(default_value)); ASSERT_THROW(validate_funcs[0](), clapp::exception::option_exception_t); long_opt_func(long_opt_name, "123"); ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true)); + ASSERT_THAT(opt.has_value(), testing::Eq(true)); ASSERT_THAT(opt.given(), testing::Eq(true)); ASSERT_THAT(opt.value(), testing::Eq(123)); ASSERT_THROW(validate_funcs[0](), clapp::exception::out_of_range_t); short_opt_func(short_opt_name, "0x2f"); ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true)); + ASSERT_THAT(opt.has_value(), testing::Eq(true)); ASSERT_THAT(opt.given(), testing::Eq(true)); ASSERT_THAT(opt.value(), testing::Eq(0x2f)); ASSERT_NO_THROW(validate_funcs[0]()); } -TEST(option, vector_string_param_option_construct_with_default_throws) { +TEST(option, vectorStringParamOptionConstructWithDefaultThrows) { const char short_opt_name{'s'}; const std::string opt_desc{"string description"}; @@ -1076,7 +1445,7 @@ TEST(option, vector_string_param_option_construct_with_default_throws) { clapp::exception::option_param_exception_t); } -TEST(option, vector_string_param_option_construct_short) { +TEST(option, vectorStringParamOptionConstructShort) { const char short_opt_name{'s'}; const std::string opt_desc{"string description"}; @@ -1116,11 +1485,13 @@ TEST(option, vector_string_param_option_construct_short) { short_opt_func_variant)}; ASSERT_THAT(static_cast<bool>(opt), testing::Eq(false)); + ASSERT_THAT(opt.has_value(), testing::Eq(false)); ASSERT_THAT(opt.given(), testing::Eq(false)); ASSERT_THAT(opt.value(), testing::Eq((std::vector<std::string>{}))); short_opt_func(short_opt_name, "string"); ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true)); + ASSERT_THAT(opt.has_value(), testing::Eq(true)); ASSERT_THAT(opt.given(), testing::Eq(true)); std::vector<std::string> vec{opt.value()}; ASSERT_THAT(vec.size(), testing::Eq(1)); @@ -1128,6 +1499,7 @@ TEST(option, vector_string_param_option_construct_short) { short_opt_func(short_opt_name, "string2"); ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true)); + ASSERT_THAT(opt.has_value(), testing::Eq(true)); ASSERT_THAT(opt.given(), testing::Eq(true)); vec = opt.value(); ASSERT_THAT(vec.size(), testing::Eq(2)); @@ -1135,7 +1507,7 @@ TEST(option, vector_string_param_option_construct_short) { ASSERT_THAT(vec[1], testing::StrEq("string2")); } -TEST(option, vector_string_param_option_construct_long_mandatory) { +TEST(option, vectorStringParamOptionConstructLongMandatory) { const std::string long_opt_name{"string"}; const std::string opt_desc{"string description"}; @@ -1176,6 +1548,7 @@ TEST(option, vector_string_param_option_construct_long_mandatory) { long_opt_func_variant)}; ASSERT_THAT(static_cast<bool>(opt), testing::Eq(false)); + ASSERT_THAT(opt.has_value(), testing::Eq(false)); ASSERT_THAT(opt.given(), testing::Eq(false)); ASSERT_THAT(opt.value(), testing::Eq((std::vector<std::string>{}))); ASSERT_THROW(validate_funcs[0](), @@ -1183,6 +1556,7 @@ TEST(option, vector_string_param_option_construct_long_mandatory) { long_opt_func(long_opt_name, "string"); ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true)); + ASSERT_THAT(opt.has_value(), testing::Eq(true)); ASSERT_THAT(opt.given(), testing::Eq(true)); std::vector<std::string> vec{opt.value()}; ASSERT_THAT(vec.size(), testing::Eq(1)); @@ -1190,6 +1564,7 @@ TEST(option, vector_string_param_option_construct_long_mandatory) { long_opt_func(long_opt_name, "string2"); ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true)); + ASSERT_THAT(opt.has_value(), testing::Eq(true)); ASSERT_THAT(opt.given(), testing::Eq(true)); vec = opt.value(); ASSERT_THAT(vec.size(), testing::Eq(2)); @@ -1198,6 +1573,7 @@ TEST(option, vector_string_param_option_construct_long_mandatory) { long_opt_func(long_opt_name, "string3"); ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true)); + ASSERT_THAT(opt.has_value(), testing::Eq(true)); ASSERT_THAT(opt.given(), testing::Eq(true)); vec = opt.value(); ASSERT_THAT(vec.size(), testing::Eq(3)); @@ -1206,7 +1582,7 @@ TEST(option, vector_string_param_option_construct_long_mandatory) { ASSERT_THAT(vec[2], testing::StrEq("string3")); } -TEST(option, vector_string_param_option_found_func_short) { +TEST(option, vectorStringParamOptionFoundFuncShort) { const std::string long_opt_name{"long"}; const std::string opt_desc{"description"}; @@ -1234,6 +1610,7 @@ TEST(option, vector_string_param_option_found_func_short) { long_opt_func_variant)}; ASSERT_THAT(static_cast<bool>(opt), testing::Eq(false)); + ASSERT_THAT(opt.has_value(), testing::Eq(false)); ASSERT_THAT(opt.given(), testing::Eq(false)); ASSERT_THAT(ss.str(), testing::StrEq("")); ASSERT_THAT(opt.value().size(), testing::Eq(0)); @@ -1241,16 +1618,19 @@ TEST(option, vector_string_param_option_found_func_short) { ASSERT_THAT(ss.str(), testing::StrEq("this is another test for params")); ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true)); + ASSERT_THAT(opt.has_value(), testing::Eq(true)); ASSERT_THAT(opt.given(), testing::Eq(true)); ASSERT_THAT(opt.value().size(), testing::Eq(1)); ASSERT_THAT(opt.value()[0], testing::StrEq("param")); } -TEST(option, vector_string_param_option_construct_long_and_short_min_max) { +TEST(option, vectorStringParamOptionConstructLongAndShortMinMax) { const char short_opt_name{'s'}; const std::string long_opt_name{"long"}; const std::string opt_desc{"int description"}; - clapp::value::min_max_value_t<std::uint8_t> min_max{10, 20}; + const int min_value{10}; + const int max_value{20}; + clapp::value::min_max_value_t<std::uint8_t> min_max{min_value, max_value}; option_test_parser_t tp; clapp::option::vector_uint8_param_option_t opt{ @@ -1301,12 +1681,14 @@ TEST(option, vector_string_param_option_construct_long_and_short_min_max) { short_opt_func_variant)}; ASSERT_THAT(static_cast<bool>(opt), testing::Eq(false)); + ASSERT_THAT(opt.has_value(), testing::Eq(false)); ASSERT_THAT(opt.given(), testing::Eq(false)); ASSERT_THAT(opt.value(), testing::Eq((std::vector<std::uint8_t>{}))); ASSERT_NO_THROW(validate_funcs[0]()); short_opt_func(short_opt_name, "0x12"); ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true)); + ASSERT_THAT(opt.has_value(), testing::Eq(true)); ASSERT_THAT(opt.given(), testing::Eq(true)); std::vector<std::uint8_t> vec{opt.value()}; ASSERT_THAT(vec.size(), testing::Eq(1)); @@ -1315,6 +1697,7 @@ TEST(option, vector_string_param_option_construct_long_and_short_min_max) { long_opt_func(long_opt_name, "12"); ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true)); + ASSERT_THAT(opt.has_value(), testing::Eq(true)); ASSERT_THAT(opt.given(), testing::Eq(true)); vec = opt.value(); ASSERT_THAT(vec.size(), testing::Eq(2)); @@ -1333,7 +1716,7 @@ TEST(option, vector_string_param_option_construct_long_and_short_min_max) { ASSERT_THROW(validate_funcs[0](), clapp::exception::out_of_range_t); } -TEST(option, invalid_long_option_construct) { +TEST(option, invalidLongOptionConstruct) { const std::string long_opt_name1{"long\n"}; const std::string long_opt_name2{"long\t"}; const std::string long_opt_name3{"long\r"}; @@ -1377,7 +1760,7 @@ TEST(option, invalid_long_option_construct) { clapp::exception::option_exception_t); } -TEST(option, invalid_short_option_construct) { +TEST(option, invalidShortOptionConstruct) { const char short_opt_name1{'\n'}; const char short_opt_name2{'\t'}; const char short_opt_name3{'\r'}; @@ -1421,95 +1804,70 @@ TEST(option, invalid_short_option_construct) { clapp::exception::option_exception_t); } -struct mock_t { - MOCK_METHOD(std::int32_t, value_func, ()); - MOCK_METHOD(bool, given_func, ()); - MOCK_METHOD(bool, has_value_func, ()); - MOCK_METHOD(void, validate_func, - (const std::int32_t&, const std::string& option_string)); -}; - -TEST(option, gen_opt_validate_func) { +TEST(option, genOptValidateFunc) { using int32_value_func_t = std::function<std::int32_t(void)>; using int32_validate_func_t = std::function<void( const std::int32_t&, const std::string& option_string)>; { - mock_t mock; std::optional<option_test_parser_t::validate_func_t> validate_func{ clapp::option::gen_opt_validate_func<std::int32_t, int32_value_func_t>( - std::nullopt, std::nullopt, - [&mock]() { return mock.given_func(); }, + std::nullopt, std::nullopt, []() { return false; }, std::vector<int32_validate_func_t>{}, "option string", option_test_parser_t::purpose_t::mandatory)}; ASSERT_THAT(validate_func.has_value(), testing::Eq(true)); - EXPECT_CALL(mock, given_func()) - .Times(1) - .WillOnce(testing::Return(false)); ASSERT_THROW((*validate_func)(), clapp::exception::option_exception_t); } { - mock_t mock; std::optional<option_test_parser_t::validate_func_t> validate_func{ clapp::option::gen_opt_validate_func<std::int32_t, int32_value_func_t>( - std::nullopt, [&mock]() { return mock.has_value_func(); }, - [&mock]() { return mock.given_func(); }, - std::vector<int32_validate_func_t>{}, "option string", - option_test_parser_t::purpose_t::mandatory)}; + std::nullopt, + []() -> bool { throw std::runtime_error{"unexpected call"}; }, + []() { return true; }, std::vector<int32_validate_func_t>{}, + "option string", option_test_parser_t::purpose_t::mandatory)}; ASSERT_THAT(validate_func.has_value(), testing::Eq(true)); - EXPECT_CALL(mock, given_func()) - .Times(1) - .WillOnce(testing::Return(true)); ASSERT_NO_THROW((*validate_func)()); } { - mock_t mock; + constexpr std::int32_t return_value{10}; std::optional<option_test_parser_t::validate_func_t> validate_func{ clapp::option::gen_opt_validate_func<std::int32_t, int32_value_func_t>( - [&mock]() { return mock.value_func(); }, - [&mock]() { return mock.has_value_func(); }, - [&mock]() { return mock.given_func(); }, + []() { return return_value; }, []() { return true; }, + []() { return true; }, std::vector<int32_validate_func_t>{ - [&mock](const std::int32_t& value, - const std::string& option_string) { - mock.validate_func(value, option_string); + [](const std::int32_t& value, + const std::string& option_string) { + if (option_string != "option string") { + throw std::runtime_error{"option_string invalid"}; + } + if (value != return_value) { + throw std::runtime_error{"value invalid"}; + } }}, "option string", option_test_parser_t::purpose_t::mandatory)}; ASSERT_THAT(validate_func.has_value(), testing::Eq(true)); - EXPECT_CALL(mock, value_func()).Times(1).WillOnce(testing::Return(10)); - EXPECT_CALL(mock, has_value_func()) - .Times(1) - .WillOnce(testing::Return(true)); - EXPECT_CALL(mock, given_func()) - .Times(1) - .WillOnce(testing::Return(true)); - EXPECT_CALL(mock, validate_func(10, testing::StrEq("option string"))) - .Times(1); ASSERT_NO_THROW((*validate_func)()); } { - mock_t mock; std::optional<option_test_parser_t::validate_func_t> validate_func{ clapp::option::gen_opt_validate_func<std::int32_t, int32_value_func_t>( - std::nullopt, std::nullopt, - [&mock]() { return mock.given_func(); }, + std::nullopt, std::nullopt, []() { return true; }, std::vector<int32_validate_func_t>{ - [&mock](const std::int32_t& value, - const std::string& option_string) { - mock.validate_func(value, option_string); + [](const std::int32_t& /*value*/, + const std::string& option_string) { + if (option_string != "option string") { + throw std::runtime_error{"option_string invalid"}; + } }}, "option string", option_test_parser_t::purpose_t::mandatory)}; ASSERT_THAT(validate_func.has_value(), testing::Eq(true)); - EXPECT_CALL(mock, given_func()) - .Times(1) - .WillOnce(testing::Return(true)); ASSERT_THROW((*validate_func)(), clapp::exception::option_param_exception_t); } diff --git a/tests/parser.cpp b/tests/parser.cpp index 24bed075..37fa64cb 100644 --- a/tests/parser.cpp +++ b/tests/parser.cpp @@ -14,21 +14,44 @@ class empty_basic_parser_t : public clapp::basic_parser_t { public: using clapp::basic_parser_t::basic_parser_t; ~empty_basic_parser_t() override; + [[nodiscard]] std::string gen_short_line_prefix() const override; + void set_max_option_string_size(std::size_t max_option_size) override; + [[nodiscard]] std::size_t get_max_option_string_size() const override; + + private: + std::size_t max_option_string_size{0}; }; empty_basic_parser_t::~empty_basic_parser_t() = default; +[[nodiscard]] std::string empty_basic_parser_t::gen_short_line_prefix() const { + return "arg-test-parser" + gen_short_line(); +} + +void empty_basic_parser_t::set_max_option_string_size( + const std::size_t max_option_size) { + max_option_string_size = max_option_size; +} + +[[nodiscard]] std::size_t empty_basic_parser_t::get_max_option_string_size() + const { + return max_option_string_size; +} + class simple_test_parser_t : public clapp::basic_parser_t { public: using clapp::basic_parser_t::basic_parser_t; ~simple_test_parser_t() override; + static constexpr std::int32_t min_int{10}; + static constexpr std::int32_t max_int{200}; + clapp::option::bool_option_t bool_option{*this, "bool", 'b', "Bool option."}; clapp::option::int32_param_option_t int_option{ *this, "int", 'i', "Int option.", - clapp::value::min_max_value_t<std::int32_t>{10, 200}}; + clapp::value::min_max_value_t<std::int32_t>{min_int, max_int}}; clapp::argument::string_argument_t string_arg{*this, "arg-name", "Arg desc"}; @@ -36,10 +59,31 @@ class simple_test_parser_t : public clapp::basic_parser_t { clapp::argument::variadic_string_argument_t variadic_string_arg{ *this, "variadic-arg-name", "Variadic arg desc", clapp::basic_parser_t::purpose_t::optional}; + + [[nodiscard]] std::string gen_short_line_prefix() const override; + void set_max_option_string_size(std::size_t max_option_size) override; + [[nodiscard]] std::size_t get_max_option_string_size() const override; + + private: + std::size_t max_option_string_size{0}; }; simple_test_parser_t::~simple_test_parser_t() = default; +[[nodiscard]] std::string simple_test_parser_t::gen_short_line_prefix() const { + return "simple-test-parser" + gen_short_line(); +} + +void simple_test_parser_t::set_max_option_string_size( + const std::size_t max_option_size) { + max_option_string_size = max_option_size; +} + +[[nodiscard]] std::size_t simple_test_parser_t::get_max_option_string_size() + const { + return max_option_string_size; +} + class simple_test_parser2_t : public clapp::basic_parser_t { public: using clapp::basic_parser_t::basic_parser_t; @@ -52,21 +96,45 @@ class simple_test_parser2_t : public clapp::basic_parser_t { clapp::argument::string_argument_t string_arg{ *this, "arg-name", "Arg desc", clapp::basic_parser_t::purpose_t::optional}; + + [[nodiscard]] std::string gen_short_line_prefix() const override; + void set_max_option_string_size(std::size_t max_option_size) override; + [[nodiscard]] std::size_t get_max_option_string_size() const override; + + private: + std::size_t max_option_string_size{0}; }; simple_test_parser2_t::~simple_test_parser2_t() = default; +[[nodiscard]] std::string simple_test_parser2_t::gen_short_line_prefix() const { + return "simple-test-parser2" + gen_short_line(); +} + +void simple_test_parser2_t::set_max_option_string_size( + const std::size_t max_option_size) { + max_option_string_size = max_option_size; +} + +[[nodiscard]] std::size_t simple_test_parser2_t::get_max_option_string_size() + const { + return max_option_string_size; +} + class simple_test_parser3_t : public clapp::basic_parser_t { public: using clapp::basic_parser_t::basic_parser_t; ~simple_test_parser3_t() override; + static constexpr std::int32_t min_int{10}; + static constexpr std::int32_t max_int{200}; + clapp::option::vector_int64_param_option_t int_option{ *this, "int", 'i', "Int option.", - clapp::value::min_max_value_t<std::int64_t>{10, 200}, + clapp::value::min_max_value_t<std::int64_t>{min_int, max_int}, clapp::basic_parser_t::purpose_t::mandatory}; clapp::option::vector_string_param_option_t string_option{ @@ -76,15 +144,40 @@ class simple_test_parser3_t : public clapp::basic_parser_t { clapp::argument::variadic_string_argument_t variadic_string_arg{ *this, "variadic-arg-name", "Variadic arg desc", clapp::basic_parser_t::purpose_t::mandatory}; + + [[nodiscard]] std::string gen_short_line_prefix() const override; + void set_max_option_string_size(std::size_t max_option_size) override; + [[nodiscard]] std::size_t get_max_option_string_size() const override; + + private: + std::size_t max_option_string_size{0}; }; simple_test_parser3_t::~simple_test_parser3_t() = default; +[[nodiscard]] std::string simple_test_parser3_t::gen_short_line_prefix() const { + return "simple-test-parser3" + gen_short_line(); +} + +void simple_test_parser3_t::set_max_option_string_size( + const std::size_t max_option_size) { + max_option_string_size = max_option_size; +} + +[[nodiscard]] std::size_t simple_test_parser3_t::get_max_option_string_size() + const { + return max_option_string_size; +} + class sub_parser_container_t : public clapp::basic_parser_t { public: using clapp::basic_parser_t::basic_parser_t; ~sub_parser_container_t() override; + [[nodiscard]] std::string gen_short_line_prefix() const override; + void set_max_option_string_size(std::size_t max_option_size) override; + [[nodiscard]] std::size_t get_max_option_string_size() const override; + clapp::option::bool_option_t bool_option{*this, "bool", 'b', "Bool option."}; @@ -110,22 +203,39 @@ class sub_parser_container_t : public clapp::basic_parser_t { }; simple_sub_parser_t sub_parser{*this, "sub-parser", "Sub parser desc"}; + + private: + std::size_t max_option_string_size{0}; }; sub_parser_container_t::simple_sub_parser_t::~simple_sub_parser_t() = default; sub_parser_container_t::~sub_parser_container_t() = default; -TEST(parser, construct_empty_basic_parser) { empty_basic_parser_t ebp; } +[[nodiscard]] std::string sub_parser_container_t::gen_short_line_prefix() + const { + return "sub_parser_container" + gen_short_line(); +} + +void sub_parser_container_t::set_max_option_string_size( + const std::size_t max_option_size) { + max_option_string_size = max_option_size; +} -TEST(parser, construct_empty_basic_parser_and_parse_empty_arguments) { +[[nodiscard]] std::size_t sub_parser_container_t::get_max_option_string_size() + const { + return max_option_string_size; +} + +TEST(parser, constructEmptyBasicParser) { empty_basic_parser_t ebp; } + +TEST(parser, constructEmptyBasicParserAndParseEmptyArguments) { constexpr const char* const argv[]{nullptr}; const clapp::parser::arg_t arg{parser_make_arg_t(argv)}; empty_basic_parser_t ebp; ASSERT_NO_THROW(ebp.parse(arg.cbegin(), arg.cend())); } -TEST(parser, - construct_empty_basic_parser_and_parse_and_validate_empty_arguments) { +TEST(parser, constructEmptyBasicParserAndParseAndValidateEmptyArguments) { constexpr const char* const argv[]{nullptr}; const clapp::parser::arg_t arg{parser_make_arg_t(argv)}; empty_basic_parser_t ebp; @@ -133,85 +243,116 @@ TEST(parser, ASSERT_NO_THROW(ebp.validate()); } -TEST(parser, construct_empty_basic_parser_and_gen_help_prefix) { +TEST(parser, constructEmptyBasicParserAndGenUsagePrefix) { + empty_basic_parser_t ebp; + ASSERT_THAT(ebp.gen_usage_prefix(), testing::StrEq("Usage:\n")); +} + +TEST(parser, constructEmptyBasicParserAndGenHelpMessage) { + empty_basic_parser_t ebp; + ASSERT_THAT(ebp.gen_help_msg(255), testing::StrEq("arg-test-parser\n")); +} + +TEST(parser, constructEmptyBasicParserIsActiveIsTrue) { empty_basic_parser_t ebp; - ASSERT_THAT(ebp.gen_help_prefix(), testing::StrEq("Usage: \n")); + ASSERT_THAT(ebp.is_active(), testing::Eq(true)); } -TEST(parser, construct_empty_basic_parser_and_gen_help_message) { +TEST(parser, constructEmptyBasicParserGetActiveReturnsThisRef) { empty_basic_parser_t ebp; - ASSERT_THAT(ebp.gen_help_msg(), testing::StrEq("\n")); + ASSERT_THAT(&ebp.get_active_parser(), testing::Eq(&ebp)); } -TEST(parser, gen_func_print_help_and_exit) { +TEST(parser, genFuncPrintHelpAndExit) { empty_basic_parser_t ebp; clapp::value::found_func_t ff{ ebp.gen_func_print_help_and_exit(EXIT_SUCCESS)}; testing::internal::CaptureStdout(); ASSERT_EXIT(ff.found(), ::testing::ExitedWithCode(0), ""); std::string output = testing::internal::GetCapturedStdout(); - ASSERT_THAT(output, testing::StrEq("Usage: \n \n")); + ASSERT_THAT(output, testing::StrEq("Usage:\narg-test-parser\n")); } -TEST(parser, construct_simple_test_parser_and_gen_help_message) { +TEST(parser, constructSimpleTestParserAndGenHelpMessage) { simple_test_parser_t stp; + constexpr std::size_t max_option_string_size{25}; + stp.set_max_option_string_size(max_option_string_size); ASSERT_THAT( - stp.gen_help_msg(), + stp.gen_help_msg(255), testing::StrEq( - "[-b|--bool] [-i|--int=<arg>] <arg-name> [<variadic-arg-name>...] " - "\n\nMandatory Arguments:\n arg-name Arg desc\n\n" - "Optional Arguments:\n variadic-arg-name Variadic arg desc " - "(variadic argument)\n\n" - "Optional Options:\n -b|--bool Bool option.\n" - " -i|--int=<arg> Int option. (constraint: [10,200])\n")); + "simple-test-parser <arg-name> [<variadic-arg-name>...] " + "[-b|--bool] [-i|--int=<arg>]\n\n Mandatory Arguments:\n " + "arg-name Arg desc\n\n Optional Arguments:\n " + "variadic-arg-name Variadic arg desc (variadic " + "argument)\n\n Optional Options:\n -b|--bool " + "Bool option.\n -i|--int=<arg> Int option. " + "(constraint: [10,200])\n")); } -TEST(parser, construct_simple_test_parser2_and_gen_help_message) { +TEST(parser, constructSimpleTestParser2AndGenHelpMessage) { simple_test_parser2_t stp; + constexpr std::size_t max_option_string_size{20}; + stp.set_max_option_string_size(max_option_string_size); ASSERT_THAT( - stp.gen_help_msg(), - testing::StrEq("-c|--count [<arg-name>] \n\n" - "Optional Arguments:\n arg-name Arg desc\n\n" - "Mandatory Options:\n -c|--count Count option.\n")); + stp.gen_help_msg(255), + testing::StrEq( + "simple-test-parser2 -c|--count [<arg-name>]\n\n Mandatory " + "Options:\n -c|--count Count option.\n\n Optional " + "Arguments:\n arg-name Arg desc\n")); } -TEST(parser, construct_simple_test_parser3_and_gen_help_message) { +TEST(parser, constructSimpleTestParser3AndGenHelpMessage) { simple_test_parser3_t stp; + constexpr std::size_t max_option_string_size{30}; + stp.set_max_option_string_size(max_option_string_size); ASSERT_THAT( - stp.gen_help_msg(), + stp.gen_help_msg(255), testing::StrEq( - "-i|--int=<arg>... [-s|--str=<arg>...] <variadic-arg-name>... \n\n" - "Mandatory Arguments:\n variadic-arg-name Variadic arg desc " - "(variadic argument)\n\n" - "Mandatory Options:\n -i|--int=<arg> Int option. (vector " - "option, constraint: [10,200])\n\n" - "Optional Options:\n -s|--str=<arg> String option. (vector " - "option)\n")); + "simple-test-parser3 <variadic-arg-name>... -i|--int=<arg>... " + "[-s|--str=<arg>...]\n\n Mandatory Arguments:\n " + "variadic-arg-name Variadic arg desc (variadic " + "argument)\n\n Mandatory Options:\n -i|--int=<arg> " + " Int option. (vector option, constraint: [10,200])\n\n " + "Optional Options:\n -s|--str=<arg> String " + "option. (vector option)\n")); } -TEST(parser, construct_sub_parser_container_and_gen_help_message) { +TEST(parser, constructSubParserContainerAndGenHelpMessage) { sub_parser_container_t spc; - ASSERT_THAT(spc.gen_help_msg(), - testing::StrEq( - "[-b|--bool] [-2|--second] <arg-name> <sub-parser> " - "[sub-parser args/opts...] \n\n" - "Mandatory Arguments:\n arg-name Arg desc\n\n" - "Available sub-parsers:\n sub-parser Sub parser desc\n\n" - "Optional Options:\n -b|--bool Bool option.\n " - "-2|--second Second bool option.\n")); + constexpr std::size_t max_option_string_size{25}; + spc.set_max_option_string_size(max_option_string_size); + ASSERT_THAT( + spc.gen_help_msg(255), + testing::StrEq( + "sub_parser_container <arg-name> [-b|--bool] [-2|--second] " + "sub-parser [<sub-arg-name>] [-b|--bool] " + "[-s|--string=<arg>]\nsub_parser_container <arg-name> [-b|--bool] " + "[-2|--second]\n\n Subparser:\n sub-parser Sub " + "parser desc\n Optional Arguments:\n sub-arg-name " + " Sub arg desc\n\n Optional Options:\n -b|--bool " + " Bool option.\n -s|--string=<arg> String " + "option.\n\n\n Mandatory Arguments:\n arg-name " + " Arg desc\n\n Optional Options:\n -b|--bool " + "Bool option.\n -2|--second Second bool " + "option.\n")); } -TEST(parser, construct_sub_parser_container_and_gen_sub_parser_help_message) { +TEST(parser, constructSubParserContainerAndGenSubParserHelpMessage) { sub_parser_container_t spc; - ASSERT_THAT(spc.sub_parser.gen_help_msg(), - testing::StrEq( - "[-b|--bool] [-s|--string=<arg>] [<sub-arg-name>] \n\n" - "Optional Arguments:\n sub-arg-name Sub arg desc\n\n" - "Optional Options:\n -b|--bool Bool option.\n" - " -s|--string=<arg> String option.\n")); + constexpr std::size_t max_option_string_size{35}; + spc.set_max_option_string_size(max_option_string_size); + ASSERT_THAT( + spc.sub_parser.gen_help_msg(255), + testing::StrEq( + "sub_parser_container <arg-name> [-b|--bool] [-2|--second] " + "sub-parser [<sub-arg-name>] [-b|--bool] [-s|--string=<arg>]\n\n " + "Optional Arguments:\n sub-arg-name Sub arg " + "desc\n\n Optional Options:\n -b|--bool " + "Bool option.\n -s|--string=<arg> String " + "option.\n")); } -TEST(parser, construct_empty_basic_parser_and_parse_unknown_arguments_throws) { +TEST(parser, constructEmptyBasicParserAndParseUnknownArgumentsThrows) { constexpr const char* const argv[]{"unknown-argument", nullptr}; const clapp::parser::arg_t arg{parser_make_arg_t(argv)}; empty_basic_parser_t ebp; @@ -219,8 +360,7 @@ TEST(parser, construct_empty_basic_parser_and_parse_unknown_arguments_throws) { clapp::exception::clapp_exception_t); } -TEST(parser, - construct_empty_basic_parser_and_parse_unknown_long_option_throws) { +TEST(parser, constructEmptyBasicParserAndParseUnknownLongOptionThrows) { constexpr const char* const argv[]{"--long-option", nullptr}; const clapp::parser::arg_t arg{parser_make_arg_t(argv)}; empty_basic_parser_t ebp; @@ -228,8 +368,7 @@ TEST(parser, clapp::exception::option_exception_t); } -TEST(parser, - construct_empty_basic_parser_and_parse_unknown_short_option_throws) { +TEST(parser, constructEmptyBasicParserAndParseUnknownShortOptionThrows) { constexpr const char* const argv[]{"-s", nullptr}; const clapp::parser::arg_t arg{parser_make_arg_t(argv)}; empty_basic_parser_t ebp; @@ -237,9 +376,7 @@ TEST(parser, clapp::exception::option_exception_t); } -TEST( - parser, - construct_simple_test_parser_and_parse_long_bool_option_with_param_throws) { +TEST(parser, constructSimpleTestParserAndParseLongBoolOptionWithParamThrows) { constexpr const char* const argv[]{"--bool=param", nullptr}; const clapp::parser::arg_t arg{parser_make_arg_t(argv)}; simple_test_parser_t ebp; @@ -247,8 +384,7 @@ TEST( clapp::exception::option_param_exception_t); } -TEST(parser, - construct_simple_test_parser_and_parse_short_option_with_param_throws) { +TEST(parser, constructSimpleTestParserAndParseShortOptionWithParamThrows) { constexpr const char* const argv[]{"-b=param", nullptr}; const clapp::parser::arg_t arg{parser_make_arg_t(argv)}; simple_test_parser_t ebp; @@ -256,9 +392,7 @@ TEST(parser, clapp::exception::option_param_exception_t); } -TEST( - parser, - construct_simple_test_parser_and_parse_long_int_option_without_param_throws) { +TEST(parser, constructSimpleTestParserAndParseLongIntOptionWithoutParamThrows) { constexpr const char* const argv[]{"--int", nullptr}; const clapp::parser::arg_t arg{parser_make_arg_t(argv)}; simple_test_parser_t ebp; @@ -266,9 +400,8 @@ TEST( clapp::exception::option_param_exception_t); } -TEST( - parser, - construct_simple_test_parser_and_parse_short_int_option_without_param_throws) { +TEST(parser, + constructSimpleTestParserAndParseShortIntOptionWithoutParamThrows) { constexpr const char* const argv[]{"-i", nullptr}; const clapp::parser::arg_t arg{parser_make_arg_t(argv)}; simple_test_parser_t ebp; @@ -276,9 +409,8 @@ TEST( clapp::exception::option_param_exception_t); } -TEST( - parser, - construct_simple_test_parser_and_parse_short_int_option_without_param_throws2) { +TEST(parser, + constructSimpleTestParserAndParseShortIntOptionWithoutParamThrows2) { constexpr const char* const argv[]{"-ib", nullptr}; const clapp::parser::arg_t arg{parser_make_arg_t(argv)}; simple_test_parser_t ebp; @@ -286,9 +418,8 @@ TEST( clapp::exception::option_param_exception_t); } -TEST( - parser, - construct_simple_test_parser_parse_without_argument_and_validate_recursive_throws) { +TEST(parser, + constructSimpleTestParserParseWithoutArgumentAndValidateRecursiveThrows) { constexpr const char* const argv[]{nullptr}; const clapp::parser::arg_t arg{parser_make_arg_t(argv)}; simple_test_parser_t ebp; @@ -298,13 +429,14 @@ TEST( clapp::exception::argument_exception_t); } -TEST(parser, - construct_simple_test_parser_parse_argument_and_validate_recursive) { +TEST(parser, constructSimpleTestParserParseArgumentAndValidateRecursive) { constexpr const char* const argv[]{"argument", nullptr}; const clapp::parser::arg_t arg{parser_make_arg_t(argv)}; simple_test_parser_t ebp; ASSERT_NO_THROW(ebp.parse(arg.cbegin(), arg.cend())); - ASSERT_THAT(static_cast<bool>(ebp.bool_option), testing::Eq(false)); + ASSERT_THAT(static_cast<bool>(ebp.bool_option), testing::Eq(true)); + ASSERT_THAT(ebp.bool_option.has_value(), testing::Eq(true)); + ASSERT_THAT(ebp.bool_option.value(), testing::Eq(false)); ASSERT_THAT(static_cast<bool>(ebp.int_option), testing::Eq(false)); ASSERT_THAT(static_cast<bool>(ebp.string_arg), testing::Eq(true)); ASSERT_THAT(ebp.string_arg.value(), testing::Eq("argument")); @@ -315,12 +447,14 @@ TEST(parser, TEST( parser, - construct_simple_test_parser_parse_argument_and_short_option_without_params_and_validate_recursive) { + constructSimpleTestParserParseArgumentAndShortOptionWithoutParamsAndValidateRecursive) { constexpr const char* const argv[]{"-b", "arg", nullptr}; const clapp::parser::arg_t arg{parser_make_arg_t(argv)}; simple_test_parser_t ebp; ASSERT_NO_THROW(ebp.parse(arg.cbegin(), arg.cend())); ASSERT_THAT(static_cast<bool>(ebp.bool_option), testing::Eq(true)); + ASSERT_THAT(ebp.bool_option.has_value(), testing::Eq(true)); + ASSERT_THAT(ebp.bool_option.value(), testing::Eq(true)); ASSERT_THAT(static_cast<bool>(ebp.int_option), testing::Eq(false)); ASSERT_THAT(static_cast<bool>(ebp.string_arg), testing::Eq(true)); ASSERT_THAT(ebp.string_arg.value(), testing::StrEq("arg")); @@ -329,14 +463,15 @@ TEST( ASSERT_NO_THROW(ebp.validate_recursive()); } -TEST( - parser, - construct_simple_test_parser_parse_argument_and_long_option_with_param_and_validate) { +TEST(parser, + constructSimpleTestParserParseArgumentAndLongOptionWithParamAndValidate) { constexpr const char* const argv[]{"--int", "123", "arg", nullptr}; const clapp::parser::arg_t arg{parser_make_arg_t(argv)}; simple_test_parser_t ebp; ASSERT_NO_THROW(ebp.parse(arg.cbegin(), arg.cend())); - ASSERT_THAT(static_cast<bool>(ebp.bool_option), testing::Eq(false)); + ASSERT_THAT(static_cast<bool>(ebp.bool_option), testing::Eq(true)); + ASSERT_THAT(ebp.bool_option.has_value(), testing::Eq(true)); + ASSERT_THAT(ebp.bool_option.value(), testing::Eq(false)); ASSERT_THAT(static_cast<bool>(ebp.int_option), testing::Eq(true)); ASSERT_THAT(ebp.int_option.value(), testing::Eq(123)); ASSERT_THAT(static_cast<bool>(ebp.string_arg), testing::Eq(true)); @@ -348,13 +483,15 @@ TEST( TEST( parser, - construct_simple_test_parser_parse_argument_variadic_argument_and_long_option_with_param_and_validate) { + constructSimpleTestParserParseArgumentVariadicArgumentAndLongOptionWithParamAndValidate) { constexpr const char* const argv[]{"--int=123", "arg", "varg0", "varg1", nullptr}; const clapp::parser::arg_t arg{parser_make_arg_t(argv)}; simple_test_parser_t ebp; ASSERT_NO_THROW(ebp.parse(arg.cbegin(), arg.cend())); - ASSERT_THAT(static_cast<bool>(ebp.bool_option), testing::Eq(false)); + ASSERT_THAT(static_cast<bool>(ebp.bool_option), testing::Eq(true)); + ASSERT_THAT(ebp.bool_option.has_value(), testing::Eq(true)); + ASSERT_THAT(ebp.bool_option.value(), testing::Eq(false)); ASSERT_THAT(static_cast<bool>(ebp.int_option), testing::Eq(true)); ASSERT_THAT(ebp.int_option.value(), testing::Eq(123)); ASSERT_THAT(static_cast<bool>(ebp.string_arg), testing::Eq(true)); @@ -367,14 +504,13 @@ TEST( ASSERT_NO_THROW(ebp.validate_recursive()); } -TEST( - parser, - construct_simple_test_parser_parse_argument_and_short_options_and_validate) { +TEST(parser, constructSimpleTestParserParseArgumentAndShortOptionsAndValidate) { constexpr const char* const argv[]{"-bi=123", "arg", nullptr}; const clapp::parser::arg_t arg{parser_make_arg_t(argv)}; simple_test_parser_t ebp; ASSERT_NO_THROW(ebp.parse(arg.cbegin(), arg.cend())); ASSERT_THAT(static_cast<bool>(ebp.bool_option), testing::Eq(true)); + ASSERT_THAT(ebp.bool_option.has_value(), testing::Eq(true)); ASSERT_THAT(ebp.bool_option.value(), testing::Eq(true)); ASSERT_THAT(static_cast<bool>(ebp.int_option), testing::Eq(true)); ASSERT_THAT(ebp.int_option.value(), testing::Eq(123)); @@ -384,9 +520,7 @@ TEST( ASSERT_NO_THROW(ebp.validate_recursive()); } -TEST( - parser, - construct_simple_test_parser2_and_parse_long_count_option_with_param_throws) { +TEST(parser, constructSimpleTestParser2AndParseLongCountOptionWithParamThrows) { constexpr const char* const argv[]{"--count=param", nullptr}; const clapp::parser::arg_t arg{parser_make_arg_t(argv)}; simple_test_parser2_t ebp; @@ -394,30 +528,31 @@ TEST( clapp::option_param_exception_t); } -TEST(parser, - construct_simple_test_parser2_parse_without_mandatory_option_throws) { +TEST(parser, constructSimpleTestParser2ParseWithoutMandatoryOptionThrows) { constexpr const char* const argv[]{nullptr}; const clapp::parser::arg_t arg{parser_make_arg_t(argv)}; simple_test_parser2_t ebp; ASSERT_NO_THROW(ebp.parse(arg.cbegin(), arg.cend())); - ASSERT_THAT(static_cast<bool>(ebp.count_option), testing::Eq(false)); + ASSERT_THAT(static_cast<bool>(ebp.count_option), testing::Eq(true)); + ASSERT_THAT(ebp.count_option.has_value(), testing::Eq(true)); + ASSERT_THAT(ebp.count_option.value(), testing::Eq(0)); ASSERT_THROW(ebp.validate(), clapp::option_param_exception_t); } -TEST(parser, - construct_simple_test_parser2_parse_option_without_param_and_validate) { +TEST(parser, constructSimpleTestParser2ParseOptionWithoutParamAndValidate) { constexpr const char* const argv[]{"-c", nullptr}; const clapp::parser::arg_t arg{parser_make_arg_t(argv)}; simple_test_parser2_t ebp; ASSERT_NO_THROW(ebp.parse(arg.cbegin(), arg.cend())); ASSERT_THAT(static_cast<bool>(ebp.count_option), testing::Eq(true)); + ASSERT_THAT(ebp.count_option.has_value(), testing::Eq(true)); ASSERT_THAT(ebp.count_option.value(), testing::Eq(1)); ASSERT_NO_THROW(ebp.validate()); } TEST( parser, - construct_simple_test_parser2_parse_option_without_param_optional_argument_and_validate) { + constructSimpleTestParser2ParseOptionWithoutParamOptionalArgumentAndValidate) { constexpr const char* const argv[]{"-c", "opt-arg", nullptr}; const clapp::parser::arg_t arg{parser_make_arg_t(argv)}; simple_test_parser2_t ebp; @@ -429,7 +564,7 @@ TEST( ASSERT_NO_THROW(ebp.validate()); } -TEST(parser, construct_sub_parser_container_parse_subparser_and_validate) { +TEST(parser, constructSubParserContainerParseSubparserAndValidate) { constexpr const char* const argv[]{"string-arg", "sub-parser", "-bs=param", nullptr}; const clapp::parser::arg_t arg{parser_make_arg_t(argv)}; @@ -437,31 +572,41 @@ TEST(parser, construct_sub_parser_container_parse_subparser_and_validate) { ASSERT_NO_THROW(spc.parse(arg.cbegin(), arg.cend())); ASSERT_THAT(static_cast<bool>(spc.string_arg), testing::Eq(true)); ASSERT_THAT(spc.string_arg.value(), testing::StrEq("string-arg")); - ASSERT_THAT(static_cast<bool>(spc.bool_option), testing::Eq(false)); - ASSERT_THAT(static_cast<bool>(spc.bool_option2), testing::Eq(false)); + ASSERT_THAT(static_cast<bool>(spc.bool_option), testing::Eq(true)); + ASSERT_THAT(spc.bool_option.has_value(), testing::Eq(true)); + ASSERT_THAT(spc.bool_option.value(), testing::Eq(false)); + ASSERT_THAT(static_cast<bool>(spc.bool_option2), testing::Eq(true)); + ASSERT_THAT(spc.bool_option2.has_value(), testing::Eq(true)); + ASSERT_THAT(spc.bool_option2.value(), testing::Eq(false)); ASSERT_THAT(static_cast<bool>(spc.sub_parser.bool_option), testing::Eq(true)); ASSERT_THAT(spc.sub_parser.bool_option.value(), testing::Eq(true)); + ASSERT_THAT(spc.sub_parser.bool_option.has_value(), testing::Eq(true)); + ASSERT_THAT(spc.sub_parser.bool_option.value(), testing::Eq(true)); ASSERT_THAT(static_cast<bool>(spc.sub_parser.string_option), testing::Eq(true)); ASSERT_THAT(spc.sub_parser.string_option.value(), testing::StrEq("param")); ASSERT_NO_THROW(spc.validate()); } -TEST(parser, construct_sub_parser_container_parse_subparser_and_validate2) { - constexpr const char* const argv[]{"string-arg", "-b", "sub-parser", "-s", - "param", "-2", nullptr}; +TEST(parser, constructSubParserContainerParseSubparserAndValidate2) { + constexpr const char* const argv[]{ + "string-arg", "-b", "-2", "sub-parser", "-s", "param", nullptr}; const clapp::parser::arg_t arg{parser_make_arg_t(argv)}; sub_parser_container_t spc; ASSERT_NO_THROW(spc.parse(arg.cbegin(), arg.cend())); ASSERT_THAT(static_cast<bool>(spc.string_arg), testing::Eq(true)); ASSERT_THAT(spc.string_arg.value(), testing::StrEq("string-arg")); ASSERT_THAT(static_cast<bool>(spc.bool_option), testing::Eq(true)); + ASSERT_THAT(spc.bool_option.has_value(), testing::Eq(true)); ASSERT_THAT(spc.bool_option.value(), testing::Eq(true)); ASSERT_THAT(static_cast<bool>(spc.bool_option2), testing::Eq(true)); + ASSERT_THAT(spc.bool_option2.has_value(), testing::Eq(true)); ASSERT_THAT(spc.bool_option2.value(), testing::Eq(true)); ASSERT_THAT(static_cast<bool>(spc.sub_parser.bool_option), - testing::Eq(false)); + testing::Eq(true)); + ASSERT_THAT(spc.sub_parser.bool_option.has_value(), testing::Eq(true)); + ASSERT_THAT(spc.sub_parser.bool_option.value(), testing::Eq(false)); ASSERT_THAT(static_cast<bool>(spc.sub_parser.string_option), testing::Eq(true)); ASSERT_THAT(spc.sub_parser.string_option.value(), testing::StrEq("param")); diff --git a/tests/sub_parser.cpp b/tests/sub_parser.cpp index d0e8a25f..4a51e85f 100644 --- a/tests/sub_parser.cpp +++ b/tests/sub_parser.cpp @@ -19,10 +19,31 @@ class empty_test_parser_t : public clapp::parser::basic_parser_t { using clapp::parser::basic_parser_t::get_sub_parsers; ~empty_test_parser_t() override; + + [[nodiscard]] std::string gen_short_line_prefix() const override; + void set_max_option_string_size(std::size_t max_option_size) override; + [[nodiscard]] std::size_t get_max_option_string_size() const override; + + private: + std::size_t max_option_string_size{0}; }; empty_test_parser_t::~empty_test_parser_t() = default; +[[nodiscard]] std::string empty_test_parser_t::gen_short_line_prefix() const { + return "empty-test-parser" + gen_short_line(); +} + +void empty_test_parser_t::set_max_option_string_size( + const std::size_t max_option_size) { + max_option_string_size = max_option_size; +} + +[[nodiscard]] std::size_t empty_test_parser_t::get_max_option_string_size() + const { + return max_option_string_size; +} + class empty_sub_parser_t : public clapp::parser::basic_sub_parser_t { public: using clapp::parser::basic_sub_parser_t::basic_sub_parser_t; @@ -44,15 +65,38 @@ class simple_test_sub_parser_t : public clapp::parser::basic_parser_t { "Count option."}; ~simple_test_sub_parser_t() override; + + [[nodiscard]] std::string gen_short_line_prefix() const override; + void set_max_option_string_size(std::size_t max_option_size) override; + [[nodiscard]] std::size_t get_max_option_string_size() const override; + + private: + std::size_t max_option_string_size{0}; }; simple_test_sub_parser_t::~simple_test_sub_parser_t() = default; +[[nodiscard]] std::string simple_test_sub_parser_t::gen_short_line_prefix() + const { + return "simple-test-sub-parser" + gen_short_line(); +} + +void simple_test_sub_parser_t::set_max_option_string_size( + const std::size_t max_option_size) { + max_option_string_size = max_option_size; +} + +[[nodiscard]] std::size_t simple_test_sub_parser_t::get_max_option_string_size() + const { + return max_option_string_size; +} + class simple_sub_parser_t : public clapp::parser::basic_sub_parser_t { public: using clapp::parser::basic_sub_parser_t::basic_sub_parser_t; - clapp::option::bool_option_t bool_option{*this, "bool", 'b', - "Bool option."}; + clapp::option::bool_option_t bool_option{ + *this, "bool", 'b', "Bool option.", + basic_parser_t::purpose_t::mandatory}; ~simple_sub_parser_t() override; }; @@ -66,11 +110,33 @@ class variadic_argument_test_sub_parser_t *this, "arg", "Arg"}; ~variadic_argument_test_sub_parser_t() override; + + [[nodiscard]] std::string gen_short_line_prefix() const override; + void set_max_option_string_size(std::size_t max_option_size) override; + [[nodiscard]] std::size_t get_max_option_string_size() const override; + + private: + std::size_t max_option_string_size{0}; }; variadic_argument_test_sub_parser_t::~variadic_argument_test_sub_parser_t() = default; +[[nodiscard]] std::string +variadic_argument_test_sub_parser_t::gen_short_line_prefix() const { + return "variadic-argument-test-sub-parser" + gen_short_line(); +} + +void variadic_argument_test_sub_parser_t::set_max_option_string_size( + const std::size_t max_option_size) { + max_option_string_size = max_option_size; +} + +[[nodiscard]] std::size_t +variadic_argument_test_sub_parser_t::get_max_option_string_size() const { + return max_option_string_size; +} + class optional_argument_test_sub_parser_t : public clapp::parser::basic_parser_t { public: @@ -78,12 +144,34 @@ class optional_argument_test_sub_parser_t *this, "arg", "Arg", clapp::basic_parser_t::purpose_t::optional}; ~optional_argument_test_sub_parser_t() override; + + [[nodiscard]] std::string gen_short_line_prefix() const override; + void set_max_option_string_size(std::size_t max_option_size) override; + [[nodiscard]] std::size_t get_max_option_string_size() const override; + + private: + std::size_t max_option_string_size{0}; }; optional_argument_test_sub_parser_t::~optional_argument_test_sub_parser_t() = default; -TEST(sub_parser, construct_empty_sub_parser_and_parse_empty_arguments) { +[[nodiscard]] std::string +optional_argument_test_sub_parser_t::gen_short_line_prefix() const { + return "optional-argument-test-sub-parser" + gen_short_line(); +} + +void optional_argument_test_sub_parser_t::set_max_option_string_size( + const std::size_t max_option_size) { + max_option_string_size = max_option_size; +} + +[[nodiscard]] std::size_t +optional_argument_test_sub_parser_t::get_max_option_string_size() const { + return max_option_string_size; +} + +TEST(subParser, constructEmptySubParserAndParseEmptyArguments) { const std::string sub_parser{"sub"}; const std::string description{"sub parser"}; @@ -107,7 +195,7 @@ TEST(sub_parser, construct_empty_sub_parser_and_parse_empty_arguments) { tp.parse(arg.cbegin(), arg.cend()); } -TEST(sub_parser, construct_simple_sub_parser_and_parse_sub_option) { +TEST(subParser, constructSimpleSubParserAndParseSubOption) { const std::string sub_parser{"subbb"}; const std::string description{"subbb parser"}; @@ -129,16 +217,18 @@ TEST(sub_parser, construct_simple_sub_parser_and_parse_sub_option) { ASSERT_THAT(static_cast<bool>(sub), testing::Eq(false)); ASSERT_THAT(sub.get_sub_parser_name(), testing::StrEq(sub_parser)); - ASSERT_THAT(static_cast<bool>(sub.bool_option), testing::Eq(false)); + ASSERT_THAT(static_cast<bool>(sub.bool_option), testing::Eq(true)); + ASSERT_THAT(sub.bool_option.has_value(), testing::Eq(true)); + ASSERT_THAT(sub.bool_option.value(), testing::Eq(false)); tp.parse(arg.cbegin(), arg.cend()); ASSERT_THAT(static_cast<bool>(sub.bool_option), testing::Eq(true)); } -TEST(sub_parser, construct_simple_sub_parser_and_parse_base_option) { +TEST(subParser, constructSimpleSubParserAndParseBaseOption) { const std::string sub_parser{"subbb"}; const std::string description{"subbb parser"}; - constexpr const char* const argv[]{"subbb", "--count", nullptr}; + constexpr const char* const argv[]{"--count", "subbb", "-b", nullptr}; const clapp::parser::arg_t arg{sub_parser_make_arg_t(argv)}; simple_test_sub_parser_t tp; @@ -161,69 +251,93 @@ TEST(sub_parser, construct_simple_sub_parser_and_parse_base_option) { ASSERT_THAT(tp.count_option.value(), testing::Eq(1)); } -TEST(sub_parser, construct_simple_sub_parser_and_ignore_main_option) { - const std::string sub_parser{"subbb"}; - const std::string description{"subbb parser"}; - - constexpr const char* const argv[]{"subbb", "--count", nullptr}; - const clapp::parser::arg_t arg{sub_parser_make_arg_t(argv)}; +TEST(subParser, constructSubParserWithSameNameThrows) { + const std::string sub_parser{"sub"}; + const std::string description{"sub parser"}; simple_test_sub_parser_t tp; - simple_sub_parser_t sub{tp, sub_parser, description, false}; - - simple_test_sub_parser_t::sub_parsers_map_t long_options{ - tp.get_sub_parsers()}; - simple_test_sub_parser_t::sub_parser_descriptions_vec_t descs{ - tp.get_sub_parser_descriptions()}; - - ASSERT_THAT(descs.size(), testing::Eq(1)); - ASSERT_THAT(descs[0].sub_parser_string, testing::StrEq(sub_parser)); - ASSERT_THAT(descs[0].description, testing::StrEq(description)); + simple_sub_parser_t sub{tp, sub_parser, description}; + ASSERT_THROW((simple_sub_parser_t{tp, sub_parser, description}), + clapp::exception::sub_parser_exception_t); +} - ASSERT_THAT(static_cast<bool>(sub), testing::Eq(false)); - ASSERT_THAT(sub.get_sub_parser_name(), testing::StrEq(sub_parser)); +TEST(subParser, constructSubParserWithVariadicArgumentsParserThrows) { + const std::string sub_parser{"sub"}; + const std::string description{"sub parser"}; - ASSERT_THAT(tp.count_option.value(), testing::Eq(0)); - ASSERT_THROW(tp.parse(arg.cbegin(), arg.cend()), - clapp::exception::option_exception_t); - ASSERT_THAT(tp.count_option.value(), testing::Eq(0)); - ASSERT_NO_THROW(tp.validate_recursive()); + variadic_argument_test_sub_parser_t tp; + ASSERT_THROW((simple_sub_parser_t{tp, sub_parser, description}), + clapp::exception::sub_parser_exception_t); } -TEST(sub_parser, construct_sub_parser_with_same_name_throws) { +TEST(subParser, constructSubParserWithOptionalArgumentParserThrows) { const std::string sub_parser{"sub"}; const std::string description{"sub parser"}; - simple_test_sub_parser_t tp; - simple_sub_parser_t sub{tp, sub_parser, description, false}; - ASSERT_THROW((simple_sub_parser_t{tp, sub_parser, description, false}), + optional_argument_test_sub_parser_t tp; + ASSERT_THROW((simple_sub_parser_t{tp, sub_parser, description}), clapp::exception::sub_parser_exception_t); } -TEST(sub_parser, construct_sub_parser_with_variadic_arguments_parser_throws) { +TEST(subParser, constructSubParserAndGenHelpPrefix) { const std::string sub_parser{"sub"}; const std::string description{"sub parser"}; - variadic_argument_test_sub_parser_t tp; - ASSERT_THROW((simple_sub_parser_t{tp, sub_parser, description, false}), - clapp::exception::sub_parser_exception_t); + empty_test_parser_t tp; + empty_sub_parser_t sub{tp, sub_parser, description}; + + ASSERT_THAT(sub.gen_short_line_prefix(), + testing::StrEq("empty-test-parser sub")); } -TEST(sub_parser, construct_sub_parser_with_optional_argument_parser_throws) { +TEST(subParser, constructSubParserIsActiveIsFalse) { const std::string sub_parser{"sub"}; const std::string description{"sub parser"}; - optional_argument_test_sub_parser_t tp; - ASSERT_THROW((simple_sub_parser_t{tp, sub_parser, description, false}), - clapp::exception::sub_parser_exception_t); + empty_test_parser_t tp; + empty_sub_parser_t sub{tp, sub_parser, description}; + ASSERT_THAT(sub.is_active(), testing::Eq(false)); } -TEST(parser, construct_sub_parser_and_gen_help_prefix) { +TEST(subParser, constructSubParserAfterParsingSubBecomesActive) { const std::string sub_parser{"sub"}; const std::string description{"sub parser"}; + constexpr const char* const argv[]{"sub", nullptr}; + const clapp::parser::arg_t arg{sub_parser_make_arg_t(argv)}; empty_test_parser_t tp; empty_sub_parser_t sub{tp, sub_parser, description}; - ASSERT_THAT(sub.gen_help_prefix(), testing::StrEq("Usage: \n sub")); + ASSERT_THAT(&tp.get_active_parser(), testing::Eq(&tp)); + ASSERT_THAT(sub.is_active(), testing::Eq(false)); + + tp.parse(arg.cbegin(), arg.cend()); + ASSERT_THAT(sub.is_active(), testing::Eq(true)); + ASSERT_THAT(&tp.get_active_parser(), testing::Eq(&sub)); +} + +TEST(subParser, constructSubParserAndValidateRecursiveDoNotThrow) { + const std::string sub_parser{"sub"}; + const std::string description{"sub parser"}; + constexpr const char* const argv[]{"sub", "-b", nullptr}; + const clapp::parser::arg_t arg{sub_parser_make_arg_t(argv)}; + + empty_test_parser_t tp; + simple_sub_parser_t sub{tp, sub_parser, description}; + + tp.parse(arg.cbegin(), arg.cend()); + ASSERT_NO_THROW(tp.validate_recursive()); +} + +TEST(subParser, constructSubParserAndValidateRecursiveDoThrow) { + const std::string sub_parser{"sub"}; + const std::string description{"sub parser"}; + constexpr const char* const argv[]{"sub", nullptr}; + const clapp::parser::arg_t arg{sub_parser_make_arg_t(argv)}; + + empty_test_parser_t tp; + simple_sub_parser_t sub{tp, sub_parser, description}; + + tp.parse(arg.cbegin(), arg.cend()); + ASSERT_THROW(tp.validate_recursive(), clapp::clapp_exception_t); } diff --git a/tests/value.cpp b/tests/value.cpp index 8eb3072b..14d40d6c 100644 --- a/tests/value.cpp +++ b/tests/value.cpp @@ -2,33 +2,33 @@ #include <clapp/value.h> #include <gmock/gmock.h> -TEST(value, convert_value_string) { +TEST(value, convertValueString) { ASSERT_THAT(clapp::value::convert_value<std::string>("aba"), testing::Eq(std::string{"aba"})); ASSERT_THAT(clapp::value::convert_value<std::string>("abba"), testing::Eq(std::string{"abba"})); } -TEST(value, convert_value_path) { +TEST(value, convertValuePath) { ASSERT_THAT(clapp::value::convert_value<clapp::fs::path>("/tmp/test"), testing::Eq(clapp::fs::path{"/tmp/test"})); } -TEST(value, convert_value_uint8_t) { +TEST(value, convertValueUint8T) { ASSERT_THAT(clapp::value::convert_value<std::uint8_t>("0"), - testing::Eq(std::uint8_t{0u})); + testing::Eq(std::uint8_t{0U})); ASSERT_THAT(clapp::value::convert_value<std::uint8_t>("100"), - testing::Eq(std::uint8_t{100u})); + testing::Eq(std::uint8_t{100U})); ASSERT_THAT(clapp::value::convert_value<std::uint8_t>("255"), - testing::Eq(std::uint8_t{255u})); + testing::Eq(std::uint8_t{255U})); ASSERT_THAT(clapp::value::convert_value<std::uint8_t>("2.555"), - testing::Eq(std::uint8_t{2})); + testing::Eq(std::uint8_t{2U})); ASSERT_THAT(clapp::value::convert_value<std::uint8_t>("0xab"), - testing::Eq(std::uint8_t{0xabu})); + testing::Eq(std::uint8_t{0xabU})); ASSERT_THAT(clapp::value::convert_value<std::uint8_t>("0xff"), - testing::Eq(std::uint8_t{0xffu})); + testing::Eq(std::uint8_t{0xffU})); ASSERT_THAT(clapp::value::convert_value<std::uint8_t>("077"), - testing::Eq(std::uint8_t{077})); + testing::Eq(std::uint8_t{077U})); ASSERT_THROW(clapp::value::convert_value<std::uint8_t>("256"), clapp::exception::out_of_range_t); ASSERT_THROW(clapp::value::convert_value<std::uint8_t>("-1"), @@ -37,21 +37,21 @@ TEST(value, convert_value_uint8_t) { clapp::exception::invalid_value_t); } -TEST(value, convert_value_uint16_t) { +TEST(value, convertValueUint16T) { ASSERT_THAT(clapp::value::convert_value<std::uint16_t>("0"), - testing::Eq(std::uint16_t{0u})); + testing::Eq(std::uint16_t{0U})); ASSERT_THAT(clapp::value::convert_value<std::uint16_t>("1000"), - testing::Eq(std::uint16_t{1000u})); + testing::Eq(std::uint16_t{1000U})); ASSERT_THAT(clapp::value::convert_value<std::uint16_t>("65535"), - testing::Eq(std::uint16_t{65535u})); + testing::Eq(std::uint16_t{65535U})); ASSERT_THAT(clapp::value::convert_value<std::uint16_t>("2222.555"), - testing::Eq(std::uint16_t{2222})); + testing::Eq(std::uint16_t{2222U})); ASSERT_THAT(clapp::value::convert_value<std::uint16_t>("0xffab"), - testing::Eq(std::uint16_t{0xffabu})); + testing::Eq(std::uint16_t{0xffabU})); ASSERT_THAT(clapp::value::convert_value<std::uint16_t>("0xffff"), - testing::Eq(std::uint16_t{0xffffu})); + testing::Eq(std::uint16_t{0xffffU})); ASSERT_THAT(clapp::value::convert_value<std::uint16_t>("01234"), - testing::Eq(std::uint16_t{01234u})); + testing::Eq(std::uint16_t{01234U})); ASSERT_THROW(clapp::value::convert_value<std::uint16_t>("65536"), clapp::exception::out_of_range_t); ASSERT_THROW(clapp::value::convert_value<std::uint16_t>("-100"), @@ -60,19 +60,19 @@ TEST(value, convert_value_uint16_t) { clapp::exception::invalid_value_t); } -TEST(value, convert_value_uint32_t) { +TEST(value, convertValueUint32T) { ASSERT_THAT(clapp::value::convert_value<std::uint32_t>("0"), - testing::Eq(std::uint32_t{0u})); + testing::Eq(std::uint32_t{0U})); ASSERT_THAT(clapp::value::convert_value<std::uint32_t>("10000"), - testing::Eq(std::uint32_t{10000u})); + testing::Eq(std::uint32_t{10000U})); ASSERT_THAT(clapp::value::convert_value<std::uint32_t>("4294967295"), - testing::Eq(std::uint32_t{4294967295u})); + testing::Eq(std::uint32_t{4294967295U})); ASSERT_THAT(clapp::value::convert_value<std::uint32_t>("0xffab"), - testing::Eq(std::uint32_t{0xffabu})); + testing::Eq(std::uint32_t{0xffabU})); ASSERT_THAT(clapp::value::convert_value<std::uint32_t>("0xffffffff"), - testing::Eq(std::uint32_t{0xffffffffu})); + testing::Eq(std::uint32_t{0xffffffffU})); ASSERT_THAT(clapp::value::convert_value<std::uint32_t>("0123456"), - testing::Eq(std::uint32_t{0123456u})); + testing::Eq(std::uint32_t{0123456U})); ASSERT_THROW(clapp::value::convert_value<std::uint32_t>("4294967296"), clapp::exception::out_of_range_t); ASSERT_THROW(clapp::value::convert_value<std::uint32_t>("-10000"), @@ -81,21 +81,21 @@ TEST(value, convert_value_uint32_t) { clapp::exception::invalid_value_t); } -TEST(value, convert_value_uint64_t) { +TEST(value, convertValueUint64T) { ASSERT_THAT(clapp::value::convert_value<std::uint64_t>("0"), - testing::Eq(std::uint64_t{0u})); + testing::Eq(std::uint64_t{0U})); ASSERT_THAT(clapp::value::convert_value<std::uint64_t>("1000000"), - testing::Eq(std::uint64_t{1000000u})); + testing::Eq(std::uint64_t{1000000U})); ASSERT_THAT( clapp::value::convert_value<std::uint64_t>("18446744073709551615"), - testing::Eq(std::uint64_t{18446744073709551615ull})); + testing::Eq(std::uint64_t{18446744073709551615ULL})); ASSERT_THAT(clapp::value::convert_value<std::uint64_t>("0xffab"), - testing::Eq(std::uint64_t{0xffabu})); + testing::Eq(std::uint64_t{0xffabU})); ASSERT_THAT( clapp::value::convert_value<std::uint64_t>("0xffffffffffffffff"), - testing::Eq(std::uint64_t{0xffffffffffffffffull})); + testing::Eq(std::uint64_t{0xffffffffffffffffULL})); ASSERT_THAT(clapp::value::convert_value<std::uint64_t>("012345671234"), - testing::Eq(std::uint64_t{012345671234ull})); + testing::Eq(std::uint64_t{012345671234ULL})); ASSERT_THROW( clapp::value::convert_value<std::uint64_t>("18446744073709551616"), clapp::exception::out_of_range_t); @@ -103,7 +103,7 @@ TEST(value, convert_value_uint64_t) { clapp::exception::invalid_value_t); } -TEST(value, convert_value_int8_t) { +TEST(value, convertValueInt8T) { ASSERT_THAT(clapp::value::convert_value<std::int8_t>("0"), testing::Eq(std::int8_t{0})); ASSERT_THAT(clapp::value::convert_value<std::int8_t>("100"), @@ -128,7 +128,7 @@ TEST(value, convert_value_int8_t) { clapp::exception::invalid_value_t); } -TEST(value, convert_value_int16_t) { +TEST(value, convertValueInt16T) { ASSERT_THAT(clapp::value::convert_value<std::int16_t>("0"), testing::Eq(std::int16_t{0})); ASSERT_THAT(clapp::value::convert_value<std::int16_t>("10000"), @@ -144,7 +144,7 @@ TEST(value, convert_value_int16_t) { ASSERT_THAT(clapp::value::convert_value<std::int16_t>("-0x7fff"), testing::Eq(std::int16_t{-0x7fff})); ASSERT_THAT(clapp::value::convert_value<std::int16_t>("-01234"), - testing::Eq(std::int16_t{-01234ll})); + testing::Eq(std::int16_t{-01234LL})); ASSERT_THROW(clapp::value::convert_value<std::int16_t>("32768"), clapp::exception::out_of_range_t); ASSERT_THROW(clapp::value::convert_value<std::int16_t>("-32769"), @@ -153,7 +153,7 @@ TEST(value, convert_value_int16_t) { clapp::exception::invalid_value_t); } -TEST(value, convert_value_int32_t) { +TEST(value, convertValueInt32T) { ASSERT_THAT(clapp::value::convert_value<std::int32_t>("0"), testing::Eq(std::int32_t{0})); ASSERT_THAT(clapp::value::convert_value<std::int32_t>("1000000"), @@ -165,11 +165,11 @@ TEST(value, convert_value_int32_t) { ASSERT_THAT(clapp::value::convert_value<std::int32_t>("-2147483648"), testing::Eq(std::int32_t{-2147483648})); ASSERT_THAT(clapp::value::convert_value<std::int32_t>("0xffab"), - testing::Eq(std::int32_t{0xffabu})); + testing::Eq(std::int32_t{0xffab})); ASSERT_THAT(clapp::value::convert_value<std::int32_t>("-0x3fffffff"), testing::Eq(std::int32_t{-0x3fffffff})); ASSERT_THAT(clapp::value::convert_value<std::int32_t>("-0123456"), - testing::Eq(std::int32_t{-0123456ll})); + testing::Eq(std::int32_t{-0123456LL})); ASSERT_THROW(clapp::value::convert_value<std::int32_t>("2147483648"), clapp::exception::out_of_range_t); ASSERT_THROW(clapp::value::convert_value<std::int32_t>("-2147483649"), @@ -178,27 +178,27 @@ TEST(value, convert_value_int32_t) { clapp::exception::invalid_value_t); } -TEST(value, convert_value_int64_t) { +TEST(value, convertValueInt64T) { ASSERT_THAT(clapp::value::convert_value<std::int64_t>("0"), testing::Eq(std::int64_t{0})); ASSERT_THAT(clapp::value::convert_value<std::int64_t>("10000000000"), - testing::Eq(std::int64_t{10000000000ll})); + testing::Eq(std::int64_t{10000000000LL})); ASSERT_THAT( clapp::value::convert_value<std::int64_t>("9223372036854775807"), - testing::Eq(std::int64_t{9223372036854775807ll})); + testing::Eq(std::int64_t{9223372036854775807LL})); ASSERT_THAT( clapp::value::convert_value<std::int64_t>("-9223372036854775807"), - testing::Eq(std::int64_t{-9223372036854775807ll})); + testing::Eq(std::int64_t{-9223372036854775807LL})); ASSERT_THAT( clapp::value::convert_value<std::int64_t>("-9223372036854775808"), - testing::Eq(std::int64_t{-9223372036854775807ll - 1ll})); + testing::Eq(std::int64_t{-9223372036854775807LL - 1LL})); ASSERT_THAT(clapp::value::convert_value<std::int64_t>("0x3fffffffffffff"), testing::Eq(std::int64_t{0x3fffffffffffffLL})); ASSERT_THAT( clapp::value::convert_value<std::int64_t>("-0x3fffffffffffffff"), testing::Eq(std::int64_t{-0x3fffffffffffffffLL})); ASSERT_THAT(clapp::value::convert_value<std::int64_t>("-012345671234"), - testing::Eq(std::int64_t{-012345671234ll})); + testing::Eq(std::int64_t{-012345671234LL})); ASSERT_THROW( clapp::value::convert_value<std::int64_t>("9223372036854775808"), clapp::exception::out_of_range_t); @@ -209,7 +209,7 @@ TEST(value, convert_value_int64_t) { clapp::exception::invalid_value_t); } -TEST(value, convert_value_double) { +TEST(value, convertValueDouble) { ASSERT_THAT(clapp::value::convert_value<double>("0"), testing::Eq(double{0})); ASSERT_THAT(clapp::value::convert_value<double>("10000000000"), @@ -230,67 +230,67 @@ TEST(value, convert_value_double) { clapp::exception::invalid_value_t); } -TEST(value, convert_value_float) { +TEST(value, convertValueFloat) { ASSERT_THAT(clapp::value::convert_value<float>("0"), - testing::Eq(float{0.f})); + testing::Eq(float{0.F})); ASSERT_THAT(clapp::value::convert_value<float>("10000000000"), - testing::Eq(float{10000000000.f})); + testing::Eq(float{10000000000.F})); ASSERT_THAT(clapp::value::convert_value<float>("0.125"), - testing::Eq(float{0.125f})); + testing::Eq(float{0.125F})); ASSERT_THAT(clapp::value::convert_value<float>("5e-1"), - testing::Eq(float{.5f})); + testing::Eq(float{.5F})); ASSERT_THROW(clapp::value::convert_value<float>("10e-600"), clapp::exception::out_of_range_t); ASSERT_THROW(clapp::value::convert_value<float>("z"), clapp::exception::invalid_value_t); } -TEST(value, convert_value_chrono_nanoseconds) { +TEST(value, convertValueChronoNanoseconds) { ASSERT_THAT(clapp::value::convert_value<std::chrono::nanoseconds>("0"), - testing::Eq(std::chrono::nanoseconds{0u})); + testing::Eq(std::chrono::nanoseconds{0U})); ASSERT_THAT( clapp::value::convert_value<std::chrono::nanoseconds>("1000000"), - testing::Eq(std::chrono::nanoseconds{1000000u})); + testing::Eq(std::chrono::nanoseconds{1000000U})); } -TEST(value, convert_value_chrono_microseconds) { +TEST(value, convertValueChronoMicroseconds) { ASSERT_THAT(clapp::value::convert_value<std::chrono::microseconds>("0"), - testing::Eq(std::chrono::microseconds{0u})); + testing::Eq(std::chrono::microseconds{0U})); ASSERT_THAT( clapp::value::convert_value<std::chrono::microseconds>("1000000"), - testing::Eq(std::chrono::microseconds{1000000u})); + testing::Eq(std::chrono::microseconds{1000000U})); } -TEST(value, convert_value_chrono_milliseconds) { +TEST(value, convertValueChronoMilliseconds) { ASSERT_THAT(clapp::value::convert_value<std::chrono::milliseconds>("0"), - testing::Eq(std::chrono::milliseconds{0u})); + testing::Eq(std::chrono::milliseconds{0U})); ASSERT_THAT( clapp::value::convert_value<std::chrono::milliseconds>("1000000"), - testing::Eq(std::chrono::milliseconds{1000000u})); + testing::Eq(std::chrono::milliseconds{1000000U})); } -TEST(value, convert_value_chrono_seconds) { +TEST(value, convertValueChronoSeconds) { ASSERT_THAT(clapp::value::convert_value<std::chrono::seconds>("0"), - testing::Eq(std::chrono::seconds{0u})); + testing::Eq(std::chrono::seconds{0U})); ASSERT_THAT(clapp::value::convert_value<std::chrono::seconds>("1000000"), - testing::Eq(std::chrono::seconds{1000000u})); + testing::Eq(std::chrono::seconds{1000000U})); } -TEST(value, convert_value_chrono_minutes) { +TEST(value, convertValueChronoMinutes) { ASSERT_THAT(clapp::value::convert_value<std::chrono::minutes>("0"), - testing::Eq(std::chrono::minutes{0u})); + testing::Eq(std::chrono::minutes{0U})); ASSERT_THAT(clapp::value::convert_value<std::chrono::minutes>("1000000"), - testing::Eq(std::chrono::minutes{1000000u})); + testing::Eq(std::chrono::minutes{1000000U})); } -TEST(value, convert_value_chrono_hours) { +TEST(value, convertValueChronoHours) { ASSERT_THAT(clapp::value::convert_value<std::chrono::hours>("0"), - testing::Eq(std::chrono::hours{0u})); + testing::Eq(std::chrono::hours{0U})); ASSERT_THAT(clapp::value::convert_value<std::chrono::hours>("1000000"), - testing::Eq(std::chrono::hours{1000000u})); + testing::Eq(std::chrono::hours{1000000U})); } -TEST(value, get_chrono_postfix) { +TEST(value, getChronoPostfix) { ASSERT_THAT(clapp::value::get_chrono_postfix<std::chrono::nanoseconds>(), testing::StrEq("ns")); ASSERT_THAT(clapp::value::get_chrono_postfix<std::chrono::microseconds>(), @@ -305,7 +305,7 @@ TEST(value, get_chrono_postfix) { testing::StrEq("h")); } -TEST(value, default_value_uint8_t) { +TEST(value, defaultValueUint8T) { constexpr std::uint8_t value{10}; clapp::value::default_value_t<std::uint8_t> dv{value}; std::stringstream ss; @@ -314,7 +314,7 @@ TEST(value, default_value_uint8_t) { ASSERT_THAT(dv.default_value(), testing::Eq(std::uint8_t{value})); } -TEST(value, default_value_int8_t) { +TEST(value, defaultValueInt8T) { constexpr std::int8_t value{-10}; clapp::value::default_value_t<std::int8_t> dv{value}; std::stringstream ss; @@ -323,7 +323,7 @@ TEST(value, default_value_int8_t) { ASSERT_THAT(dv.default_value(), testing::Eq(std::int8_t{value})); } -TEST(value, default_value_int32_t) { +TEST(value, defaultValueInt32T) { constexpr std::int32_t value{100'000}; clapp::value::default_value_t<std::int32_t> dv{value}; std::stringstream ss; @@ -332,7 +332,7 @@ TEST(value, default_value_int32_t) { ASSERT_THAT(dv.default_value(), testing::Eq(value)); } -TEST(value, default_value_string) { +TEST(value, defaultValueString) { const std::string value{"value"}; clapp::value::default_value_t<std::string> dv{value}; std::stringstream ss; @@ -341,7 +341,7 @@ TEST(value, default_value_string) { ASSERT_THAT(dv.default_value(), testing::Eq(value)); } -TEST(value, default_value_milliseconds) { +TEST(value, defaultValueMilliseconds) { constexpr std::uint64_t value{30}; clapp::value::default_value_t<std::chrono::milliseconds> dv{ std::chrono::milliseconds{value}}; @@ -352,7 +352,7 @@ TEST(value, default_value_milliseconds) { testing::Eq(std::chrono::milliseconds{value})); } -TEST(value, min_max_value_uint8_t) { +TEST(value, minMaxValueUint8T) { constexpr std::uint8_t min{10}; constexpr std::uint8_t max{50}; clapp::value::min_max_value_t<std::uint8_t> mmv{min, max}; @@ -367,7 +367,7 @@ TEST(value, min_max_value_uint8_t) { ASSERT_THROW(mmv.validate(55, "option"), clapp::exception::out_of_range_t); } -TEST(value, min_max_value_int32_t) { +TEST(value, minMaxValueInt32T) { constexpr std::int32_t min{-1024}; constexpr std::int32_t max{50}; clapp::value::min_max_value_t<std::int32_t> mmv{min, max}; @@ -382,9 +382,10 @@ TEST(value, min_max_value_int32_t) { ASSERT_THROW(mmv.validate(55, "option"), clapp::exception::out_of_range_t); } -TEST(value, min_max_value_double_t) { +TEST(value, minMaxValueDoubleT) { const double min{100.}; const double max{200.5}; + const double above{max + 0.1}; clapp::value::min_max_value_t<double> mmv{min, max}; std::stringstream ss; ss << "constraint: [" << min << "," << max << "]"; @@ -393,11 +394,11 @@ TEST(value, min_max_value_double_t) { EXPECT_NO_THROW(mmv.validate(max, "option")); EXPECT_NO_THROW(mmv.validate(130, "option")); ASSERT_THROW(mmv.validate(5, "option"), clapp::exception::out_of_range_t); - ASSERT_THROW(mmv.validate(200.6, "option"), + ASSERT_THROW(mmv.validate(above, "option"), clapp::exception::out_of_range_t); } -TEST(value, min_max_value_milliseconds_t) { +TEST(value, minMaxValueMillisecondsT) { const std::chrono::milliseconds min{std::chrono::milliseconds{100}}; const std::chrono::milliseconds max{std::chrono::milliseconds{200}}; clapp::value::min_max_value_t<std::chrono::milliseconds> mmv{min, max}; @@ -413,7 +414,7 @@ TEST(value, min_max_value_milliseconds_t) { clapp::exception::out_of_range_t); } -TEST(value, path_exists_t) { +TEST(value, pathExistsT) { clapp::value::path_exists_t pe; ASSERT_THAT(pe.append_restriction_text(), testing::StrEq("existing path")); EXPECT_NO_THROW(pe.validate(clapp::fs::path{"/tmp"}, "option")); @@ -422,7 +423,7 @@ TEST(value, path_exists_t) { clapp::exception::path_does_not_exist_t); } -TEST(value, found_func_t) { +TEST(value, foundFuncT) { std::stringstream ss; clapp::value::found_func_t ff{[&ss]() { ss << "called func"; }}; ff.found(); -- GitLab