From c451594cdaf80476b7289a542141c56416c36cfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20W=C3=B6lzer?= <martin@libclapp.org> Date: Tue, 28 Jan 2020 06:18:48 +0100 Subject: [PATCH] added new features: - new exceptions based on clapp::exception::clapp_exception. - added `parse_and_validate()` function. - added found-func - added help-option - added more constexpr - added more comments to examples - code cleanup - bump version t0 0.1.0 - added gitlab-ci pipeline - added double/float values - added support for vector of short/long options - added doc-extractor --- .clang-format | 148 +++++ .gitlab-ci.yml | 573 +++++++++++++++++++ CMakeLists.txt | 66 ++- README.md | 38 +- doc/CMakeLists.txt | 6 + doc/doc.md | 52 ++ doc/sample_extractor.p6 | 28 + examples/CMakeLists.txt | 12 + examples/full_example.cpp | 278 +++++---- examples/short_example.cpp | 127 +++-- examples/sub_parser_example.cpp | 173 +++--- libclapp.pc.in | 2 +- src/clapp/CMakeLists.txt | 16 +- src/clapp/exception.cpp | 178 ++++++ src/clapp/main_parser.cpp | 10 + src/clapp/option.cpp | 20 +- src/clapp/parser.cpp | 166 +++--- src/clapp/sub_parser.cpp | 23 +- src/clapp/value.cpp | 152 ++++- src/include/clapp/CMakeLists.txt | 1 + src/include/clapp/argument.h | 49 +- src/include/clapp/argument.hpp | 51 +- src/include/clapp/build_info.h.in | 4 +- src/include/clapp/exception.h | 136 +++++ src/include/clapp/filesystem.h | 12 +- src/include/clapp/main_parser.h | 10 +- src/include/clapp/option.h | 182 ++++-- src/include/clapp/option.hpp | 258 ++++++--- src/include/clapp/parser.h | 69 ++- src/include/clapp/parser.hpp | 94 ++-- src/include/clapp/sub_parser.h | 12 +- src/include/clapp/sub_parser.hpp | 4 +- src/include/clapp/type_traits.h | 43 +- src/include/clapp/value.h | 28 +- src/include/clapp/value.hpp | 127 +++-- tests/CMakeLists.txt | 9 +- tests/argument.cpp | 423 ++++++++++---- tests/clapp.cpp | 9 - tests/exception.cpp | 172 ++++++ tests/main_parser.cpp | 21 +- tests/option.cpp | 902 +++++++++++++++++++++--------- tests/parser.cpp | 442 +++++++++++++-- tests/sub_parser.cpp | 155 +++-- tests/value.cpp | 215 ++++++- 44 files changed, 4304 insertions(+), 1192 deletions(-) create mode 100644 .clang-format create mode 100644 .gitlab-ci.yml create mode 100644 doc/CMakeLists.txt create mode 100755 doc/sample_extractor.p6 create mode 100644 src/clapp/exception.cpp create mode 100644 src/include/clapp/exception.h delete mode 100644 tests/clapp.cpp create mode 100644 tests/exception.cpp diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..c5c4c9d1 --- /dev/null +++ b/.clang-format @@ -0,0 +1,148 @@ +--- +Language: Cpp +# BasedOnStyle: Google +AccessModifierOffset: -1 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Left +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: true +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Attach +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 80 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: true +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^<ext/.*\.h>' + Priority: 2 + - Regex: '^<.*\.h>' + Priority: 1 + - Regex: '^<.*' + Priority: 2 + - Regex: '.*' + Priority: 3 +IncludeIsMainRegex: '([-_](test|unittest))?$' +IndentCaseLabels: true +IndentPPDirectives: None +IndentWidth: 4 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Never +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 1 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 200 +PointerAlignment: Left +RawStringFormats: + - Language: Cpp + Delimiters: + - cc + - CC + - cpp + - Cpp + - CPP + - 'c++' + - 'C++' + CanonicalDelimiter: '' + BasedOnStyle: google + - Language: TextProto + Delimiters: + - pb + - PB + - proto + - PROTO + EnclosingFunctions: + - EqualsProto + - EquivToProto + - PARSE_PARTIAL_TEXT_PROTO + - PARSE_TEST_PROTO + - PARSE_TEXT_PROTO + - ParseTextOrDie + - ParseTextProtoOrDie + CanonicalDelimiter: '' + BasedOnStyle: google +ReflowComments: true +SortIncludes: true +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Auto +TabWidth: 8 +UseTab: Never +... + diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 00000000..34e13ad9 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,573 @@ +stages: + - build + - test +variables: + GIT_STRATEGY: clone + GIT_SUBMODULE_STRATEGY: recursive + +compile_install_g++9: + stage: build + 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=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++-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 + +compile_release_g++9: + stage: build + 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=Release .. + - make -j2 VERBOSE=1 + tags: + - g++-9 + - cmake + artifacts: + paths: + - build + +test_release_g++9: + stage: test + script: + - 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 + +compile_debug_g++9: + stage: build + 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 + +test_debug_g++9: + stage: test + script: + - cd build + - ./tests/libclapp_tests + - gcovr -r ../ -e ../third_party/ -e ../tests/ -e ../examples/ --gcov-executable="gcov-9" + tags: + - g++-9 + - cmake + coverage: '/^TOTAL.*\s+(\d+%)$/' + dependencies: + - compile_debug_g++9 + +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 + tags: + - g++-9 + - cmake + artifacts: + paths: + - build + +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++ - + tags: + - g++-8 + - cmake + artifacts: + paths: + - build + +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 + tags: + - g++-8 + - cmake + artifacts: + paths: + - build + +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" + tags: + - g++-8 + - cmake + dependencies: + - compile_release_g++8 + +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 + tags: + - g++-8 + - cmake + artifacts: + paths: + - build + +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 + tags: + - g++-8 + - cmake + artifacts: + paths: + - build + +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" + tags: + - g++-8 + - cmake + coverage: '/^TOTAL.*\s+(\d+%)$/' + dependencies: + - compile_debug_g++8 + +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++ - + tags: + - g++-7 + - cmake + artifacts: + paths: + - build + +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 + tags: + - g++-7 + - cmake + artifacts: + paths: + - build + +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" + tags: + - g++-7 + - cmake + dependencies: + - compile_release_g++7 + +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 + tags: + - g++-7 + - cmake + artifacts: + paths: + - build + +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" + tags: + - g++-7 + - cmake + coverage: '/^TOTAL.*\s+(\d+%)$/' + dependencies: + - compile_debug_g++7 + +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 + tags: + - g++-7 + - cmake + artifacts: + paths: + - build + +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++ - + tags: + - clang++-9 + - cmake + artifacts: + paths: + - build + +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 + tags: + - clang++-9 + - cmake + artifacts: + paths: + - build + +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" + tags: + - clang++-9 + - cmake + coverage: '/^TOTAL.*\s+(\d+%)$/' + dependencies: + - compile_debug_clang++9 + +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 + tags: + - clang++-8 + - cmake + artifacts: + paths: + - build + +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 + tags: + - clang++-9 + - cmake + artifacts: + paths: + - build + +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" + tags: + - clang++-9 + - cmake + dependencies: + - compile_release_clang++9 + +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 + tags: + - clang++-9 + - cmake + artifacts: + paths: + - build + +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++ - + tags: + - clang++-8 + - cmake + artifacts: + paths: + - build + +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 + tags: + - clang++-8 + - cmake + artifacts: + paths: + - build + +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" + tags: + - clang++-8 + - cmake + coverage: '/^TOTAL.*\s+(\d+%)$/' + dependencies: + - compile_debug_clang++8 + +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 + tags: + - clang++-8 + - cmake + artifacts: + paths: + - build + +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 + tags: + - clang++-8 + - cmake + artifacts: + paths: + - build + +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" + tags: + - clang++-8 + - cmake + dependencies: + - compile_release_clang++8 + +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 + tags: + - clang++-8 + - cmake + artifacts: + paths: + - build + +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++ - + tags: + - clang++-7 + - cmake + artifacts: + paths: + - build + +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 + tags: + - clang++-7 + - cmake + artifacts: + paths: + - build + +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" + tags: + - clang++-7 + - cmake + coverage: '/^TOTAL.*\s+(\d+%)$/' + dependencies: + - compile_debug_clang++7 + +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 + tags: + - clang++-7 + - cmake + artifacts: + paths: + - build + +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 + tags: + - clang++-7 + - cmake + artifacts: + paths: + - build + +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" + tags: + - clang++-7 + - cmake + dependencies: + - compile_release_clang++7 + +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 + tags: + - clang++-7 + - cmake + artifacts: + paths: + - build diff --git a/CMakeLists.txt b/CMakeLists.txt index d1229371..a9f856ef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,13 +13,17 @@ if (NOT CMAKE_BUILD_TYPE) endif() set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -set(CMAKE_CXX_FLAGS_DEBUG "-ggdb -O0 -DGSL_THROW_ON_CONTRACT_VIOLATION") +set(CMAKE_CXX_FLAGS_DEBUG "-ggdb -O0") set(CMAKE_CXX_FLAGS_RELEASE "-O2 -DNDEBUG") if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - add_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 -Wuninitialized -Wwrite-strings) + 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) + set(libClaPP_COMPILE_OPTIONS ${libClaPP_TEST_COMPILE_OPTIONS}) + list(APPEND libClaPP_COMPILE_OPTIONS -Weffc++) elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - add_compile_options(-Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-padded) + list(APPEND libClaPP_COMPILE_OPTIONS -Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-padded -Werror -pedantic-errors) + set(libClaPP_TEST_COMPILE_OPTIONS ${libClaPP_COMPILE_OPTIONS}) + list(APPEND libClaPP_TEST_COMPILE_OPTION -Wno-global-constructors) endif() set(CMAKE_CXX_STANDARD 17) @@ -31,20 +35,40 @@ if(COMPILER_SUPPORTS_FILESYSTEM) set(libClaPP_FS_LINKER_FLAG "-lstdc++fs") endif() +option(libClaPP_CLANG_TIDY "Clang-Tidy." 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) if(libClaPP_BUILD_COVERAGE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage") - set(CMAKE_EXE_LINKER_FLAGS "-fprofile-arcs -ftest-coverage") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fprofile-arcs -ftest-coverage") +endif() + +if(libClaPP_CLANG_TIDY) + if(NOT CLANG_TIDY) + find_program(CLANG_TIDY NAMES "clang-tidy" DOC "Path to clang-tidy executable") + + if(NOT CLANG_TIDY) + message(STATUS "clang-tidy not found.") + else() + message(STATUS "found CLANG_TIDY by find_program().") + endif() + endif() + + 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") + endif() endif() find_package(PkgConfig REQUIRED) set (project_VERSION_MAJOR 0) -set (project_VERSION_MINOR 0) +set (project_VERSION_MINOR 1) set (project_VERSION_PATCH_LEVEL 0) string(TIMESTAMP project_CONFIG_TIME "%Y-%m-%d %H:%M:%S") @@ -57,9 +81,17 @@ if(libClaPP_SUBMODULE_DEPENDENCIES) add_subdirectory("${THIRD_PARTY_DIR}/GSL" EXCLUDE_FROM_ALL) endif() target_link_libraries(libClaPP_GSL INTERFACE GSL) + if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + target_compile_options(libClaPP_GSL INTERFACE -Wno-error=weak-vtables) + endif() if(libClaPP_BUILD_TESTS) - if(NOT TARGET gtest_main) + if(NOT TARGET gmock_main) add_subdirectory("${THIRD_PARTY_DIR}/googletest" EXCLUDE_FROM_ALL) + add_library(libClaPP_gmock_main INTERFACE) + target_link_libraries(libClaPP_gmock_main INTERFACE gmock_main) + if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + target_compile_options(libClaPP_gmock_main INTERFACE -Wno-error=used-but-marked-unused -Wno-error=covered-switch-default -Wno-error=global-constructors -Wno-gnu-zero-variadic-macro-arguments) + endif() endif() endif() else() @@ -73,20 +105,22 @@ else() target_include_directories(libClaPP_GSL INTERFACE ${libClaPP_GSL_INCLUDE_DIR}/..) endif() if(libClaPP_BUILD_TESTS) - pkg_check_modules(GTEST REQUIRED gtest>=1.10.0) - add_library(gtest_main INTERFACE) - target_include_directories(gtest_main INTERFACE ${GTEST_INCLUDE_DIRS}) - target_link_libraries(gtest_main INTERFACE ${GTEST_LIBRARIES}) - - pkg_check_modules(GMOCK REQUIRED gmock>=1.10.0) - add_library(gmock INTERFACE) - target_include_directories(gmock INTERFACE ${GMOCK_INCLUDE_DIRS}) - target_link_libraries(gmock INTERFACE ${GMOCK_LIBRARIES}) + pkg_check_modules(GMOCK REQUIRED gmock_main>=1.10.0) + add_library(libClaPP_gmock_main INTERFACE) + target_include_directories(libClaPP_gmock_main SYSTEM INTERFACE ${GMOCK_INCLUDE_DIRS}) + target_link_libraries(libClaPP_gmock_main INTERFACE ${GMOCK_LIBRARIES}) endif() endif() add_subdirectory(src) -add_subdirectory(examples) + +if(libClaPP_BUILD_EXAMPLES) + add_subdirectory(examples) +endif() + +if(libClaPP_BUILD_DOC_CODE) + add_subdirectory(doc) +endif() configure_file(${CMAKE_CURRENT_SOURCE_DIR}/libclapp.pc.in ${CMAKE_CURRENT_BINARY_DIR}/libclapp.pc @ONLY) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libclapp.pc DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig/") diff --git a/README.md b/README.md index d3351464..405d568d 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,15 @@ - _____ _ _____ _____ - / ____|| | __ _ | __ \ | __ \ + _____ _ _____ _____ + / ____|| | __ _ | __ \ | __ \ | | | |/ _` || |__) || |__) | - | | |_|\__,_|| ___/ | ___/ - | |____ | | | | - \_____| |_| |_| + | | |_|\__,_|| ___/ | ___/ + | |____ | | | | + \_____| |_| |_| libClaPP: ========= +[](../commits/master/) +[](../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. @@ -36,6 +38,27 @@ But if all dependencies are installed on your system, the following steps are su cmake .. make +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 + gcovr -r ../ -e ../third_party/ -e ../tests/ -e ../examples/ + +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 + gcovr -r ../ -e ../third_party/ -e ../tests/ -e ../examples/ + + ### Install the library To install the library on your system, type the following command. @@ -69,3 +92,8 @@ Coverage: cmake -DlibClaPP_BUILD_COVERAGE=On -DCMAKE_BUILD_TYPE=Debug -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DlibClaPP_BUILD_TESTS=On .. gcovr -r ../ -e ../third_party/ -e ../tests/ -e ../examples/ + +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 .. diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt new file mode 100644 index 00000000..1303f269 --- /dev/null +++ b/doc/CMakeLists.txt @@ -0,0 +1,6 @@ +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}") +endif() diff --git a/doc/doc.md b/doc/doc.md index 5555f0e3..606b5d46 100644 --- a/doc/doc.md +++ b/doc/doc.md @@ -185,3 +185,55 @@ This classes must contain at least one of these methods: The returend strig will be appended to the description of the argumen or option. * `void validate(const T &value, const std::string ¶m_name) const;` If this method is defined, it will be called during the `validate()`-call of the parser. + +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 +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. + +[//]:#begin_cpp_listing_simple_main_parser +```c++ +#include <clapp/argument.h> +#include <clapp/main_parser.h> +#include <clapp/option.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::string_argument_t string_arg{*this, "string-arg", "String argument"}; + + //explicitly delete copy/move-ctors and assignmet operators + 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; + +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 + std::cout << "string-arg: " << cp.string_arg.value() << std::endl; + } catch (std::exception &e) { + std::cout << "Caught Exception: " << e.what() << std::endl; + } +} +``` +[//]:#end_cpp_listing_simple_main_parser diff --git a/doc/sample_extractor.p6 b/doc/sample_extractor.p6 new file mode 100755 index 00000000..dd71a33c --- /dev/null +++ b/doc/sample_extractor.p6 @@ -0,0 +1,28 @@ +#!/usr/bin/perl6 + +sub MAIN($doc_dir) { + chdir $doc_dir; + for dir(test => /^ .* \. cpp$/) -> $file { + say "Removing " ~ $file; + unlink($file); + } + + my Str $listing_name; + + for 'doc.md'.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 ~ "'."; + $listing_name=Nil; + } + if $listing_name { + unless $line ~~ /^^\`\`\`/ { + spurt $listing_name ~ ".cpp", $line ~ "\n", :append; + } + } + if !$listing_name and $line ~~ /^^\[\/\/\]\:\#begin_cpp_listing_$<name>=[\w+]$$/ { + $listing_name = $<name>.Str; + } + } +} diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 0936bb0d..3c8ceabb 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,13 +1,25 @@ 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}) + if(CLANG_TIDY) + set_target_properties(libclapp_example_full PROPERTIES CXX_CLANG_TIDY "${CLANG_TIDY_ARGS}") + endif() install(TARGETS libclapp_example_full DESTINATION ${CMAKE_INSTALL_BINDIR}) 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}") +endif() install(TARGETS libclapp_example_short DESTINATION ${CMAKE_INSTALL_BINDIR}) 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}") +endif() install(TARGETS libclapp_example_sub_parser DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/examples/full_example.cpp b/examples/full_example.cpp index e55267ef..7597be24 100644 --- a/examples/full_example.cpp +++ b/examples/full_example.cpp @@ -7,7 +7,8 @@ #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 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>; @@ -38,16 +39,29 @@ struct restriction_t { class cli_parser_t : public clapp::basic_main_parser_t { public: - using clapp::basic_main_parser_t::basic_main_parser_t; + cli_parser_t() = default; + cli_parser_t(int argc, const char *const *argv) : cli_parser_t{} { parse(argc, argv); } - ~cli_parser_t(); + + 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; - ~cmd1_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}; @@ -56,25 +70,30 @@ class cli_parser_t : public clapp::basic_main_parser_t { 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}, std::int32_t{10}}}; - string_argument_t string_arg_x{*this, "string-arg-x", - "String argument x", purpose_t::optional, - clapp::default_value_t{"abaa"}}; + 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; - ~cmd2_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{"default-string-arg-x"}}; + 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}, std::int32_t{10}}}; + 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 }; @@ -102,13 +121,27 @@ class cli_parser_t : public clapp::basic_main_parser_t { purpose_t::mandatory, restriction_t{}}; - clapp::count_option_t verbose{ - *this, - "verbose", - 'v', - "Verbose option.", - clapp::min_max_value_t{std::size_t{0}, std::size_t{7}}, - clapp::default_value_t{std::size_t{2}}}; + 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}; @@ -132,19 +165,19 @@ class cli_parser_t : public clapp::basic_main_parser_t { 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{10}}; + 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}, std::int32_t{20}}}; + 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>(const std::string_view param); +entry_t clapp::value::convert_value<entry_t>(std::string_view param); void process_cmd1(const cli_parser_t::cmd1_parser_t &cmd1); @@ -167,11 +200,11 @@ template <> entry_t clapp::value::convert_value<entry_t>(const std::string_view param) { if (param == "entry1") { return entry_t::entry1; - } else if (param == "entry2") { + } + if (param == "entry2") { return entry_t::entry2; - } else { - throw std::runtime_error("Invalid enum param type."); } + throw std::runtime_error("Invalid enum param type."); } void process_cmd1(const cli_parser_t::cmd1_parser_t &cmd1) { @@ -235,119 +268,126 @@ void process_cmd2(const cli_parser_t::cmd2_parser_t &cmd2) { } int main(int argc, char *argv[]) { - std::cout << clapp::project_name << " " << clapp::version << " (" - << clapp::git_branch << ":" << clapp::git_hash << "), " - << clapp::build_type << 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) { - std::cout << "Usage: \n" - << cp.get_executable() << ' ' << cp.gen_help_msg(); - return EXIT_SUCCESS; - } + try { + std::cout << clapp::project_name << " " << clapp::version << " (" + << clapp::git_branch << ":" << clapp::git_hash << "), " + << clapp::build_type << 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; + } - cp.validate(); + if (cp.help) { + std::cout << "Usage: \n" + << cp.get_executable() << ' ' << cp.gen_help_msg(); + return EXIT_SUCCESS; + } - if (cp.string_arg) { - std::cout << "string-arg: " << cp.string_arg.value() << "\n"; - } else { - std::cout << "string-arg: not given\n"; - } + cp.validate(); - if (cp.verbose) { - std::cout << "verbose: " << cp.verbose.value() << "\n"; - } else { - std::cout << "verbose: not given\n"; - } + if (cp.string_arg) { + std::cout << "string-arg: " << cp.string_arg.value() << "\n"; + } else { + std::cout << "string-arg: 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.verbose) { + std::cout << "verbose: " << cp.verbose.value() << "\n"; + } else { + std::cout << "verbose: 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 << ", "; + if (cp.string_param) { + std::cout << "string_param: '" << cp.string_param.value() << "'\n"; + } else { + std::cout << "string_param: not given\n"; } - std::cout << "\n"; - } else { - std::cout << "string_vector_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"; - if (cp.short_bool.given()) { - std::cout << "short_bool: given\n"; - } else { - std::cout << "short_bool: not given\n"; - } + } else { + std::cout << "string_vector_param: not given\n"; + } - if (cp.restricted_bool) { - std::cout << "restricted_bool: given\n"; - } else { - std::cout << "restricted_bool: not given\n"; - } + if (cp.short_bool.given()) { + std::cout << "short_bool: given\n"; + } else { + std::cout << "short_bool: not given\n"; + } - if (cp.long_bool) { - std::cout << "long_bool: " << cp.long_bool.value() << "\n"; - } else { - std::cout << "long_bool: not given\n"; - } + if (cp.restricted_bool) { + std::cout << "restricted_bool: given\n"; + } else { + std::cout << "restricted_bool: not given\n"; + } - if (cp.count) { - std::cout << "count: " << cp.count.value() << "\n"; - } else { - std::cout << "count: not given\n"; - } + if (cp.long_bool) { + std::cout << "long_bool: " << cp.long_bool.value() << "\n"; + } else { + std::cout << "long_bool: not given\n"; + } + + if (cp.count) { + 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"; - } + 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"; + 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"; + 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"; + 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"; - } + if (cp.optional_int) { + std::cout << "optional_int: " << cp.optional_int.value() << "\n"; + } - std::cout << "entry_param: '" << cp.entry_param.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.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; + 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; + } catch (std::exception &e) { + std::cout << "Caught Exception: " << e.what() << std::endl; } } diff --git a/examples/short_example.cpp b/examples/short_example.cpp index 4d7de979..2b18a70d 100644 --- a/examples/short_example.cpp +++ b/examples/short_example.cpp @@ -1,31 +1,60 @@ #include <clapp/argument.h> +#include <clapp/build_info.h> #include <clapp/main_parser.h> #include <clapp/option.h> #include <iostream> +[[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; + exit(EXIT_SUCCESS); +} + class cli_parser_t : public clapp::basic_main_parser_t { public: - using clapp::basic_main_parser_t::basic_main_parser_t; - ~cli_parser_t(); - cli_parser_t(int argc, const char *const *argv) : cli_parser_t{} { - parse(argc, argv); + cli_parser_t(int argc, const char *const *argv) { + parse_and_validate(argc, argv); } - clapp::bool_option_t help{*this, "help", 'h', "Show help options."}; + 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; + + // if help is given, help is printed and exit(EXIT_SUCCESS) is called + clapp::help_option_t help{*this, std::vector<std::string>{"help", "usage"}, + std::vector<char>{'h', '?'}, + "Show help options."}; + // if version is given, print_version_and_exit() is called. + clapp::bool_option_t version{ + *this, std::vector<std::string>{"version", "vers"}, 'v', + "Show version info", + clapp::value::found_func_t{print_version_and_exit}}; + + // first mandatory argument of string clapp::string_argument_t string_arg{*this, "string-arg", "String argument"}; + // second mandatory argument of int32_t clapp::int32_argument_t int_arg{ *this, "int-arg", "Int argument", - clapp::min_max_value_t{std::int32_t{5}, std::int32_t{10}}}; + clapp::min_max_value_t<std::int32_t>{5, 10}}; + // third optional variadic arguments of strings clapp::variadic_string_argument_t variadic_string_arg{ *this, "variadic-string-arg", "Variadic String argument", purpose_t::optional}; + // mandatory string option clapp::string_param_option_t string_param{ - *this, "string", 's', "String option 1.", purpose_t::mandatory}; + *this, "string", std::vector<char>{'s', '1'}, "String option 1.", + purpose_t::mandatory}; + // optional string option (multiple string vectors) clapp::vector_string_param_option_t string_vector_param{ *this, "string-vector", "String vector param."}; }; @@ -33,54 +62,46 @@ class cli_parser_t : public clapp::basic_main_parser_t { cli_parser_t::~cli_parser_t() = default; int main(int argc, char *argv[]) { - cli_parser_t cp{argc, argv}; - - if (cp.help) { - std::cout << "Usage: \n" - << cp.get_executable() << ' ' << cp.gen_help_msg(); - 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.int_arg) { - std::cout << "int-arg: " << cp.int_arg.value() << "\n"; - } else { - std::cout << "int-arg: not given\n"; - } - - if (cp.variadic_string_arg) { - std::cout << "variadic-string-arg (size: " - << cp.variadic_string_arg.value().size() << "): "; - for (auto &val : cp.variadic_string_arg.value()) { - std::cout << val << ", "; + try { + cli_parser_t cp{argc, argv}; // parses and validates cli-arguments + + Expects(cp.string_arg); // parser ensures mandatory arguments are given + std::cout << "string-arg: " << cp.string_arg.value() << std::endl; + + Expects(cp.int_arg); // parser ensures mandatory arguments are given + std::cout << "int-arg: " << cp.int_arg.value() << std::endl; + + if (cp.variadic_string_arg) { // if variadic_string_arg is given + std::cout << "variadic-string-arg (size: " + << cp.variadic_string_arg.value().size() << "): "; + // iterate over the vector of arguments + for (auto &val : cp.variadic_string_arg.value()) { + std::cout << val << ", "; + } + std::cout << std::endl; + } else { + std::cout << "variadic-string-arg: not given" << std::endl; } - std::cout << "\n"; - } else { - std::cout << "variadic-string-arg: 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 << ", "; + Expects(cp.string_param); // The parser ensures that mandatory options + // are given + std::cout << "string_param: '" << cp.string_param.value() << "'" + << std::endl; + + if (cp.string_vector_param) { // if string_vector_param is given + std::cout << "string_vector_param (size: " + << cp.string_vector_param.value().size() << "): "; + // iterate over the vector of options + for (auto &val : cp.string_vector_param.value()) { + std::cout << val << ", "; + } + std::cout << std::endl; + } else { + std::cout << "string_vector_param: not given" << std::endl; } - std::cout << "\n"; - - } else { - std::cout << "string_vector_param: not given\n"; + } catch (clapp::exception::clapp_exception_t &e) { + std::cout << "Caught ClaPP-Exception: " << e.what() << std::endl; + } catch (std::exception &e) { + std::cout << "Caught Exception: " << e.what() << std::endl; } } diff --git a/examples/sub_parser_example.cpp b/examples/sub_parser_example.cpp index 4b38c9a4..a4d59e90 100644 --- a/examples/sub_parser_example.cpp +++ b/examples/sub_parser_example.cpp @@ -1,60 +1,108 @@ #include <clapp/argument.h> +#include <clapp/build_info.h> #include <clapp/main_parser.h> #include <clapp/option.h> #include <clapp/sub_parser.h> #include <iostream> +[[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; + exit(EXIT_SUCCESS); +} + class cli_parser_t : public clapp::basic_main_parser_t { public: using clapp::basic_main_parser_t::basic_main_parser_t; + cli_parser_t() = default; + cli_parser_t(int argc, const char *const *argv) : cli_parser_t{} { - parse(argc, argv); + parse_and_validate(argc, argv); } - ~cli_parser_t(); + 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; - clapp::bool_option_t help{*this, "help", 'h', "Show help options."}; + // if help is given, help is printed and exit(EXIT_SUCCESS) is called + clapp::help_option_t help{*this, "help", 'h', "Show help options."}; - clapp::count_option_t verbose{ - *this, - "verbose", - 'v', - "Verbose option.", - clapp::min_max_value_t{std::size_t{0}, std::size_t{7}}, - clapp::default_value_t{std::size_t{2}}}; + // if version is given, print_version_and_exit() is called. + clapp::bool_option_t version{ + *this, "version", "Show version info", + clapp::value::found_func_t{print_version_and_exit}}; + 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}}; + + // first subparser declaration class first_parser_t : public clapp::basic_sub_parser_t { public: using clapp::basic_sub_parser_t::basic_sub_parser_t; - ~first_parser_t(); - clapp::bool_option_t help{*this, "help", 'h', "Show help options."}; + + explicit first_parser_t(const first_parser_t &) = delete; + explicit first_parser_t(first_parser_t &&) noexcept = delete; + first_parser_t &operator=(const first_parser_t &) = delete; + first_parser_t &operator=(first_parser_t &&) noexcept = delete; + + ~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.", purpose_t::mandatory}; + clapp::double_param_option_t double_opt{ + *this, 'd', "Double param option", + clapp::default_value_t<double>{10}}; + clapp::string_param_option_t string{*this, 's', "String param option."}; clapp::string_argument_t string_arg_x{ *this, "string-arg-x", "String argument x", purpose_t::optional, - clapp::default_value_t{"abaa"}}; + clapp::default_value_t<std::string>{"abaa"}}; }; + // second subparser declaration class second_parser_t : public clapp::basic_sub_parser_t { public: using clapp::basic_sub_parser_t::basic_sub_parser_t; - ~second_parser_t(); - clapp::bool_option_t help{*this, "help", 'h', "Show help options."}; + + explicit second_parser_t(const second_parser_t &) = delete; + explicit second_parser_t(second_parser_t &&) noexcept = delete; + second_parser_t &operator=(const second_parser_t &) = delete; + second_parser_t &operator=(second_parser_t &&) noexcept = delete; + + ~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{ *this, "int-arg", "Int argument", - clapp::min_max_value_t{std::int32_t{5}, std::int32_t{10}}}; + clapp::min_max_value_t<std::int32_t>{5, 10}}; + + clapp::double_argument_t double_arg{ + *this, "double-arg", "Double argument", + clapp::min_max_value_t<double>{-1000., 1000.}}; clapp::string_argument_t string_arg_x{ *this, "string-arg-x", "String argument x", purpose_t::optional, - clapp::default_value_t{"default-string-arg-x"}}; + clapp::default_value_t<std::string>{"default-string-arg-x"}}; clapp::variadic_int64_argument_t variadic_int_arg{ *this, "variadic-int-arg", "Int argument", purpose_t::optional, - clapp::min_max_value_t{std::int64_t{5}, std::int64_t{10}}}; + clapp::min_max_value_t<std::int64_t>{5, 10}}; }; first_parser_t first{*this, "first", "First parser."}; @@ -69,21 +117,10 @@ void process_first(const cli_parser_t::first_parser_t &first); void process_second(const cli_parser_t::second_parser_t &second); void process_first(const cli_parser_t::first_parser_t &first) { - if (first.help) { - std::cout << "Usage: \n" - << first.get_sub_parser_name() << ' ' << first.gen_help_msg(); - return; - } - - first.validate(); - - if (first.short_bool) { - std::cout << "short_bool: " << first.short_bool.value() << "\n"; - } else { - std::cout << "short_bool: not given\n"; - } + Expects(first.short_bool); // parser ensures mandatory options are given + std::cout << "short_bool: " << first.short_bool.value() << "\n"; - if (first.string) { + if (first.string) { // if optional string option is given std::cout << "string: " << first.string.value() << "\n"; } else { std::cout << "string: not given\n"; @@ -94,31 +131,25 @@ void process_first(const cli_parser_t::first_parser_t &first) { } else { std::cout << "string-arg-x: not given\n"; } + + Expects(first.double_opt); + std::cout << "double-opt: " << first.double_opt.value() << "\n"; } void process_second(const cli_parser_t::second_parser_t &second) { - if (second.help) { - std::cout << "Usage: \n" - << second.get_sub_parser_name() << ' ' - << second.gen_help_msg(); - return; - } + Expects(second.int_arg); // parser ensures mandatory arguments are given + std::cout << "int-arg: " << second.int_arg.value() << "\n"; - second.validate(); + Expects(second.double_arg); // parser ensures mandatory arguments are given + std::cout << "double-arg: " << second.double_arg.value() << "\n"; - if (second.int_arg) { - std::cout << "int-arg: " << second.int_arg.value() << "\n"; - } else { - std::cout << "int-arg: not given\n"; - } - - if (second.string_arg_x) { + if (second.string_arg_x) { // if optional string-arg-x option is given std::cout << "string-arg-x: " << second.string_arg_x.value() << "\n"; } else { std::cout << "string-arg-x: not given\n"; } - if (second.variadic_int_arg) { + if (second.variadic_int_arg) { // if optional variadic int argument given std::cout << "variadic-int-arg (size: " << second.variadic_int_arg.value().size() << "): "; for (auto &val : second.variadic_int_arg.value()) { @@ -131,33 +162,31 @@ void process_second(const cli_parser_t::second_parser_t &second) { } int main(int argc, char *argv[]) { - cli_parser_t cp{argc, argv}; - - if (cp.help) { - std::cout << "Usage: \n" - << cp.get_executable() << ' ' << cp.gen_help_msg(); - return EXIT_SUCCESS; - } + try { + cli_parser_t cp{argc, argv}; - cp.validate(); - - if (cp.verbose) { - std::cout << "verbose: " << cp.verbose.value() << "\n"; - } else { - std::cout << "verbose: not given\n"; - } + if (cp.verbose) { // if the optional verbose option is given + std::cout << "verbose: " << cp.verbose.value() << "\n"; + } else { + std::cout << "verbose: not given\n"; + } - if (cp.first) { - std::cout << "first parser active" << std::endl; - process_first(cp.first); - } else { - std::cout << "first parser not active" << std::endl; - } + if (cp.first) { // if the first sub-parser is selected + std::cout << "first parser active" << std::endl; + process_first(cp.first); + } else { + std::cout << "first parser not active" << std::endl; + } - if (cp.second) { - std::cout << "second parser active" << std::endl; - process_second(cp.second); - } else { - std::cout << "second parser not active" << std::endl; + if (cp.second) { // if the first sub-parser is selected + std::cout << "second parser active" << std::endl; + process_second(cp.second); + } else { + std::cout << "second parser not active" << std::endl; + } + } catch (clapp::exception::clapp_exception_t &e) { + std::cout << "Caught ClaPP-Exception: " << e.what() << std::endl; + } catch (std::exception &e) { + std::cout << "Caught Exception: " << e.what() << std::endl; } } diff --git a/libclapp.pc.in b/libclapp.pc.in index 43a02197..edb5617b 100644 --- a/libclapp.pc.in +++ b/libclapp.pc.in @@ -5,5 +5,5 @@ Name: @CMAKE_PROJECT_NAME@ Description: libClaPP is an open source command line argument processing library for C++. Version: @project_VERSION_MAJOR@.@project_VERSION_MINOR@.@project_VERSION_PATCH_LEVEL@ Requires: -Libs: -L${libdir} -lclapp +Libs: -L${libdir} -lclapp @libClaPP_FS_LINKER_FLAG@ Cflags: -I${includedir} diff --git a/src/clapp/CMakeLists.txt b/src/clapp/CMakeLists.txt index d386b4d7..291ce40a 100644 --- a/src/clapp/CMakeLists.txt +++ b/src/clapp/CMakeLists.txt @@ -1,11 +1,15 @@ -add_library(clapp SHARED option.cpp value.cpp parser.cpp main_parser.cpp sub_parser.cpp) -target_include_directories(clapp PUBLIC - $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../include> - $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/../include> - $<INSTALL_INTERFACE:../include> -) +add_library(clapp SHARED option.cpp value.cpp parser.cpp main_parser.cpp sub_parser.cpp exception.cpp) +set(libClaPP_INCLUDE_DIRS $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../include> + $<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_compile_options(clapp PRIVATE ${libClaPP_COMPILE_OPTIONS}) set_target_properties(clapp PROPERTIES VERSION "${project_VERSION_MAJOR}.${project_VERSION_MINOR}" POSITION_INDEPENDENT_CODE ON) +if(CLANG_TIDY) + set_target_properties(clapp PROPERTIES CXX_CLANG_TIDY "${CLANG_TIDY_ARGS}") +endif() + install(TARGETS clapp LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") diff --git a/src/clapp/exception.cpp b/src/clapp/exception.cpp new file mode 100644 index 00000000..78332ccd --- /dev/null +++ b/src/clapp/exception.cpp @@ -0,0 +1,178 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2020 Martin Wölzer +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +/////////////////////////////////////////////////////////////////////////////// + +#include <clapp/exception.h> + +clapp::exception::clapp_exception_t::clapp_exception_t(const char* message) + : std::runtime_error(message) {} +clapp::exception::clapp_exception_t::clapp_exception_t( + const std::string& message) + : std::runtime_error(message) {} +clapp::exception::clapp_exception_t::clapp_exception_t( + const clapp_exception_t&) = default; +clapp::exception::clapp_exception_t& clapp::exception::clapp_exception_t:: +operator=(const clapp_exception_t&) = default; +clapp::exception::clapp_exception_t::clapp_exception_t( + clapp_exception_t&&) noexcept = default; +clapp::exception::clapp_exception_t& clapp::exception::clapp_exception_t:: +operator=(clapp_exception_t&&) noexcept = default; +clapp::exception::clapp_exception_t::~clapp_exception_t() noexcept = default; + +clapp::exception::invalid_value_t::invalid_value_t(const char* message) + : clapp_exception_t(message) {} +clapp::exception::invalid_value_t::invalid_value_t(const std::string& message) + : clapp_exception_t(message) {} +clapp::exception::invalid_value_t::invalid_value_t(const invalid_value_t&) = + default; +clapp::exception::invalid_value_t& clapp::exception::invalid_value_t::operator=( + const invalid_value_t&) = default; +clapp::exception::invalid_value_t::invalid_value_t(invalid_value_t&&) noexcept = + default; +clapp::exception::invalid_value_t& clapp::exception::invalid_value_t::operator=( + invalid_value_t&&) noexcept = default; +clapp::exception::invalid_value_t::~invalid_value_t() noexcept = default; + +clapp::exception::value_undefined_t::value_undefined_t(const char* message) + : clapp_exception_t(message) {} +clapp::exception::value_undefined_t::value_undefined_t( + const std::string& message) + : clapp_exception_t(message) {} +clapp::exception::value_undefined_t::value_undefined_t( + const value_undefined_t&) = default; +clapp::exception::value_undefined_t& clapp::exception::value_undefined_t:: +operator=(const value_undefined_t&) = default; +clapp::exception::value_undefined_t::value_undefined_t( + value_undefined_t&&) noexcept = default; +clapp::exception::value_undefined_t& clapp::exception::value_undefined_t:: +operator=(value_undefined_t&&) noexcept = default; +clapp::exception::value_undefined_t::~value_undefined_t() noexcept = default; + +clapp::exception::out_of_range_t::out_of_range_t(const char* message) + : invalid_value_t(message) {} +clapp::exception::out_of_range_t::out_of_range_t(const std::string& message) + : invalid_value_t(message) {} +clapp::exception::out_of_range_t::out_of_range_t(const out_of_range_t&) = + default; +clapp::exception::out_of_range_t& clapp::exception::out_of_range_t::operator=( + const out_of_range_t&) = default; +clapp::exception::out_of_range_t::out_of_range_t(out_of_range_t&&) noexcept = + default; +clapp::exception::out_of_range_t& clapp::exception::out_of_range_t::operator=( + out_of_range_t&&) noexcept = default; +clapp::exception::out_of_range_t::~out_of_range_t() noexcept = default; + +clapp::exception::path_does_not_exist_t::path_does_not_exist_t( + const char* message) + : invalid_value_t(message) {} +clapp::exception::path_does_not_exist_t::path_does_not_exist_t( + const std::string& message) + : invalid_value_t(message) {} +clapp::exception::path_does_not_exist_t::path_does_not_exist_t( + const path_does_not_exist_t&) = default; +clapp::exception::path_does_not_exist_t& +clapp::exception::path_does_not_exist_t::operator=( + const path_does_not_exist_t&) = default; +clapp::exception::path_does_not_exist_t::path_does_not_exist_t( + path_does_not_exist_t&&) noexcept = default; +clapp::exception::path_does_not_exist_t& +clapp::exception::path_does_not_exist_t::operator=( + path_does_not_exist_t&&) noexcept = default; +clapp::exception::path_does_not_exist_t::~path_does_not_exist_t() noexcept = + default; + +clapp::exception::option_exception_t::option_exception_t(const char* message) + : clapp_exception_t(message) {} +clapp::exception::option_exception_t::option_exception_t( + const std::string& message) + : clapp_exception_t(message) {} +clapp::exception::option_exception_t::option_exception_t( + const option_exception_t&) = default; +clapp::exception::option_exception_t& clapp::exception::option_exception_t:: +operator=(const option_exception_t&) = default; +clapp::exception::option_exception_t::option_exception_t( + option_exception_t&&) noexcept = default; +clapp::exception::option_exception_t& clapp::exception::option_exception_t:: +operator=(option_exception_t&&) noexcept = default; +clapp::exception::option_exception_t::~option_exception_t() noexcept = default; + +clapp::exception::option_param_exception_t::option_param_exception_t( + const char* message) + : option_exception_t(message) {} +clapp::exception::option_param_exception_t::option_param_exception_t( + const std::string& message) + : option_exception_t(message) {} +clapp::exception::option_param_exception_t::option_param_exception_t( + const option_param_exception_t&) = default; +clapp::exception::option_param_exception_t& +clapp::exception::option_param_exception_t::operator=( + const option_param_exception_t&) = default; +clapp::exception::option_param_exception_t::option_param_exception_t( + option_param_exception_t&&) noexcept = default; +clapp::exception::option_param_exception_t& +clapp::exception::option_param_exception_t::operator=( + option_param_exception_t&&) noexcept = default; +clapp::exception::option_param_exception_t:: + ~option_param_exception_t() noexcept = default; + +clapp::exception::argument_exception_t::argument_exception_t( + const char* message) + : clapp_exception_t(message) {} +clapp::exception::argument_exception_t::argument_exception_t( + const std::string& message) + : clapp_exception_t(message) {} +clapp::exception::argument_exception_t::argument_exception_t( + const argument_exception_t&) = default; +clapp::exception::argument_exception_t& clapp::exception::argument_exception_t:: +operator=(const argument_exception_t&) = default; +clapp::exception::argument_exception_t::argument_exception_t( + argument_exception_t&&) noexcept = default; +clapp::exception::argument_exception_t& clapp::exception::argument_exception_t:: +operator=(argument_exception_t&&) noexcept = default; +clapp::exception::argument_exception_t::~argument_exception_t() noexcept = + default; + +clapp::exception::parser_exception_t::parser_exception_t(const char* message) + : clapp_exception_t(message) {} +clapp::exception::parser_exception_t::parser_exception_t( + const std::string& message) + : clapp_exception_t(message) {} +clapp::exception::parser_exception_t::parser_exception_t( + const parser_exception_t&) = default; +clapp::exception::parser_exception_t& clapp::exception::parser_exception_t:: +operator=(const parser_exception_t&) = default; +clapp::exception::parser_exception_t::parser_exception_t( + parser_exception_t&&) noexcept = default; +clapp::exception::parser_exception_t& clapp::exception::parser_exception_t:: +operator=(parser_exception_t&&) noexcept = default; +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) {} +clapp::exception::sub_parser_exception_t::sub_parser_exception_t( + const std::string& message) + : clapp_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& +clapp::exception::sub_parser_exception_t::operator=( + const sub_parser_exception_t&) = default; +clapp::exception::sub_parser_exception_t::sub_parser_exception_t( + sub_parser_exception_t&&) noexcept = default; +clapp::exception::sub_parser_exception_t& +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; diff --git a/src/clapp/main_parser.cpp b/src/clapp/main_parser.cpp index 003ee878..722f8ae3 100644 --- a/src/clapp/main_parser.cpp +++ b/src/clapp/main_parser.cpp @@ -38,3 +38,13 @@ void clapp::parser::basic_main_parser_t::parse(const arg_t& arg) { executable = *it; parse(it + 1, arg.cend()); } + +void clapp::parser::basic_main_parser_t::parse_and_validate( + int argc, const char* const* argv) { + parse(argc, argv); + validate_recursive(); +} + +std::string clapp::parser::basic_main_parser_t::gen_help_prefix() const { + return basic_parser_t::gen_help_prefix() + get_executable(); +} diff --git a/src/clapp/option.cpp b/src/clapp/option.cpp index bf24d5ef..c7926ef5 100644 --- a/src/clapp/option.cpp +++ b/src/clapp/option.cpp @@ -21,7 +21,7 @@ void clapp::option::check_long_option(const std::string_view option) { ss << "Whitespaces and equalsigns are not allowed in long options: " "option='" << option << "'"; - throw std::runtime_error(ss.str()); + throw clapp::exception::option_exception_t(ss.str()); } } @@ -36,7 +36,7 @@ void clapp::option::check_short_option(const char option) { ss << "Whitespaces and equalsigns are not allowed in short " "options: option='" << option << "'"; - throw std::runtime_error(ss.str()); + throw clapp::exception::option_exception_t(ss.str()); } default: return; @@ -56,11 +56,9 @@ clapp::option::bool_option_t::create_callbacks(bool_option_t* inst) { void clapp::option::bool_option_t::found_entry() { _given = true; _value = true; -} - -clapp::option::bool_option_t::operator bool() const { - Expects(_value.has_value()); - return _value.value(); + for (auto& found_func : _found) { + found_func.found(); + } } clapp::option::bool_option_t::~bool_option_t() = default; @@ -78,11 +76,9 @@ clapp::option::count_option_t::create_callbacks(count_option_t* inst) { void clapp::option::count_option_t::found_entry() { _given = true; _value = _value.value() + 1; -} - -clapp::option::count_option_t::operator bool() const { - Expects(_value.has_value()); - return _value.value() > 0; + for (auto& found_func : _found) { + found_func.found(); + } } clapp::option::count_option_t::~count_option_t() = default; diff --git a/src/clapp/parser.cpp b/src/clapp/parser.cpp index 19abf652..7abf014d 100644 --- a/src/clapp/parser.cpp +++ b/src/clapp/parser.cpp @@ -14,15 +14,11 @@ /////////////////////////////////////////////////////////////////////////////// #include <clapp/parser.h> +#include <clapp/sub_parser.h> #include <iostream> #include <sstream> #include <utility> -clapp::parser::arg_t::arg_t(const char* const* argv, int argc) - : base{gsl::make_span(argv, argc)} { - Expects(argv[argc] == nullptr); -} - clapp::parser::basic_parser_t::basic_parser_t() = default; clapp::parser::basic_parser_t::sub_parsers_map_t& @@ -84,11 +80,11 @@ void clapp::parser::basic_parser_t::reg(reg_sub_parser_conf_t&& config) { max_option_string_size = config.sub_parser_name.size(); } - if (get_optional_argument_descriptions().size() > 0) { + if (!get_optional_argument_descriptions().empty()) { std::stringstream ss; ss << "Can't register sub-parser '" << config.sub_parser_name << "' as optional arguments are already registered."; - throw std::runtime_error(ss.str()); + throw clapp::exception::sub_parser_exception_t(ss.str()); } const std::size_t num_arguments{get_arguments().size()}; @@ -97,7 +93,7 @@ void clapp::parser::basic_parser_t::reg(reg_sub_parser_conf_t&& config) { std::stringstream ss; ss << "Can't register sub-parser '" << config.sub_parser_name << "' as a variadic argument is already registered."; - throw std::runtime_error(ss.str()); + throw clapp::exception::sub_parser_exception_t(ss.str()); } auto found_sub_parser{get_sub_parsers().find(config.sub_parser_name)}; @@ -105,14 +101,17 @@ void clapp::parser::basic_parser_t::reg(reg_sub_parser_conf_t&& config) { std::stringstream ss; ss << "Can't register sub-parser '" << config.sub_parser_name << "' as a sub-parser with this name is already registered."; - throw std::runtime_error(ss.str()); + throw clapp::exception::sub_parser_exception_t(ss.str()); } get_sub_parser_descriptions().push_back( {config.sub_parser_name, std::move(config.description)}); - get_sub_parsers().emplace(std::move(config.sub_parser_name), - std::move(config.parse)); + 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_help_msg() const { @@ -122,7 +121,7 @@ std::string clapp::parser::basic_parser_t::gen_help_msg() const { std::string optional_option_desc; std::string mandatory_argument_desc; std::string optional_argument_desc; - if (mandatory_option_descriptions.size() > 0) { + if (!mandatory_option_descriptions.empty()) { mandatory_option_desc = "\nMandatory Options:\n"; for (const option_description_container_t& desc_cont : mandatory_option_descriptions) { @@ -141,7 +140,7 @@ std::string clapp::parser::basic_parser_t::gen_help_msg() const { } } - if (optional_option_descriptions.size() > 0) { + if (!optional_option_descriptions.empty()) { optional_option_desc = "\nOptional Options:\n"; for (const option_description_container_t& desc_cont : optional_option_descriptions) { @@ -160,7 +159,7 @@ std::string clapp::parser::basic_parser_t::gen_help_msg() const { } } - if (mandatory_argument_descriptions.size() > 0) { + if (!mandatory_argument_descriptions.empty()) { mandatory_argument_desc = "\nMandatory Arguments:\n"; for (const argument_description_container_t& desc_cont : @@ -180,7 +179,7 @@ std::string clapp::parser::basic_parser_t::gen_help_msg() const { } } - if (optional_argument_descriptions.size() > 0) { + if (!optional_argument_descriptions.empty()) { optional_argument_desc = "\nOptional Arguments:\n"; for (const argument_description_container_t& desc_cont : @@ -201,7 +200,7 @@ std::string clapp::parser::basic_parser_t::gen_help_msg() const { } } - if (sub_parser_descriptions.size() > 0) { + if (!sub_parser_descriptions.empty()) { sub_parser_desc = "\nAvailable sub-parsers:\n"; for (const sub_parser_description_container_t& desc_cont : @@ -227,20 +226,19 @@ clapp::basic_parser_t::process_parse_result( if (it == parse_result.it) { if (parse_result.short_option) { std::stringstream ss; - ss << "Invalid (sub-)parser line option '-" + ss << "Invalid (sub-)parser option '-" << parse_result.short_option.value() << "'"; - throw std::runtime_error(ss.str()); - } else if (parse_result.long_option) { + throw clapp::exception::option_exception_t(ss.str()); + } + if (parse_result.long_option) { std::stringstream ss; - ss << "Invalid (sub-)parser line option '--" + ss << "Invalid (sub-)parser option '--" << parse_result.long_option.value() << "'"; - throw std::runtime_error(ss.str()); - } else { - return it + 1; // ignore unknonwn arg + throw clapp::exception::option_exception_t(ss.str()); } - } else { - return parse_result.it; + return it + 1; // ignore unknonwn arg } + return parse_result.it; } void clapp::parser::basic_parser_t::parse(arg_iterator begin, @@ -257,12 +255,12 @@ clapp::parser::basic_parser_t::parse_result_t clapp::basic_parser_t::parse( if (option.size() >= 3 && option[0] == '-' && option[1] == '-') { option.remove_prefix(2); return parse_long(option, it, end); - } else if (option.size() >= 2 && option[0] == '-') { + } + if (option.size() >= 2 && option[0] == '-') { option.remove_prefix(1); return parse_short(option, it, end); - } else { - return parse_arg(option, it, end); } + return parse_arg(option, it, end); } void clapp::parser::basic_parser_t::validate() const { @@ -271,6 +269,15 @@ void clapp::parser::basic_parser_t::validate() const { } } +void clapp::parser::basic_parser_t::validate_recursive() const { + validate(); + for (const auto& elem : sub_parsers) { + if (elem.second) { + elem.second.validate_recursive(); + } + } +} + clapp::parser::basic_parser_t::parse_result_t clapp::parser::basic_parser_t::parse_arg(const std::string_view option, arg_iterator it, arg_iterator end) { @@ -293,9 +300,9 @@ clapp::parser::basic_parser_t::parse_arg(const std::string_view option, if (found_sub_parser == get_sub_parsers().end()) { std::stringstream ss; ss << "Unknown argument/sub-parser '" << option << "'."; - throw std::runtime_error(ss.str()); + throw clapp::exception::clapp_exception_t(ss.str()); } - found_sub_parser->second(it + 1, end); + found_sub_parser->second.sub_parse(it + 1, end); return parse_result_t{end, std::nullopt, std::nullopt}; } @@ -310,38 +317,36 @@ clapp::parser::basic_parser_t::parse_long(const std::string_view option, return parse_result_t{it, std::nullopt, std::string{opt}}; } if (equal_index != std::string_view::npos) { - if (std::holds_alternative<long_opt_param_func_t>(found->second)) { - std::get<long_opt_param_func_t>(found->second)( - opt, std::string_view{&(option[equal_index + 1])}); - return parse_result_t{it + 1, std::nullopt, std::nullopt}; - } else { + if (!std::holds_alternative<long_opt_param_func_t>(found->second)) { std::stringstream ss; ss << "No param allowed for option '--" << opt << "'"; - throw std::runtime_error(ss.str()); + throw clapp::exception::option_param_exception_t(ss.str()); } - } else { - if (std::holds_alternative<long_opt_param_func_t>(found->second)) { - if (it + 1 != end) { - it++; - std::get<long_opt_param_func_t>(found->second)( - opt, std::string_view{*(it)}); - return parse_result_t{it + 1, std::nullopt, std::nullopt}; - } else { - std::stringstream ss; - ss << "No param given for option '--" << opt << "'"; - throw std::runtime_error(ss.str()); - } - } else { - Expects(std::holds_alternative<long_opt_func_t>(found->second)); - std::get<long_opt_func_t>(found->second)(opt); - return parse_result_t{it + 1, std::nullopt, std::nullopt}; + std::get<long_opt_param_func_t>(found->second)( + opt, std::string_view{&(option[equal_index + 1])}); + return parse_result_t{it + 1, std::nullopt, std::nullopt}; + } + if (std::holds_alternative<long_opt_param_func_t>(found->second)) { + if (it + 1 == end) { + std::stringstream ss; + ss << "No param given for option '--" << opt << "'"; + throw clapp::exception::option_param_exception_t(ss.str()); } + + it++; + std::get<long_opt_param_func_t>(found->second)(opt, + std::string_view{*(it)}); + return parse_result_t{it + 1, std::nullopt, std::nullopt}; } + Expects(std::holds_alternative<long_opt_func_t>(found->second)); + std::get<long_opt_func_t>(found->second)(opt); + return parse_result_t{it + 1, std::nullopt, std::nullopt}; } clapp::parser::basic_parser_t::parse_result_t clapp::parser::basic_parser_t::parse_short(const std::string_view option, arg_iterator it, arg_iterator end) { + Expects(!option.empty()); const std::size_t equal_index{option.find_first_of("=")}; for (std::size_t i{0}; i < option.size(); i++) { auto found{get_short_options().find(option[i])}; @@ -349,42 +354,49 @@ clapp::parser::basic_parser_t::parse_short(const std::string_view option, return parse_result_t{it, option[i], std::nullopt}; } if (equal_index == i + 1) { - if (std::holds_alternative<short_opt_param_func_t>(found->second)) { - std::get<short_opt_param_func_t>(found->second)( - option[i], std::string_view{&(option[equal_index + 1])}); - return parse_result_t{it + 1, std::nullopt, std::nullopt}; - } else { + if (!std::holds_alternative<short_opt_param_func_t>( + found->second)) { std::stringstream ss; ss << "No param allowed for option '-" << option[i] << "'"; - throw std::runtime_error(ss.str()); + throw clapp::exception::option_param_exception_t(ss.str()); } - } else if (i == option.size() - 1) { + std::get<short_opt_param_func_t>(found->second)( + option[i], std::string_view{&(option[equal_index + 1])}); + return parse_result_t{it + 1, std::nullopt, std::nullopt}; + } + if (i == option.size() - 1) { if (std::holds_alternative<short_opt_param_func_t>(found->second)) { - if (it + 1 != end) { - it++; - std::get<short_opt_param_func_t>(found->second)( - option[i], std::string_view{*(it)}); - return parse_result_t{it + 1, std::nullopt, std::nullopt}; - } else { + if (it + 1 == end) { std::stringstream ss; ss << "No param given for option '-" << option[i] << "'"; - throw std::runtime_error(ss.str()); + throw clapp::exception::option_param_exception_t(ss.str()); } - } else { - std::get<short_opt_func_t>(found->second)(option[i]); + it++; + std::get<short_opt_param_func_t>(found->second)( + option[i], std::string_view{*(it)}); return parse_result_t{it + 1, std::nullopt, std::nullopt}; } - } else { - if (std::holds_alternative<short_opt_param_func_t>(found->second)) { - std::stringstream ss; - ss << "No param given for option '-" << option[i] << "'"; - throw std::runtime_error(ss.str()); - } else { - std::get<short_opt_func_t>(found->second)(option[i]); - } + std::get<short_opt_func_t>(found->second)(option[i]); + return parse_result_t{it + 1, std::nullopt, std::nullopt}; } + if (std::holds_alternative<short_opt_param_func_t>(found->second)) { + std::stringstream ss; + ss << "No param given for option '-" << option[i] << "'"; + throw clapp::exception::option_param_exception_t(ss.str()); + } + std::get<short_opt_func_t>(found->second)(option[i]); } - return parse_result_t{it + 1, std::nullopt, std::nullopt}; + throw clapp::exception::clapp_exception_t( + "This exection should not be thrown."); +} + +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(); + exit(exit_code); + }}; } clapp::parser::basic_parser_t::~basic_parser_t() = default; diff --git a/src/clapp/sub_parser.cpp b/src/clapp/sub_parser.cpp index 3f7dcf11..244a41fb 100644 --- a/src/clapp/sub_parser.cpp +++ b/src/clapp/sub_parser.cpp @@ -37,16 +37,15 @@ void clapp::parser::basic_sub_parser_t::sub_parse(arg_iterator begin, } clapp::parser::basic_sub_parser_t::basic_sub_parser_t( - clapp::basic_parser_t& parser, const std::string& _sub_parser_name, - const std::string& _description, bool _parse_parent) - : basic_parser_t{}, - 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}); + clapp::basic_parser_t& parser, std::string sub_parser_name_arg, + std::string description_arg, bool parse_parent_arg) + : parent_parser{parser}, + sub_parser_name{std::move(sub_parser_name_arg)}, + description{std::move(description_arg)}, + parse_parent{parse_parent_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; } diff --git a/src/clapp/value.cpp b/src/clapp/value.cpp index 1074f470..6439f00a 100644 --- a/src/clapp/value.cpp +++ b/src/clapp/value.cpp @@ -14,13 +14,18 @@ /////////////////////////////////////////////////////////////////////////////// #include <clapp/value.h> -#include <stdexcept> +#include <iostream> #include <string> template <typename T> -static T convert_uint(const std::string_view param); +static T convert_uint(std::string_view param); template <typename T> -static T convert_int(const std::string_view param); +static T convert_int(std::string_view param); + +clapp::value::found_func_t::found_func_t(func_t&& func_arg) + : func{std::move(func_arg)} {} + +void clapp::value::found_func_t::found() { func(); } template <> std::string clapp::value::convert_value<std::string>( @@ -76,6 +81,78 @@ std::uint64_t clapp::value::convert_value<std::uint64_t>( return convert_uint<std::uint64_t>(param); } +template <> +std::chrono::nanoseconds clapp::value::convert_value<std::chrono::nanoseconds>( + const std::string_view param) { + return std::chrono::nanoseconds{convert_uint<std::uint64_t>(param)}; +} + +template <> +std::chrono::microseconds +clapp::value::convert_value<std::chrono::microseconds>( + const std::string_view param) { + return std::chrono::microseconds{convert_uint<std::uint64_t>(param)}; +} + +template <> +std::chrono::milliseconds +clapp::value::convert_value<std::chrono::milliseconds>( + const std::string_view param) { + return std::chrono::milliseconds{convert_uint<std::uint64_t>(param)}; +} + +template <> +std::chrono::seconds clapp::value::convert_value<std::chrono::seconds>( + const std::string_view param) { + return std::chrono::seconds{convert_uint<std::uint64_t>(param)}; +} + +template <> +std::chrono::minutes clapp::value::convert_value<std::chrono::minutes>( + const std::string_view param) { + return std::chrono::minutes{convert_uint<std::uint64_t>(param)}; +} + +template <> +std::chrono::hours clapp::value::convert_value<std::chrono::hours>( + const std::string_view param) { + return std::chrono::hours{convert_uint<std::uint64_t>(param)}; +} + +template <> +float clapp::value::convert_value<float>(const std::string_view param) { + try { + return std::stof(std::string{param}, nullptr); + } catch (std::out_of_range& e) { + std::stringstream ss; + ss << "convert_value: value " << param << " is out of float-range. (" + << e.what() << ")"; + throw clapp::exception::out_of_range_t{ss.str()}; + } catch (std::invalid_argument& e) { + std::stringstream ss; + ss << "convert_value: value " << param << " is invalid. (" << e.what() + << ")"; + throw clapp::exception::invalid_value_t{ss.str()}; + } +} + +template <> +double clapp::value::convert_value<double>(const std::string_view param) { + try { + return std::stod(std::string{param}, nullptr); + } catch (std::out_of_range& e) { + std::stringstream ss; + ss << "convert_value: value " << param << " is out of double-range. (" + << e.what() << ")"; + throw clapp::exception::out_of_range_t{ss.str()}; + } catch (std::invalid_argument& e) { + std::stringstream ss; + ss << "convert_value: value " << param << " is invalid. (" << e.what() + << ")"; + throw clapp::exception::invalid_value_t{ss.str()}; + } +} + #ifdef CLAPP_FS_AVAIL template <> clapp::fs::path clapp::value::convert_value<clapp::fs::path>( @@ -87,13 +164,13 @@ std::string clapp::path_exists_t::append_restriction_text() { return "existing path"; } -void clapp::path_exists_t::validate(const clapp::fs::path &path, - const std::string ¶m_name) const { +void clapp::path_exists_t::validate(const clapp::fs::path& path, + const std::string& param_name) const { if (!clapp::fs::exists(path)) { std::stringstream ss; ss << "CLI value " << path << " for '" << param_name << "' does not exist."; - throw std::runtime_error(ss.str()); + throw clapp::exception::path_does_not_exist_t(ss.str()); } } #endif @@ -104,14 +181,28 @@ static T convert_uint(const std::string_view param) { "Integral template parameter required."); static_assert(std::is_unsigned<T>::value, "Unsigned template parameter required."); - const std::uint64_t value{std::stoull(std::string{param}, 0, 0)}; - if (value > std::numeric_limits<T>::max()) { + try { + const std::uint64_t value{std::stoull(std::string{param}, nullptr, 0)}; + + if (value > std::numeric_limits<T>::max()) { + std::stringstream ss; + ss << "convert_value: value " << value << " is bigger than max " + << std::numeric_limits<T>::max(); + throw clapp::exception::out_of_range_t{ss.str()}; + } + return static_cast<T>(value); + } catch (std::out_of_range& e) { std::stringstream ss; - ss << "convert_value: value " << value << " is bigger than max " - << std::numeric_limits<T>::max(); - throw std::out_of_range{ss.str()}; + ss << "convert_value: value " << param << " is out of range. (" + << std::numeric_limits<T>::min() << " and " + << std::numeric_limits<T>::max() << ", " << e.what() << ")"; + throw clapp::exception::out_of_range_t{ss.str()}; + } catch (std::invalid_argument& e) { + std::stringstream ss; + ss << "convert_value: value " << param << " is invalid. (" << e.what() + << ")"; + throw clapp::exception::invalid_value_t{ss.str()}; } - return static_cast<T>(value); } template <typename T> @@ -120,18 +211,33 @@ static T convert_int(const std::string_view param) { "Integral template parameter required."); static_assert(std::is_signed<T>::value, "Signed template parameter required."); - const std::int64_t value{std::stoll(std::string{param}, 0, 0)}; - if (value > std::numeric_limits<T>::max()) { + try { + const std::int64_t value{std::stoll(std::string{param}, nullptr, 0)}; + + if (value > std::numeric_limits<T>::max()) { + std::stringstream ss; + ss << "convert_value: value " << value << " is bigger than max " + << std::numeric_limits<T>::max(); + throw clapp::exception::out_of_range_t{ss.str()}; + } + + if (value < std::numeric_limits<T>::min()) { + std::stringstream ss; + ss << "convert_value: value " << value << " is smaller than min " + << std::numeric_limits<T>::max(); + throw clapp::exception::out_of_range_t{ss.str()}; + } + return static_cast<T>(value); + } catch (std::out_of_range& e) { std::stringstream ss; - ss << "convert_value: value " << value << " is bigger than max " - << std::numeric_limits<T>::max(); - throw std::out_of_range{ss.str()}; - } - if (value < std::numeric_limits<T>::min()) { + ss << "convert_value: value " << param << " is out of range. (" + << std::numeric_limits<T>::min() << " and " + << std::numeric_limits<T>::max() << ", " << e.what() << ")"; + throw clapp::exception::out_of_range_t{ss.str()}; + } catch (std::invalid_argument& e) { std::stringstream ss; - ss << "convert_value: value " << value << " is smaller than min " - << std::numeric_limits<T>::max(); - throw std::out_of_range{ss.str()}; + ss << "convert_value: value " << param << " is invalid. (" << e.what() + << ")"; + throw clapp::exception::invalid_value_t{ss.str()}; } - return static_cast<T>(value); } diff --git a/src/include/clapp/CMakeLists.txt b/src/include/clapp/CMakeLists.txt index 246ecb8c..9edbc1b8 100644 --- a/src/include/clapp/CMakeLists.txt +++ b/src/include/clapp/CMakeLists.txt @@ -2,6 +2,7 @@ configure_file(build_info.h.in build_info.h) install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/argument.h" "${CMAKE_CURRENT_SOURCE_DIR}/argument.hpp" "${CMAKE_CURRENT_BINARY_DIR}/build_info.h" + "${CMAKE_CURRENT_SOURCE_DIR}/exception.h" "${CMAKE_CURRENT_SOURCE_DIR}/filesystem.h" "${CMAKE_CURRENT_SOURCE_DIR}/main_parser.h" "${CMAKE_CURRENT_SOURCE_DIR}/option.h" diff --git a/src/include/clapp/argument.h b/src/include/clapp/argument.h index 793db1f7..a9468da4 100644 --- a/src/include/clapp/argument.h +++ b/src/include/clapp/argument.h @@ -13,11 +13,12 @@ // SOFTWARE. /////////////////////////////////////////////////////////////////////////////// -#ifndef LIBCLAPP_ARGUMENT_H -#define LIBCLAPP_ARGUMENT_H +#ifndef CLAPP_ARGUMENT_H +#define CLAPP_ARGUMENT_H #include <clapp/parser.h> #include <clapp/type_traits.h> +#include <clapp/value.h> #include <functional> #include <optional> #include <string> @@ -53,16 +54,18 @@ template <typename T> struct arg_params_t { using validate_func_t = std::function<void(const T&, const std::string& option_string)>; - std::vector<std::string> restrictions; - std::vector<validate_func_t> validate_funcs; + std::vector<std::string> restrictions{}; + std::vector<validate_func_t> validate_funcs{}; basic_parser_t::purpose_t purpose{basic_parser_t::purpose_t::mandatory}; - std::optional<T> default_value; + std::optional<T> default_value{}; + std::vector<clapp::value::found_func_t> found{}; }; template <typename T, typename ARG_CONF> struct arg_conf_container_t { ARG_CONF arg_conf; std::optional<T> default_value; + std::vector<clapp::value::found_func_t> found; }; template <typename T> @@ -103,9 +106,9 @@ class basic_argument_t { template <typename... Params> basic_argument_t(basic_parser_t& parser, const std::string& argument_name, const std::string& description, Params&&... parameters); - explicit operator bool() const; + constexpr explicit operator bool() const noexcept; T value() const; - bool given() const; + constexpr bool given() const noexcept; protected: void validate() const; @@ -114,7 +117,8 @@ class basic_argument_t { private: static callbacks_t create_callbacks(basic_argument_t<T>* inst); - std::optional<T> _value; + std::vector<clapp::value::found_func_t> _found{}; + std::optional<T> _value{}; bool _given{false}; }; @@ -129,9 +133,9 @@ class basic_variadic_argument_t { const std::string& argument_name, const std::string& description, Params&&... parameters); - explicit operator bool() const; + constexpr explicit operator bool() const noexcept; std::vector<T> value() const; - bool given() const; + constexpr bool given() const noexcept; static std::string variadic_argument_restrictions(); @@ -142,7 +146,8 @@ class basic_variadic_argument_t { private: static callbacks_t create_callbacks(basic_variadic_argument_t<T>* inst); - std::vector<T> _value; + std::vector<clapp::value::found_func_t> _found{}; + std::vector<T> _value{}; bool _given{false}; }; @@ -158,6 +163,14 @@ using int32_argument_t = clapp::basic_argument_t<std::int32_t>; using uint32_argument_t = clapp::basic_argument_t<std::uint32_t>; using int64_argument_t = clapp::basic_argument_t<std::int64_t>; using uint64_argument_t = clapp::basic_argument_t<std::uint64_t>; +using double_argument_t = clapp::basic_argument_t<double>; +using float_argument_t = clapp::basic_argument_t<float>; +using ns_argument_t = clapp::basic_argument_t<std::chrono::nanoseconds>; +using us_argument_t = clapp::basic_argument_t<std::chrono::microseconds>; +using ms_argument_t = clapp::basic_argument_t<std::chrono::milliseconds>; +using sec_argument_t = clapp::basic_argument_t<std::chrono::seconds>; +using min_argument_t = clapp::basic_argument_t<std::chrono::minutes>; +using hours_argument_t = clapp::basic_argument_t<std::chrono::hours>; using variadic_string_argument_t = clapp::basic_variadic_argument_t<std::string>; @@ -180,6 +193,20 @@ using variadic_int64_argument_t = clapp::basic_variadic_argument_t<std::int64_t>; using variadic_uint64_argument_t = clapp::basic_variadic_argument_t<std::uint64_t>; +using variadic_double_argument_t = clapp::basic_variadic_argument_t<double>; +using variadic_float_argument_t = clapp::basic_variadic_argument_t<float>; +using variadic_ns_argument_t = + clapp::basic_variadic_argument_t<std::chrono::nanoseconds>; +using variadic_us_argument_t = + clapp::basic_variadic_argument_t<std::chrono::microseconds>; +using variadic_ms_argument_t = + clapp::basic_variadic_argument_t<std::chrono::milliseconds>; +using variadic_sec_argument_t = + clapp::basic_variadic_argument_t<std::chrono::seconds>; +using variadic_min_argument_t = + clapp::basic_variadic_argument_t<std::chrono::minutes>; +using variadic_hours_argument_t = + clapp::basic_variadic_argument_t<std::chrono::hours>; } // namespace argument diff --git a/src/include/clapp/argument.hpp b/src/include/clapp/argument.hpp index 7a0c5ff1..28b4b8ba 100644 --- a/src/include/clapp/argument.hpp +++ b/src/include/clapp/argument.hpp @@ -13,8 +13,8 @@ // SOFTWARE. /////////////////////////////////////////////////////////////////////////////// -#ifndef LIBCLAPP_ARGUMENT_HPP -#define LIBCLAPP_ARGUMENT_HPP +#ifndef CLAPP_ARGUMENT_HPP +#define CLAPP_ARGUMENT_HPP #include <clapp/argument.h> #include <clapp/value.h> @@ -26,7 +26,8 @@ inline void clapp::argument::gen_arg_conf_process_params(arg_params_t<T>&) {} template <typename T, typename Param> void clapp::argument::gen_arg_conf_process_params(arg_params_t<T>& arg_params, Param&& param) { - if constexpr (has_append_restriction_text<typename std::decay<Param>::type>::value) { + if constexpr (has_append_restriction_text< + typename std::decay<Param>::type>::value) { arg_params.restrictions.push_back(param.append_restriction_text()); } if constexpr (has_default_value<typename std::decay<Param>::type>::value) { @@ -38,7 +39,11 @@ void clapp::argument::gen_arg_conf_process_params(arg_params_t<T>& arg_params, param.validate(value, argument_name); }); } - if constexpr (std::is_same<typename std::decay<Param>::type, basic_parser_t::purpose_t>::value) { + if constexpr (has_found<typename std::decay<Param>::type>::value) { + arg_params.found.push_back(param); + } + if constexpr (std::is_same<typename std::decay<Param>::type, + basic_parser_t::purpose_t>::value) { arg_params.purpose = param; } } @@ -66,7 +71,7 @@ clapp::argument::gen_arg_validate_func( argument_name, validate_funcs = std::move(validate_funcs)]() { if (purpose == basic_parser_t::purpose_t::mandatory && given_func) { if (!given_func.value()()) { - throw std::runtime_error( + throw clapp::exception::argument_exception_t( std::string{"Mandatory argument '"} + argument_name + "' not given."); } @@ -93,7 +98,7 @@ clapp::argument::gen_arg_validate_func( } } else { if (validate_funcs.size() > 0) { - throw std::runtime_error( + throw clapp::exception::argument_exception_t( std::string{"Cannot validate argument '"} + argument_name + "' without a given value function."); } @@ -126,7 +131,8 @@ clapp::argument::gen_arg_conf(CALLBACKS&& callbacks, arg_params_t<T> arg_params; if (std::is_same<ARG_CONF, basic_parser_t::variadic_arg_conf_t>::value) { arg_params.restrictions.push_back( - clapp::argument::basic_variadic_argument_t<T>::variadic_argument_restrictions()); + clapp::argument::basic_variadic_argument_t< + T>::variadic_argument_restrictions()); } gen_arg_conf_process_params(arg_params, std::forward<Params>(parameters)...); @@ -146,7 +152,7 @@ clapp::argument::gen_arg_conf(CALLBACKS&& callbacks, std::forward<CALLBACKS>(callbacks), argument_name, std::move(arg_params.validate_funcs), description + restriction, arg_params.purpose), - arg_params.default_value}; + arg_params.default_value, std::move(arg_params.found)}; } template <typename T> @@ -171,20 +177,25 @@ clapp::argument::basic_argument_t<T>::basic_argument_t( std::forward<Params>(parameters)...)}; parser.reg(std::move(conf.arg_conf)); _value = conf.default_value; + _found = std::move(conf.found); } template <typename T> -clapp::argument::basic_argument_t<T>::operator bool() const { +constexpr clapp::argument::basic_argument_t<T>::operator bool() const noexcept { return _value.has_value(); } template <typename T> T clapp::argument::basic_argument_t<T>::value() const { - return _value.value(); + if (_value) { + return _value.value(); + } + throw clapp::exception::value_undefined_t{ + "Requested argument value is not defined."}; } template <typename T> -bool clapp::argument::basic_argument_t<T>::given() const { +constexpr bool clapp::argument::basic_argument_t<T>::given() const noexcept { return _given; } @@ -193,6 +204,9 @@ void clapp::argument::basic_argument_t<T>::found_entry( const std::string_view argument) { _given = true; _value = clapp::value::convert_value<T>(argument); + for (auto& found_func : _found) { + found_func.found(); + } } template <typename T> @@ -219,13 +233,15 @@ clapp::argument::basic_variadic_argument_t<T>::basic_variadic_argument_t( std::stringstream ss; ss << "No default value for variadic argument '" << conf.arg_conf.argument_name << "' allowed."; - throw std::runtime_error(ss.str()); + throw clapp::exception::argument_exception_t(ss.str()); } + _found = std::move(conf.found); parser.reg(std::move(conf.arg_conf)); } template <typename T> -clapp::argument::basic_variadic_argument_t<T>::operator bool() const { +constexpr clapp::argument::basic_variadic_argument_t<T>::operator bool() const + noexcept { return _value.size() > 0; } @@ -235,7 +251,8 @@ std::vector<T> clapp::argument::basic_variadic_argument_t<T>::value() const { } template <typename T> -bool clapp::argument::basic_variadic_argument_t<T>::given() const { +constexpr bool clapp::argument::basic_variadic_argument_t<T>::given() const + noexcept { return _given; } @@ -244,10 +261,14 @@ void clapp::argument::basic_variadic_argument_t<T>::found_entry( const std::string_view argument) { _given = true; _value.push_back(clapp::value::convert_value<T>(argument)); + for (auto& found_func : _found) { + found_func.found(); + } } template <typename T> -std::string clapp::argument::basic_variadic_argument_t<T>::variadic_argument_restrictions() { +std::string clapp::argument::basic_variadic_argument_t< + T>::variadic_argument_restrictions() { return "variadic argument"; } diff --git a/src/include/clapp/build_info.h.in b/src/include/clapp/build_info.h.in index 0ae7248b..d092878e 100644 --- a/src/include/clapp/build_info.h.in +++ b/src/include/clapp/build_info.h.in @@ -13,8 +13,8 @@ // SOFTWARE. /////////////////////////////////////////////////////////////////////////////// -#ifndef LIBCLAPP_BUILD_INFO_H -#define LIBCLAPP_BUILD_INFO_H +#ifndef CLAPP_BUILD_INFO_H +#define CLAPP_BUILD_INFO_H #include <cstddef> #include <cstdint> diff --git a/src/include/clapp/exception.h b/src/include/clapp/exception.h new file mode 100644 index 00000000..acf65350 --- /dev/null +++ b/src/include/clapp/exception.h @@ -0,0 +1,136 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2020 Martin Wölzer +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +/////////////////////////////////////////////////////////////////////////////// + +#ifndef CLAPP_EXCEPTION_H +#define CLAPP_EXCEPTION_H + +#include <stdexcept> + +namespace clapp { +inline namespace exception { +class clapp_exception_t : public std::runtime_error { + public: + explicit clapp_exception_t(const char* message); + explicit clapp_exception_t(const std::string& message); + clapp_exception_t(const clapp_exception_t&); + clapp_exception_t& operator=(const clapp_exception_t&); + clapp_exception_t(clapp_exception_t&&) noexcept; + clapp_exception_t& operator=(clapp_exception_t&&) noexcept; + ~clapp_exception_t() noexcept override; +}; + +class invalid_value_t : public clapp_exception_t { + public: + explicit invalid_value_t(const char* message); + explicit invalid_value_t(const std::string& message); + invalid_value_t(const invalid_value_t&); + invalid_value_t& operator=(const invalid_value_t&); + invalid_value_t(invalid_value_t&&) noexcept; + invalid_value_t& operator=(invalid_value_t&&) noexcept; + ~invalid_value_t() noexcept override; +}; + +class value_undefined_t : public clapp_exception_t { + public: + explicit value_undefined_t(const char* message); + explicit value_undefined_t(const std::string& message); + value_undefined_t(const value_undefined_t&); + value_undefined_t& operator=(const value_undefined_t&); + value_undefined_t(value_undefined_t&&) noexcept; + value_undefined_t& operator=(value_undefined_t&&) noexcept; + ~value_undefined_t() noexcept override; +}; + +class out_of_range_t : public invalid_value_t { + public: + explicit out_of_range_t(const char* message); + explicit out_of_range_t(const std::string& message); + out_of_range_t(const out_of_range_t&); + out_of_range_t& operator=(const out_of_range_t&); + out_of_range_t(out_of_range_t&&) noexcept; + out_of_range_t& operator=(out_of_range_t&&) noexcept; + ~out_of_range_t() noexcept override; +}; + +class path_does_not_exist_t : public invalid_value_t { + public: + explicit path_does_not_exist_t(const char* message); + explicit path_does_not_exist_t(const std::string& message); + path_does_not_exist_t(const path_does_not_exist_t&); + path_does_not_exist_t& operator=(const path_does_not_exist_t&); + path_does_not_exist_t(path_does_not_exist_t&&) noexcept; + path_does_not_exist_t& operator=(path_does_not_exist_t&&) noexcept; + ~path_does_not_exist_t() noexcept override; +}; + +class option_exception_t : public clapp_exception_t { + public: + explicit option_exception_t(const char* message); + explicit option_exception_t(const std::string& message); + option_exception_t(const option_exception_t&); + option_exception_t& operator=(const option_exception_t&); + option_exception_t(option_exception_t&&) noexcept; + option_exception_t& operator=(option_exception_t&&) noexcept; + ~option_exception_t() noexcept override; +}; + +class option_param_exception_t : public option_exception_t { + public: + explicit option_param_exception_t(const char* message); + explicit option_param_exception_t(const std::string& message); + option_param_exception_t(const option_param_exception_t&); + option_param_exception_t& operator=(const option_param_exception_t&); + option_param_exception_t(option_param_exception_t&&) noexcept; + option_param_exception_t& operator=(option_param_exception_t&&) noexcept; + ~option_param_exception_t() noexcept override; +}; + +class argument_exception_t : public clapp_exception_t { + public: + explicit argument_exception_t(const char* message); + explicit argument_exception_t(const std::string& message); + argument_exception_t(const argument_exception_t&); + argument_exception_t& operator=(const argument_exception_t&); + argument_exception_t(argument_exception_t&&) noexcept; + argument_exception_t& operator=(argument_exception_t&&) noexcept; + ~argument_exception_t() noexcept override; +}; + +class parser_exception_t : public clapp_exception_t { + public: + explicit parser_exception_t(const char* message); + explicit parser_exception_t(const std::string& message); + parser_exception_t(const parser_exception_t&); + parser_exception_t& operator=(const parser_exception_t&); + parser_exception_t(parser_exception_t&&) noexcept; + parser_exception_t& operator=(parser_exception_t&&) noexcept; + ~parser_exception_t() noexcept override; +}; + +class sub_parser_exception_t : public clapp_exception_t { + public: + explicit sub_parser_exception_t(const char* message); + explicit sub_parser_exception_t(const std::string& message); + sub_parser_exception_t(const sub_parser_exception_t&); + sub_parser_exception_t& operator=(const sub_parser_exception_t&); + sub_parser_exception_t(sub_parser_exception_t&&) noexcept; + sub_parser_exception_t& operator=(sub_parser_exception_t&&) noexcept; + ~sub_parser_exception_t() noexcept override; +}; + +} // namespace exception +} // namespace clapp + +#endif diff --git a/src/include/clapp/filesystem.h b/src/include/clapp/filesystem.h index 9fd47326..b9ee6968 100644 --- a/src/include/clapp/filesystem.h +++ b/src/include/clapp/filesystem.h @@ -13,20 +13,24 @@ // SOFTWARE. /////////////////////////////////////////////////////////////////////////////// -#ifndef LIBCLAPP_FILESYSTEM_H -#define LIBCLAPP_FILESYSTEM_H +#ifndef CLAPP_FILESYSTEM_H +#define CLAPP_FILESYSTEM_H #if __has_include(<filesystem>) + #include <filesystem> namespace clapp { namespace fs = std::filesystem; -} +} // namespace clapp + #define CLAPP_FS_AVAIL #elif __has_include(<experimental/filesystem>) + #include <experimental/filesystem> namespace clapp { namespace fs = std::experimental::filesystem; -} +} // namespace clapp + #define CLAPP_FS_AVAIL #endif diff --git a/src/include/clapp/main_parser.h b/src/include/clapp/main_parser.h index c0e9f9f4..01f67144 100644 --- a/src/include/clapp/main_parser.h +++ b/src/include/clapp/main_parser.h @@ -13,8 +13,8 @@ // SOFTWARE. /////////////////////////////////////////////////////////////////////////////// -#ifndef LIBCLAPP_MAIN_PARSER_H -#define LIBCLAPP_MAIN_PARSER_H +#ifndef CLAPP_MAIN_PARSER_H +#define CLAPP_MAIN_PARSER_H #include <clapp/parser.h> @@ -30,11 +30,15 @@ class basic_main_parser_t : public basic_parser_t { void parse(int argc, const char* const* argv); void parse(const arg_t& arg); + void parse_and_validate(int argc, const char* const* argv); + explicit operator bool() const; std::string get_executable() const; + std::string gen_help_prefix() const override; + private: - std::optional<std::string> executable; + std::optional<std::string> executable{}; }; } // namespace parser diff --git a/src/include/clapp/option.h b/src/include/clapp/option.h index 193aa815..4aaf8410 100644 --- a/src/include/clapp/option.h +++ b/src/include/clapp/option.h @@ -13,11 +13,12 @@ // SOFTWARE. /////////////////////////////////////////////////////////////////////////////// -#ifndef LIBCLAPP_CLI_OPTION_H -#define LIBCLAPP_CLI_OPTION_H +#ifndef CLAPP_OPTION_H +#define CLAPP_OPTION_H #include <clapp/filesystem.h> #include <clapp/parser.h> +#include <clapp/value.h> #include <functional> #include <optional> #include <string> @@ -59,23 +60,28 @@ struct option_vector_param_callbacks_t { std::optional<vector_value_func_t<T>> value; }; -void check_short_option(const char option); -void check_long_option(const std::string_view option); +void check_short_option(char option); +void check_long_option(std::string_view option); + +template <typename T> +inline std::vector<std::string> gen_string_vec(const std::vector<T>& vec); template <typename T> struct opt_params_t { using validate_func_t = std::function<void(const T&, const std::string& option_string)>; - std::vector<std::string> restrictions; - std::vector<validate_func_t> validate_funcs; + std::vector<std::string> restrictions{}; + std::vector<validate_func_t> validate_funcs{}; basic_parser_t::purpose_t purpose{basic_parser_t::purpose_t::optional}; - std::optional<T> default_value; + std::optional<T> default_value{}; + std::vector<clapp::value::found_func_t> found{}; }; template <typename T, typename OPT_CONF> struct opt_conf_container_t { OPT_CONF opt_conf; std::optional<T> default_value; + std::vector<clapp::value::found_func_t> found; }; template <typename T> @@ -85,19 +91,24 @@ class basic_param_option_t { using opt_conf_t = basic_parser_t::opt_scalar_param_conf_t; template <typename... Params> - basic_param_option_t(basic_parser_t& parser, Params... parameters); + explicit basic_param_option_t(basic_parser_t& parser, Params... parameters); + explicit basic_param_option_t(const basic_param_option_t&) = delete; + explicit basic_param_option_t(basic_param_option_t&&) noexcept = delete; + basic_param_option_t& operator=(const basic_param_option_t&) = delete; + basic_param_option_t& operator=(basic_param_option_t&&) noexcept = delete; - explicit operator bool() const; + inline explicit operator bool() const; T value() const; - bool given() const; + constexpr bool given() const noexcept; virtual ~basic_param_option_t(); protected: - void found_entry(const std::string_view param); + void found_entry(std::string_view param); static callbacks_t create_callbacks(basic_param_option_t<T>* inst); - std::optional<T> _value; + std::vector<clapp::value::found_func_t> _found{}; + std::optional<T> _value{}; bool _given{false}; }; @@ -108,19 +119,29 @@ class basic_vector_param_option_t { using opt_conf_t = basic_parser_t::opt_vector_param_conf_t; template <typename... Params> - basic_vector_param_option_t(basic_parser_t& parser, Params... parameters); - - explicit operator bool() const; + explicit basic_vector_param_option_t(basic_parser_t& parser, + Params... parameters); + explicit basic_vector_param_option_t(const basic_vector_param_option_t&) = + delete; + explicit basic_vector_param_option_t( + basic_vector_param_option_t&&) noexcept = delete; + basic_vector_param_option_t& operator=(const basic_vector_param_option_t&) = + delete; + basic_vector_param_option_t& operator=( + basic_vector_param_option_t&&) noexcept = delete; + + inline explicit operator bool() const; std::vector<T> value() const; - bool given() const; + constexpr bool given() const noexcept; virtual ~basic_vector_param_option_t(); protected: - void found_entry(const std::string_view param); + void found_entry(std::string_view param); static callbacks_t create_callbacks(basic_vector_param_option_t<T>* inst); - std::vector<T> _value; + std::vector<clapp::value::found_func_t> _found{}; + std::vector<T> _value{}; bool _given{false}; }; @@ -133,6 +154,10 @@ class basic_option_t { template <typename... Params> basic_option_t(basic_parser_t& parser, callbacks_t&& callbacks, Params&&... parameters); + explicit basic_option_t(const basic_option_t&) = delete; + explicit basic_option_t(basic_option_t&&) noexcept = delete; + basic_option_t& operator=(const basic_option_t&) = delete; + basic_option_t& operator=(basic_option_t&&) noexcept = delete; virtual ~basic_option_t(); @@ -140,32 +165,57 @@ class basic_option_t { bool given() const; protected: - std::optional<T> _value; + std::vector<clapp::value::found_func_t> _found{}; + std::optional<T> _value{}; bool _given{false}; }; -class bool_option_t final : public basic_option_t<bool> { +class bool_option_t : public basic_option_t<bool> { public: template <typename... Params> - bool_option_t(basic_parser_t& parser, Params... parameters); + explicit bool_option_t(basic_parser_t& parser, Params... parameters); + explicit bool_option_t(const bool_option_t&) = delete; + explicit bool_option_t(bool_option_t&&) noexcept = delete; + bool_option_t& operator=(const bool_option_t&) = delete; + bool_option_t& operator=(bool_option_t&&) noexcept = delete; ~bool_option_t() override; - explicit operator bool() const; + inline explicit operator bool() const; private: void found_entry(); static callbacks_t create_callbacks(bool_option_t* inst); }; -class count_option_t final : public basic_option_t<std::uint32_t> { +template <int EXIT_CODE> +class basic_help_option_t : public bool_option_t { public: template <typename... Params> - count_option_t(basic_parser_t& parser, Params... parameters); + explicit basic_help_option_t(basic_parser_t& parser, Params... parameters); + explicit basic_help_option_t(const basic_help_option_t&) = delete; + explicit basic_help_option_t(basic_help_option_t&&) noexcept = delete; + basic_help_option_t& operator=(const basic_help_option_t&) = delete; + basic_help_option_t& operator=(basic_help_option_t&&) noexcept = delete; + + ~basic_help_option_t() override; + + static value::found_func_t gen_func_print_help_and_exit( + basic_parser_t& parser); +}; + +class count_option_t : public basic_option_t<std::uint32_t> { + public: + template <typename... Params> + explicit count_option_t(basic_parser_t& parser, Params... parameters); + explicit count_option_t(const count_option_t&) = delete; + explicit count_option_t(count_option_t&&) noexcept = delete; + count_option_t& operator=(const count_option_t&) = delete; + count_option_t& operator=(count_option_t&&) noexcept = delete; ~count_option_t() override; - explicit operator bool() const; + inline explicit operator bool() const; private: void found_entry(); @@ -173,7 +223,7 @@ class count_option_t final : public basic_option_t<std::uint32_t> { }; template <typename T> -inline void gen_opt_conf_process_params(opt_params_t<T>&); +inline void gen_opt_conf_process_params(opt_params_t<T>& opt_params); template <typename T, typename Param> void gen_opt_conf_process_params(opt_params_t<T>& opt_params, Param&& param); @@ -183,50 +233,50 @@ void gen_opt_conf_process_params(opt_params_t<T>& opt_params, Param&& param, Params&&... parameters); template <typename short_option_func_t> -std::optional<basic_parser_t::basic_short_opt_conf_t<short_option_func_t>> -gen_short_option(short_option_func_t&& sof, - const std::optional<char> short_option); +std::vector<basic_parser_t::basic_short_opt_conf_t<short_option_func_t>> +gen_short_option(short_option_func_t&& sof, std::vector<char> short_option); template <typename long_option_func_t> -std::optional<basic_parser_t::basic_long_opt_conf_t<long_option_func_t>> +std::vector<basic_parser_t::basic_long_opt_conf_t<long_option_func_t>> gen_long_option(long_option_func_t&& lof, - const std::optional<std::string>& long_option); + const std::vector<std::string>& long_option); template <typename T, typename VALUE_FUNC> std::optional<basic_parser_t::validate_func_t> gen_opt_validate_func( std::optional<VALUE_FUNC>&& vf, std::optional<has_value_func_t>&& hvf, std::optional<given_func_t>&& gf, std::vector<typename opt_params_t<T>::validate_func_t>&& validate_funcs, - const std::string& option_string, const basic_parser_t::purpose_t purpose); + const std::string& option_string, basic_parser_t::purpose_t purpose); -template <typename T, typename OPT_CONF, typename CALLBACKS> -OPT_CONF gen_opt_conf( - CALLBACKS&& callbacks, const std::optional<std::string>& long_option, - const std::optional<char> short_option, - std::vector<typename opt_params_t<T>::validate_func_t>&& validate_funcs, - const std::string& description, basic_parser_t::purpose_t purpose); - -template <typename T, typename OPT_CONF, typename CALLBACKS, typename... Params> -opt_conf_container_t<T, OPT_CONF> gen_opt_conf( - CALLBACKS&& callbacks, const std::optional<std::string>& long_option, - const std::optional<char> short_option, const std::string& description, - Params&&... parameters); - -template <typename T, typename OPT_CONF, typename CALLBACKS, typename... Params> +template <typename T, typename OPT_CONF, typename CALLBACKS, typename T1, + typename... Params> opt_conf_container_t<T, OPT_CONF> gen_opt_conf(CALLBACKS&& callbacks, - const char short_option, + T1&& single_option, + const std::string& description, Params&&... parameters); -template <typename T, typename OPT_CONF, typename CALLBACKS, typename... Params> +template <typename T, typename OPT_CONF, typename CALLBACKS, typename T1, + typename T2, typename... Params> opt_conf_container_t<T, OPT_CONF> gen_opt_conf(CALLBACKS&& callbacks, - const std::string& long_option, + T1&& long_option, + T2&& short_option, + const std::string& description, Params&&... parameters); template <typename T, typename OPT_CONF, typename CALLBACKS, typename... Params> -opt_conf_container_t<T, OPT_CONF> gen_opt_conf(CALLBACKS&& callbacks, - const std::string& long_option, - const char short_option, - Params&&... parameters); +opt_conf_container_t<T, OPT_CONF> gen_opt_conf1( + CALLBACKS&& callbacks, const std::vector<std::string>& long_option, + const std::vector<char>& short_option, const std::string& description, + Params&&... parameters); + +template <typename T, typename OPT_CONF, typename CALLBACKS> +OPT_CONF gen_opt_conf2( + CALLBACKS&& callbacks, const std::vector<std::string>& long_option, + const std::vector<char>& short_option, + std::vector<typename opt_params_t<T>::validate_func_t>&& validate_funcs, + const std::string& description, basic_parser_t::purpose_t purpose); + +using help_option_t = basic_help_option_t<EXIT_SUCCESS>; using string_param_option_t = clapp::basic_param_option_t<std::string>; @@ -242,6 +292,16 @@ using int32_param_option_t = clapp::basic_param_option_t<std::int32_t>; using uint32_param_option_t = clapp::basic_param_option_t<std::uint32_t>; using int64_param_option_t = clapp::basic_param_option_t<std::int64_t>; using uint64_param_option_t = clapp::basic_param_option_t<std::uint64_t>; +using double_param_option_t = clapp::basic_param_option_t<double>; +using float_param_option_t = clapp::basic_param_option_t<float>; +using ns_param_option_t = clapp::basic_param_option_t<std::chrono::nanoseconds>; +using us_param_option_t = + clapp::basic_param_option_t<std::chrono::microseconds>; +using ms_param_option_t = + clapp::basic_param_option_t<std::chrono::milliseconds>; +using sec_param_option_t = clapp::basic_param_option_t<std::chrono::seconds>; +using min_param_option_t = clapp::basic_param_option_t<std::chrono::minutes>; +using hours_param_option_t = clapp::basic_param_option_t<std::chrono::hours>; using vector_string_param_option_t = clapp::basic_vector_param_option_t<std::string>; @@ -267,6 +327,22 @@ using vector_int64_param_option_t = clapp::basic_vector_param_option_t<std::int64_t>; 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 = + clapp::basic_vector_param_option_t<std::chrono::microseconds>; +using vector_ms_param_option_t = + clapp::basic_vector_param_option_t<std::chrono::milliseconds>; +using vector_sec_param_option_t = + clapp::basic_vector_param_option_t<std::chrono::seconds>; +using vector_min_param_option_t = + clapp::basic_vector_param_option_t<std::chrono::minutes>; +using vector_hours_param_option_t = + clapp::basic_vector_param_option_t<std::chrono::hours>; } // namespace option diff --git a/src/include/clapp/option.hpp b/src/include/clapp/option.hpp index 413b1744..b64bf1df 100644 --- a/src/include/clapp/option.hpp +++ b/src/include/clapp/option.hpp @@ -13,8 +13,8 @@ // SOFTWARE. /////////////////////////////////////////////////////////////////////////////// -#ifndef LIBCLAPP_CLI_OPTION_HPP -#define LIBCLAPP_CLI_OPTION_HPP +#ifndef CLAPP_OPTION_HPP +#define CLAPP_OPTION_HPP #include <clapp/option.h> #include <clapp/type_traits.h> @@ -23,7 +23,8 @@ #include <sstream> template <typename T> -inline void clapp::option::gen_opt_conf_process_params(opt_params_t<T>&) {} +inline void clapp::option::gen_opt_conf_process_params([ + [maybe_unused]] opt_params_t<T>& opt_params) {} template <typename T, typename Param> void clapp::option::gen_opt_conf_process_params(opt_params_t<T>& opt_params, @@ -41,6 +42,9 @@ void clapp::option::gen_opt_conf_process_params(opt_params_t<T>& opt_params, param.validate(value, option_string); }); } + if constexpr (has_found<typename std::decay<Param>::type>::value) { + opt_params.found.push_back(param); + } if constexpr (std::is_same<typename std::decay<Param>::type, basic_parser_t::purpose_t>::value) { opt_params.purpose = param; @@ -57,28 +61,35 @@ void clapp::option::gen_opt_conf_process_params(opt_params_t<T>& opt_params, } template <typename short_option_func_t> -std::optional< - clapp::basic_parser_t::basic_short_opt_conf_t<short_option_func_t>> +std::vector<clapp::basic_parser_t::basic_short_opt_conf_t<short_option_func_t>> clapp::option::gen_short_option(short_option_func_t&& sof, - const std::optional<char> short_option) { - if (short_option) { - check_short_option(short_option.value()); - return basic_parser_t::basic_short_opt_conf_t<short_option_func_t>{ - short_option.value(), std::forward<short_option_func_t>(sof)}; + const std::vector<char> short_option) { + std::vector< + clapp::basic_parser_t::basic_short_opt_conf_t<short_option_func_t>> + ret; + for (auto opt : short_option) { + check_short_option(opt); + ret.emplace_back( + basic_parser_t::basic_short_opt_conf_t<short_option_func_t>{opt, + sof}); } - return std::nullopt; + return ret; } template <typename long_option_func_t> -std::optional<clapp::basic_parser_t::basic_long_opt_conf_t<long_option_func_t>> +std::vector<clapp::basic_parser_t::basic_long_opt_conf_t<long_option_func_t>> clapp::option::gen_long_option(long_option_func_t&& lof, - const std::optional<std::string>& long_option) { - if (long_option) { - check_long_option(long_option.value()); - return basic_parser_t::basic_long_opt_conf_t<long_option_func_t>{ - long_option.value(), std::forward<long_option_func_t>(lof)}; + const std::vector<std::string>& long_option) { + std::vector< + clapp::basic_parser_t::basic_long_opt_conf_t<long_option_func_t>> + ret; + for (auto opt : long_option) { + check_long_option(opt); + ret.emplace_back( + basic_parser_t::basic_long_opt_conf_t<long_option_func_t>{opt, + lof}); } - return std::nullopt; + return ret; } template <typename T, typename VALUE_FUNC> @@ -95,7 +106,7 @@ clapp::option::gen_opt_validate_func( option_string, validate_funcs = std::move(validate_funcs)]() { if (purpose == basic_parser_t::purpose_t::mandatory && given_func) { if (!given_func.value()()) { - throw std::runtime_error( + throw clapp::exception::option_param_exception_t( std::string{"Mandatory parameter for option '"} + option_string + "' not given."); } @@ -120,7 +131,7 @@ clapp::option::gen_opt_validate_func( } } else { if (validate_funcs.size() > 0) { - throw std::runtime_error( + throw clapp::exception::option_param_exception_t( std::string{"Cannot validate option '"} + option_string + "' without a given value and has_value function."); @@ -131,30 +142,88 @@ clapp::option::gen_opt_validate_func( return std::nullopt; } -template <typename T, typename OPT_CONF, typename CALLBACKS> -OPT_CONF clapp::option::gen_opt_conf( - CALLBACKS&& callbacks, const std::optional<std::string>& long_option, - const std::optional<char> short_option, - std::vector<typename opt_params_t<T>::validate_func_t>&& validate_funcs, - 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), - std::move(option_string), - description, - purpose}; +template <typename T> +inline std::vector<std::string> clapp::option::gen_string_vec( + const std::vector<T>& vec) { + return std::vector<std::string>{vec.begin(), vec.end()}; } -template <typename T, typename OPT_CONF, typename CALLBACKS, typename... Params> +template <> +inline std::vector<std::string> clapp::option::gen_string_vec( + const std::vector<std::string>& vec) { + return vec; +} + +template <typename T, typename OPT_CONF, typename CALLBACKS, typename T1, + typename... Params> clapp::option::opt_conf_container_t<T, OPT_CONF> clapp::option::gen_opt_conf( - CALLBACKS&& callbacks, const std::optional<std::string>& long_option, - const std::optional<char> short_option, const std::string& description, + CALLBACKS&& callbacks, T1&& single_option, const std::string& description, + Params&&... parameters) { + if constexpr (clapp::is_vector<typename std::decay<T1>::type>::value) { + if constexpr (std::is_same<typename std::decay<T1>::type, + std::vector<char>>::value) { + return gen_opt_conf1<T, OPT_CONF>( + std::forward<CALLBACKS>(callbacks), std::vector<std::string>{}, + std::forward<T1>(single_option), std::move(description), + std::forward<Params>(parameters)...); + } + return gen_opt_conf1<T, OPT_CONF>( + std::forward<CALLBACKS>(callbacks), + gen_string_vec(std::forward<T1>(single_option)), + std::vector<char>{}, std::move(description), + std::forward<Params>(parameters)...); + } + if constexpr (std::is_same<typename std::decay<T1>::type, char>::value) { + return gen_opt_conf1<T, OPT_CONF>( + std::forward<CALLBACKS>(callbacks), std::vector<std::string>{}, + std::vector<char>{std::forward<T1>(single_option)}, + std::move(description), std::forward<Params>(parameters)...); + } + return gen_opt_conf1<T, OPT_CONF>( + std::forward<CALLBACKS>(callbacks), + std::vector<std::string>{std::string{std::forward<T1>(single_option)}}, + std::vector<char>{}, std::move(description), + std::forward<Params>(parameters)...); +} + +template <typename T, typename OPT_CONF, typename CALLBACKS, typename T1, + typename T2, typename... Params> +clapp::option::opt_conf_container_t<T, OPT_CONF> clapp::option::gen_opt_conf( + CALLBACKS&& callbacks, T1&& long_option, T2&& short_option, + const std::string& description, Params&&... parameters) { + if constexpr (clapp::is_vector<typename std::decay<T1>::type>::value && + clapp::is_vector<typename std::decay<T2>::type>::value) { + return gen_opt_conf1<T, OPT_CONF>( + std::forward<CALLBACKS>(callbacks), + gen_string_vec(std::forward<T1>(long_option)), + std::forward<T2>(short_option), std::move(description), + std::forward<Params>(parameters)...); + } + if constexpr (clapp::is_vector<typename std::decay<T1>::type>::value) { + return gen_opt_conf1<T, OPT_CONF>( + std::forward<CALLBACKS>(callbacks), + gen_string_vec(std::forward<T1>(long_option)), + std::vector<char>{std::forward<T2>(short_option)}, + std::move(description), std::forward<Params>(parameters)...); + } + if constexpr (clapp::is_vector<typename std::decay<T2>::type>::value) { + return gen_opt_conf1<T, OPT_CONF>( + std::forward<CALLBACKS>(callbacks), + std::vector<std::string>{std::forward<T1>(long_option)}, + std::forward<T2>(short_option), std::move(description), + std::forward<Params>(parameters)...); + } + return gen_opt_conf1<T, OPT_CONF>( + std::forward<CALLBACKS>(callbacks), + std::vector<std::string>{std::forward<T1>(long_option)}, + std::vector<char>{std::forward<T2>(short_option)}, + std::move(description), std::forward<Params>(parameters)...); +} + +template <typename T, typename OPT_CONF, typename CALLBACKS, typename... Params> +clapp::option::opt_conf_container_t<T, OPT_CONF> clapp::option::gen_opt_conf1( + CALLBACKS&& callbacks, const std::vector<std::string>& long_option, + const std::vector<char>& short_option, const std::string& description, Params&&... parameters) { opt_params_t<T> opt_params; if (std::is_same< @@ -172,42 +241,36 @@ clapp::option::opt_conf_container_t<T, OPT_CONF> clapp::option::gen_opt_conf( [](const std::string& a, const std::string& b) -> std::string { return a + (a.length() > 0 && b.length() > 0 ? ", " : "") + b; })}; - if (restriction.size() > 0) { + if (!restriction.empty()) { restriction = " (" + restriction + ")"; } return opt_conf_container_t<T, OPT_CONF>{ - gen_opt_conf<T, OPT_CONF>( + gen_opt_conf2<T, OPT_CONF>( std::forward<CALLBACKS>(callbacks), long_option, short_option, std::move(opt_params.validate_funcs), description + restriction, opt_params.purpose), - opt_params.default_value}; -} - -template <typename T, typename OPT_CONF, typename CALLBACKS, typename... Params> -clapp::option::opt_conf_container_t<T, OPT_CONF> clapp::option::gen_opt_conf( - CALLBACKS&& callbacks, const char short_option, Params&&... parameters) { - return gen_opt_conf<T, OPT_CONF>( - std::forward<CALLBACKS>(callbacks), std::nullopt, - std::make_optional(short_option), std::forward<Params>(parameters)...); + opt_params.default_value, std::move(opt_params.found)}; } -template <typename T, typename OPT_CONF, typename CALLBACKS, typename... Params> -clapp::option::opt_conf_container_t<T, OPT_CONF> clapp::option::gen_opt_conf( - CALLBACKS&& callbacks, const std::string& long_option, - Params&&... parameters) { - return gen_opt_conf<T, OPT_CONF>( - std::forward<CALLBACKS>(callbacks), std::make_optional(long_option), - std::nullopt, std::forward<Params>(parameters)...); -} - -template <typename T, typename OPT_CONF, typename CALLBACKS, typename... Params> -clapp::option::opt_conf_container_t<T, OPT_CONF> clapp::option::gen_opt_conf( - CALLBACKS&& callbacks, const std::string& long_option, - const char short_option, Params&&... parameters) { - return gen_opt_conf<T, OPT_CONF>( - std::forward<CALLBACKS>(callbacks), std::make_optional(long_option), - std::make_optional(short_option), std::forward<Params>(parameters)...); +template <typename T, typename OPT_CONF, typename CALLBACKS> +OPT_CONF clapp::option::gen_opt_conf2( + CALLBACKS&& callbacks, const std::vector<std::string>& long_option, + const std::vector<char>& short_option, + std::vector<typename opt_params_t<T>::validate_func_t>&& validate_funcs, + 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), + std::move(option_string), + description, + purpose}; } template <typename T> @@ -218,6 +281,7 @@ clapp::option::basic_param_option_t<T>::basic_param_option_t( create_callbacks(this), std::forward<Params>(parameters)...)}; parser.reg(std::move(conf.opt_conf)); _value = conf.default_value; + _found = std::move(conf.found); } template <typename T> @@ -243,20 +307,27 @@ void clapp::option::basic_param_option_t<T>::found_entry( const std::string_view param) { _given = true; _value = convert_value<T>(param); + for (auto& found_func : _found) { + found_func.found(); + } } template <typename T> -clapp::option::basic_param_option_t<T>::operator bool() const { +inline clapp::option::basic_param_option_t<T>::operator bool() const { return _value.has_value(); } template <typename T> T clapp::option::basic_param_option_t<T>::value() const { - return _value.value(); + if (_value) { + return _value.value(); + } + throw clapp::exception::value_undefined_t{ + "Requested value is not defined."}; } template <typename T> -bool clapp::option::basic_param_option_t<T>::given() const { +constexpr bool clapp::option::basic_param_option_t<T>::given() const noexcept { return _given; } @@ -270,9 +341,10 @@ clapp::option::basic_vector_param_option_t<T>::basic_vector_param_option_t( std::stringstream ss; ss << "No default value for vector based param option '" << conf.opt_conf.option_string << "' possible."; - throw std::runtime_error(ss.str()); + throw clapp::exception::option_param_exception_t(ss.str()); } parser.reg(std::move(conf.opt_conf)); + _found = std::move(conf.found); } template <typename T> @@ -299,10 +371,13 @@ void clapp::option::basic_vector_param_option_t<T>::found_entry( const std::string_view param) { _given = true; _value.push_back(convert_value<T>(param)); + for (auto& found_func : _found) { + found_func.found(); + } } template <typename T> -clapp::option::basic_vector_param_option_t<T>::operator bool() const { +inline clapp::option::basic_vector_param_option_t<T>::operator bool() const { return _value.size() > 0; } @@ -312,7 +387,8 @@ std::vector<T> clapp::option::basic_vector_param_option_t<T>::value() const { } template <typename T> -bool clapp::option::basic_vector_param_option_t<T>::given() const { +constexpr bool clapp::option::basic_vector_param_option_t<T>::given() const + noexcept { return _given; } @@ -325,6 +401,7 @@ clapp::option::basic_option_t<T>::basic_option_t(clapp::basic_parser_t& parser, callbacks, std::forward<Params>(parameters)...)}; parser.reg(std::move(conf.opt_conf)); _value = conf.default_value; + _found = std::move(conf.found); } template <typename T> @@ -332,7 +409,11 @@ clapp::option::basic_option_t<T>::~basic_option_t() = default; template <typename T> T clapp::option::basic_option_t<T>::value() const { - return _value.value(); + if (_value) { + return _value.value(); + } + throw clapp::exception::value_undefined_t{ + "Requested option value is not defined."}; } template <typename T> @@ -350,6 +431,23 @@ clapp::option::bool_option_t::bool_option_t(clapp::basic_parser_t& parser, } } +template <int EXIT_CODE> +template <typename... Params> +clapp::option::basic_help_option_t<EXIT_CODE>::basic_help_option_t( + basic_parser_t& parser, Params... parameters) + : bool_option_t{parser, std::forward<Params>(parameters)..., + gen_func_print_help_and_exit(parser)} {} + +template <int EXIT_CODE> +clapp::value::found_func_t +clapp::option::basic_help_option_t<EXIT_CODE>::gen_func_print_help_and_exit( + basic_parser_t& parser) { + return parser.gen_func_print_help_and_exit(EXIT_CODE); +} + +template <int EXIT_CODE> +clapp::option::basic_help_option_t<EXIT_CODE>::~basic_help_option_t() = default; + template <typename... Params> clapp::option::count_option_t::count_option_t(clapp::basic_parser_t& parser, Params... parameters) @@ -360,4 +458,14 @@ 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 b474aa90..a0d3c4e0 100644 --- a/src/include/clapp/parser.h +++ b/src/include/clapp/parser.h @@ -13,9 +13,10 @@ // SOFTWARE. /////////////////////////////////////////////////////////////////////////////// -#ifndef LIBCLAPP_PARSER_H -#define LIBCLAPP_PARSER_H +#ifndef CLAPP_PARSER_H +#define CLAPP_PARSER_H +#include <clapp/value.h> #include <functional> #include <gsl/gsl_assert> #include <gsl/span> @@ -30,15 +31,15 @@ struct arg_t : public gsl::span<const char* const> { public: using base = gsl::span<const char* const>; - arg_t(const char* const* argv, int argc); + inline arg_t(const char* const* argv, int argc); }; +class basic_sub_parser_t; + class basic_parser_t { public: using arg_iterator = arg_t::const_iterator; using long_opt_func_t = std::function<void(const std::string_view option)>; - using parse_func_t = - std::function<void(arg_iterator begin, arg_iterator end)>; using long_opt_param_func_t = std::function<void( const std::string_view option, const std::string_view param)>; using short_opt_func_t = std::function<void(const char option)>; @@ -74,23 +75,23 @@ class basic_parser_t { option_type_t option_type> struct reg_option_conf_t { static std::string create_option_string( - const std::optional<char> short_option, - const std::optional<std::string>& long_option); + std::vector<char> short_option, + const std::vector<std::string>& long_option); static std::string create_option_string(const std::string& long_option); - static std::string create_option_string(const char short_option); + static std::string create_option_string(char short_option); using long_opt_conf_t = basic_long_opt_conf_t<long_option_func_t>; using short_opt_conf_t = basic_short_opt_conf_t<short_option_func_t>; - std::optional<short_opt_conf_t> short_option; - std::optional<long_opt_conf_t> long_option; + std::vector<short_opt_conf_t> short_option; + std::vector<long_opt_conf_t> long_option; std::optional<validate_func_t> validate_func; std::string option_string; std::string description; - purpose_t purpose; + purpose_t purpose{purpose_t::optional}; }; struct reg_sub_parser_conf_t { - parse_func_t parse; + basic_sub_parser_t& parser; std::string sub_parser_name; std::string description; }; @@ -101,7 +102,7 @@ class basic_parser_t { std::string argument_name; std::string description; std::optional<validate_func_t> validate_func; - purpose_t purpose; + purpose_t purpose{purpose_t::mandatory}; }; using single_arg_conf_t = reg_argument_conf_t<argument_type_t::single>; @@ -121,7 +122,8 @@ class basic_parser_t { using short_opt_variant_t = std::variant<short_opt_func_t, short_opt_param_func_t>; - using sub_parsers_map_t = std::map<std::string, parse_func_t, std::less<>>; + using sub_parsers_map_t = + std::map<std::string, basic_sub_parser_t&, std::less<>>; using arguments_vector_t = std::vector<arg_conf_t>; using optional_argument_t = std::optional<arg_conf_t>; using long_options_map_t = @@ -155,6 +157,11 @@ class basic_parser_t { public: basic_parser_t(); + explicit basic_parser_t(const basic_parser_t&) = delete; + explicit basic_parser_t(basic_parser_t&&) noexcept = delete; + basic_parser_t& operator=(const basic_parser_t&) = delete; + basic_parser_t& operator=(basic_parser_t&&) noexcept = delete; + virtual ~basic_parser_t(); template <typename short_option_func_t, typename long_option_func_t> @@ -176,7 +183,7 @@ class basic_parser_t { std::optional<std::string> long_option; }; - arg_iterator process_parse_result(const arg_iterator it, + arg_iterator process_parse_result(arg_iterator it, const parse_result_t& parse_result) const; void parse(arg_iterator begin, arg_iterator end); parse_result_t parse(std::string_view option, arg_iterator it, @@ -184,7 +191,11 @@ class basic_parser_t { void validate() const; + void validate_recursive() const; + + virtual std::string gen_help_prefix() const; std::string gen_help_msg() const; + value::found_func_t gen_func_print_help_and_exit(int exit_code) const; std::size_t get_num_processed_arguments() const; @@ -202,24 +213,24 @@ class basic_parser_t { argument_descriptions_vec_t& get_optional_argument_descriptions(); private: - parse_result_t parse_arg(const std::string_view option, arg_iterator it, + parse_result_t parse_arg(std::string_view option, arg_iterator it, arg_iterator end); - parse_result_t parse_long(const std::string_view option, arg_iterator it, + parse_result_t parse_long(std::string_view option, arg_iterator it, arg_iterator end); - parse_result_t parse_short(const std::string_view option, arg_iterator it, + parse_result_t parse_short(std::string_view option, arg_iterator it, arg_iterator end); - sub_parsers_map_t sub_parsers; - long_options_map_t long_options; - short_options_map_t short_options; - arguments_vector_t arguments; - optional_argument_t optional_argument; - validate_func_vec_t validate_functions; - option_descriptions_vec_t mandatory_option_descriptions; - option_descriptions_vec_t optional_option_descriptions; - sub_parser_descriptions_vec_t sub_parser_descriptions; - argument_descriptions_vec_t mandatory_argument_descriptions; - argument_descriptions_vec_t optional_argument_descriptions; + sub_parsers_map_t sub_parsers{}; + long_options_map_t long_options{}; + short_options_map_t short_options{}; + arguments_vector_t arguments{}; + optional_argument_t optional_argument{}; + validate_func_vec_t validate_functions{}; + option_descriptions_vec_t mandatory_option_descriptions{}; + option_descriptions_vec_t optional_option_descriptions{}; + 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}; }; diff --git a/src/include/clapp/parser.hpp b/src/include/clapp/parser.hpp index f1ebc21a..7cac92a9 100644 --- a/src/include/clapp/parser.hpp +++ b/src/include/clapp/parser.hpp @@ -13,12 +13,16 @@ // SOFTWARE. /////////////////////////////////////////////////////////////////////////////// -#ifndef LIBCLAPP_PARSER_HPP -#define LIBCLAPP_PARSER_HPP +#ifndef CLAPP_PARSER_HPP +#define CLAPP_PARSER_HPP #include <clapp/parser.h> +#include <numeric> #include <sstream> +inline clapp::parser::arg_t::arg_t(const char* const* argv, int argc) + : base{gsl::make_span(argv, argc)} {} + template <typename short_option_func_t, typename long_option_func_t> constexpr bool clapp::parser::basic_parser_t::is_param_opt() { return std::is_same<short_option_func_t, short_opt_param_func_t>::value && @@ -30,33 +34,32 @@ template <typename short_option_func_t, typename long_option_func_t, void clapp::parser::basic_parser_t::reg( reg_option_conf_t<short_option_func_t, long_option_func_t, option_type>&& config) { - if (config.option_string.size() == 0) { - throw std::runtime_error("Option string '' is too short."); - } - - if (config.short_option) { - std::pair<short_options_map_t::iterator, bool> ret{ - get_short_options().emplace( - std::move(config.short_option.value().option), - std::move(config.short_option.value().func))}; - if (!ret.second) { - std::stringstream ss; - ss << "Can't register option '" - << config.short_option.value().option - << "' as it is already registered."; - throw std::runtime_error(ss.str()); + Expects(config.option_string.size() > 1); + + if (!config.short_option.empty()) { + for (auto& so : config.short_option) { + std::pair<short_options_map_t::iterator, bool> ret{ + get_short_options().emplace(std::move(so.option), + std::move(so.func))}; + if (!ret.second) { + std::stringstream ss; + ss << "Can't register option '" << so.option + << "' as it is already registered."; + throw clapp::exception::option_exception_t(ss.str()); + } } } - if (config.long_option) { - std::pair<long_options_map_t::iterator, bool> ret{ - get_long_options().emplace( - std::move(config.long_option.value().option), - std::move(config.long_option.value().func))}; - if (!ret.second) { - std::stringstream ss; - ss << "Can't register option '" << config.long_option.value().option - << "' as it is already registered."; - throw std::runtime_error(ss.str()); + if (!config.long_option.empty()) { + for (auto& lo : config.long_option) { + std::pair<long_options_map_t::iterator, bool> ret{ + get_long_options().emplace(std::move(lo.option), + std::move(lo.func))}; + if (!ret.second) { + std::stringstream ss; + ss << "Can't register option '" << lo.option + << "' as it is already registered."; + throw clapp::exception::option_exception_t(ss.str()); + } } } @@ -89,7 +92,7 @@ void clapp::parser::basic_parser_t::reg( if (config.argument_name.size() == 0) { std::stringstream ss; ss << "Argument name '" << config.argument_name << "' is too short."; - throw std::runtime_error(ss.str()); + throw clapp::exception::argument_exception_t(ss.str()); } const std::size_t num_arguments{get_arguments().size()}; @@ -98,7 +101,7 @@ void clapp::parser::basic_parser_t::reg( std::stringstream ss; ss << "Can't register argument '" << config.argument_name << "' when variadic arguments are already registered."; - throw std::runtime_error(ss.str()); + throw clapp::exception::argument_exception_t(ss.str()); } if (max_option_string_size < config.argument_name.size()) { @@ -106,21 +109,21 @@ void clapp::parser::basic_parser_t::reg( } if (config.purpose == purpose_t::mandatory) { - if (get_optional_argument_descriptions().size() > 0) { + if (!get_optional_argument_descriptions().empty()) { std::stringstream ss; ss << "Can't register mandatory argument '" << config.argument_name << "' when optional arguments are already registered."; - throw std::runtime_error(ss.str()); + throw clapp::exception::argument_exception_t(ss.str()); } get_mandatory_argument_descriptions().push_back( {config.argument_name, std::move(config.description), argument_type}); } else { - if (get_sub_parser_descriptions().size() > 0) { + if (!get_sub_parser_descriptions().empty()) { std::stringstream ss; ss << "Can't register optional argument '" << config.argument_name << "' when a sub-parser is already registered."; - throw std::runtime_error(ss.str()); + throw clapp::exception::argument_exception_t(ss.str()); } get_optional_argument_descriptions().push_back( {config.argument_name, std::move(config.description), @@ -156,18 +159,25 @@ template <typename short_option_func_t, typename long_option_func_t, clapp::parser::basic_parser_t::option_type_t option_type> std::string clapp::parser::basic_parser_t::reg_option_conf_t< short_option_func_t, long_option_func_t, - option_type>::create_option_string(const std::optional<char> short_option, - const std::optional<std::string>& + option_type>::create_option_string(const std::vector<char> short_option, + const std::vector<std::string>& long_option) { std::string option_string; - if (short_option) { - option_string += create_option_string(short_option.value()); + if (!short_option.empty()) { + option_string = std::accumulate( + short_option.begin(), short_option.end(), std::string(), + [](const std::string& a, const char b) -> std::string { + std::string opt_str{create_option_string(b)}; + return a + (a.length() > 0 ? "|" : "") + opt_str; + }); } - if (long_option) { - if (option_string != "") { - option_string += "|"; - } - option_string += create_option_string(long_option.value()); + if (!long_option.empty()) { + option_string = std::accumulate( + long_option.begin(), long_option.end(), option_string, + [](const std::string& a, const std::string& b) -> std::string { + std::string opt_str{create_option_string(b)}; + return a + (a.length() > 0 ? "|" : "") + opt_str; + }); } return option_string; } diff --git a/src/include/clapp/sub_parser.h b/src/include/clapp/sub_parser.h index 8db5f4e2..9d2423ff 100644 --- a/src/include/clapp/sub_parser.h +++ b/src/include/clapp/sub_parser.h @@ -13,8 +13,8 @@ // SOFTWARE. /////////////////////////////////////////////////////////////////////////////// -#ifndef LIBCLAPP_SUB_PARSER_H -#define LIBCLAPP_SUB_PARSER_H +#ifndef CLAPP_SUB_PARSER_H +#define CLAPP_SUB_PARSER_H #include <clapp/parser.h> @@ -23,14 +23,14 @@ namespace clapp { inline namespace parser { class basic_sub_parser_t : public basic_parser_t { public: - basic_sub_parser_t(basic_parser_t& parser, - const std::string& sub_parser_name, - const std::string& description, - bool _parse_parent = true); + basic_sub_parser_t(basic_parser_t& parser, std::string sub_parser_name_arg, + std::string description_arg, + bool parse_parent_arg = true); ~basic_sub_parser_t() override; explicit operator bool() const; std::string get_sub_parser_name() const; void sub_parse(arg_iterator begin, arg_iterator end); + std::string gen_help_prefix() const override; private: basic_parser_t& parent_parser; diff --git a/src/include/clapp/sub_parser.hpp b/src/include/clapp/sub_parser.hpp index 1b237ab1..b3a2c54b 100644 --- a/src/include/clapp/sub_parser.hpp +++ b/src/include/clapp/sub_parser.hpp @@ -13,8 +13,8 @@ // SOFTWARE. /////////////////////////////////////////////////////////////////////////////// -#ifndef LIBCLAPP_SUB_PARSER_HPP -#define LIBCLAPP_SUB_PARSER_HPP +#ifndef CLAPP_SUB_PARSER_HPP +#define CLAPP_SUB_PARSER_HPP #include <clapp/sub_parser.h> diff --git a/src/include/clapp/type_traits.h b/src/include/clapp/type_traits.h index d036d54f..ea5a8490 100644 --- a/src/include/clapp/type_traits.h +++ b/src/include/clapp/type_traits.h @@ -13,22 +13,38 @@ // SOFTWARE. /////////////////////////////////////////////////////////////////////////////// -#ifndef LIBCLAPP_TYPE_TRAITS_H -#define LIBCLAPP_TYPE_TRAITS_H +#ifndef CLAPP_TYPE_TRAITS_H +#define CLAPP_TYPE_TRAITS_H #include <type_traits> +#include <chrono> #include <iostream> #include <type_traits> +#include <vector> namespace clapp { inline namespace type_traits { +template <typename T> +struct is_chrono_duration : std::false_type {}; + +template <typename Rep, typename Period> +struct is_chrono_duration<std::chrono::duration<Rep, Period>> : std::true_type { +}; + +template <typename T> +struct is_vector : std::false_type {}; + +template <typename T, typename A> +struct is_vector<std::vector<T, A>> : std::true_type {}; + template <typename T> class has_append_restriction_text { private: template <typename C> - constexpr static bool test(decltype(&C::append_restriction_text)) { + constexpr static bool test([ + [maybe_unused]] decltype(&C::append_restriction_text) func) { return true; } template <typename C> @@ -44,7 +60,7 @@ template <typename T> class has_validate { private: template <typename C> - constexpr static bool test(decltype(&C::validate)) { + constexpr static bool test([[maybe_unused]] decltype(&C::validate) func) { return true; } template <typename C> @@ -60,7 +76,24 @@ template <typename T> class has_default_value { private: template <typename C> - constexpr static bool test(decltype(&C::default_value)) { + constexpr static bool test([ + [maybe_unused]] decltype(&C::default_value) func) { + return true; + } + template <typename C> + constexpr static bool test(...) { + return false; + } + + public: + constexpr static bool value{test<T>(nullptr)}; +}; + +template <typename T> +class has_found { + private: + template <typename C> + constexpr static bool test([[maybe_unused]] decltype(&C::found) func) { return true; } template <typename C> diff --git a/src/include/clapp/value.h b/src/include/clapp/value.h index 3eac3df6..9a3c846a 100644 --- a/src/include/clapp/value.h +++ b/src/include/clapp/value.h @@ -13,10 +13,11 @@ // SOFTWARE. /////////////////////////////////////////////////////////////////////////////// -#ifndef LIBCLAPP_VALUE_H -#define LIBCLAPP_VALUE_H +#ifndef CLAPP_VALUE_H +#define CLAPP_VALUE_H #include <clapp/filesystem.h> +#include <functional> #include <string> #include <string_view> @@ -24,11 +25,17 @@ namespace clapp { inline namespace value { template <typename T> -T convert_value(const std::string_view param); +T convert_value(std::string_view param); + +template <typename T> +constexpr const char *get_chrono_postfix() noexcept; + +template <typename T> +std::string to_string(const T &value); template <typename T> struct default_value_t { - constexpr default_value_t(T _value); + explicit default_value_t(T _value); std::string append_restriction_text() const; @@ -41,7 +48,7 @@ struct default_value_t { template <typename T> class min_max_value_t { public: - constexpr min_max_value_t(T _min, T _max); + min_max_value_t(T _min, T _max); std::string append_restriction_text() const; @@ -52,6 +59,17 @@ class min_max_value_t { T max; }; +class found_func_t { + public: + using func_t = std::function<void()>; + explicit found_func_t(func_t &&func_arg); + + void found(); + + private: + func_t func; +}; + #ifdef CLAPP_FS_AVAIL class path_exists_t { public: diff --git a/src/include/clapp/value.hpp b/src/include/clapp/value.hpp index c595ff4b..41eae0a8 100644 --- a/src/include/clapp/value.hpp +++ b/src/include/clapp/value.hpp @@ -13,36 +13,62 @@ // SOFTWARE. /////////////////////////////////////////////////////////////////////////////// -#ifndef LIBCLAPP_VALUE_HPP -#define LIBCLAPP_VALUE_HPP +#ifndef CLAPP_VALUE_HPP +#define CLAPP_VALUE_HPP +#include <clapp/exception.h> +#include <clapp/type_traits.h> #include <clapp/value.h> #include <gsl/gsl_assert> #include <sstream> template <typename T> -constexpr clapp::value::default_value_t<T>::default_value_t(T _value) - : value{_value} {} +clapp::value::default_value_t<T>::default_value_t(T _value) : value{_value} {} template <typename T> -std::string clapp::value::default_value_t<T>::append_restriction_text() const { +constexpr const char* clapp::value::get_chrono_postfix() noexcept { + const char* postfix{""}; + if constexpr (std::is_same<T, std::chrono::nanoseconds>::value) { + postfix = "ns"; + } else if constexpr (std::is_same<T, std::chrono::microseconds>::value) { + postfix = "µs"; + } else if constexpr (std::is_same<T, std::chrono::milliseconds>::value) { + postfix = "ms"; + } else if constexpr (std::is_same<T, std::chrono::seconds>::value) { + postfix = "s"; + } else if constexpr (std::is_same<T, std::chrono::minutes>::value) { + postfix = "m"; + } else if constexpr (std::is_same<T, std::chrono::hours>::value) { + postfix = "h"; + } + return postfix; +} + +template <typename T> +std::string clapp::value::to_string(const T& value) { std::stringstream ss; - ss << "default-value: "; if constexpr (std::is_integral<T>::value) { ss << std::to_string(value); + } else if constexpr (is_chrono_duration<T>::value) { + ss << value.count() << get_chrono_postfix<T>(); } else { ss << value; } return ss.str(); } +template <typename T> +std::string clapp::value::default_value_t<T>::append_restriction_text() const { + return "default-value: " + to_string(value); +} + template <typename T> T clapp::value::default_value_t<T>::default_value() const { return value; } template <typename T> -constexpr clapp::value::min_max_value_t<T>::min_max_value_t(T _min, T _max) +clapp::value::min_max_value_t<T>::min_max_value_t(T _min, T _max) : min{_min}, max{_max} { Expects(min < max); } @@ -50,80 +76,89 @@ constexpr clapp::value::min_max_value_t<T>::min_max_value_t(T _min, T _max) template <typename T> std::string clapp::value::min_max_value_t<T>::append_restriction_text() const { std::stringstream ss; - if constexpr (std::is_integral<T>::value) { - ss << "constraint: [" << std::to_string(min) << "," - << std::to_string(max) << "]"; - } else { - ss << "constraint: [" << min << "," << max << "]"; - } + ss << "constraint: [" << to_string(min) << "," << to_string(max) << "]"; return ss.str(); } template <typename T> void clapp::value::min_max_value_t<T>::validate( - const T &value, const std::string ¶m_name) const { + const T& value, const std::string& param_name) const { if (value < min) { - std::stringstream ss; - ss << "CLI value for '" << param_name << "' is too low ("; - if constexpr (std::is_integral<T>::value) { - ss << std::to_string(value) << " < " << std::to_string(min) << ")"; - } else { - ss << value << " < " << min << ")"; - } - throw std::out_of_range{ss.str()}; + throw clapp::exception::out_of_range_t{ + "CLI value for '" + param_name + "' is too low (" + + to_string(value) + " < " + to_string(min) + ")"}; } if (value > max) { - std::stringstream ss; - ss << "CLI value for '" << param_name << "' is too high ("; - if constexpr (std::is_integral<T>::value) { - ss << std::to_string(value) << " < " << std::to_string(min) << ")"; - } else { - ss << value << " > " << min << ")"; - } - throw std::out_of_range{ss.str()}; + throw clapp::exception::out_of_range_t{ + "CLI value for '" + param_name + "' is too high (" + + to_string(value) + " > " + to_string(max) + ")"}; } } template <> -std::string clapp::value::convert_value<std::string>( - const std::string_view param); +std::string clapp::value::convert_value<std::string>(std::string_view param); template <> -std::int8_t clapp::value::convert_value<std::int8_t>( - const std::string_view param); +std::int8_t clapp::value::convert_value<std::int8_t>(std::string_view param); template <> -std::int16_t clapp::value::convert_value<std::int16_t>( - const std::string_view param); +std::int16_t clapp::value::convert_value<std::int16_t>(std::string_view param); template <> -std::int32_t clapp::value::convert_value<std::int32_t>( - const std::string_view param); +std::int32_t clapp::value::convert_value<std::int32_t>(std::string_view param); template <> -std::int64_t clapp::value::convert_value<std::int64_t>( - const std::string_view param); +std::int64_t clapp::value::convert_value<std::int64_t>(std::string_view param); template <> -std::uint8_t clapp::value::convert_value<std::uint8_t>( - const std::string_view param); +std::uint8_t clapp::value::convert_value<std::uint8_t>(std::string_view param); template <> std::uint16_t clapp::value::convert_value<std::uint16_t>( - const std::string_view param); + std::string_view param); template <> std::uint32_t clapp::value::convert_value<std::uint32_t>( - const std::string_view param); + std::string_view param); template <> std::uint64_t clapp::value::convert_value<std::uint64_t>( - const std::string_view param); + std::string_view param); + +template <> +std::chrono::nanoseconds clapp::value::convert_value<std::chrono::nanoseconds>( + std::string_view param); + +template <> +std::chrono::microseconds +clapp::value::convert_value<std::chrono::microseconds>(std::string_view param); + +template <> +std::chrono::milliseconds +clapp::value::convert_value<std::chrono::milliseconds>(std::string_view param); + +template <> +std::chrono::seconds clapp::value::convert_value<std::chrono::seconds>( + std::string_view param); + +template <> +std::chrono::minutes clapp::value::convert_value<std::chrono::minutes>( + std::string_view param); + +template <> +std::chrono::hours clapp::value::convert_value<std::chrono::hours>( + std::string_view param); + +template <> +float clapp::value::convert_value<float>(std::string_view param); + +template <> +double clapp::value::convert_value<double>(std::string_view param); #ifdef CLAPP_FS_AVAIL template <> clapp::fs::path clapp::value::convert_value<clapp::fs::path>( - const std::string_view param); + std::string_view param); #endif #endif diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index b12defb9..74a3ae8a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,5 +1,10 @@ -set(sources value.cpp argument.cpp option.cpp parser.cpp sub_parser.cpp main_parser.cpp) +set(sources value.cpp argument.cpp option.cpp parser.cpp sub_parser.cpp main_parser.cpp exception.cpp) add_executable(libclapp_tests ${sources}) -target_link_libraries(libclapp_tests gtest_main gmock clapp) +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") +endif() + diff --git a/tests/argument.cpp b/tests/argument.cpp index a4deee71..ca4b9997 100644 --- a/tests/argument.cpp +++ b/tests/argument.cpp @@ -1,44 +1,133 @@ #include <clapp/argument.h> +#include <clapp/sub_parser.h> #include <gmock/gmock.h> -class test_parser_t : public clapp::parser::basic_parser_t { - public: +class argument_test_parser_t : public clapp::parser::basic_parser_t { + public: using clapp::parser::basic_parser_t::purpose_t; + using clapp::parser::basic_parser_t::argument_descriptions_vec_t; using clapp::parser::basic_parser_t::argument_type_t; using clapp::parser::basic_parser_t::arguments_vector_t; - using clapp::parser::basic_parser_t::argument_descriptions_vec_t; using clapp::parser::basic_parser_t::validate_func_vec_t; using clapp::parser::basic_parser_t::get_arguments; using clapp::parser::basic_parser_t::get_mandatory_argument_descriptions; 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; +}; + +argument_test_parser_t::~argument_test_parser_t() = default; + +class simple_argument_sub_parser_t : public clapp::parser::basic_sub_parser_t { + using clapp::parser::basic_sub_parser_t::basic_sub_parser_t; + + public: + ~simple_argument_sub_parser_t() override; }; +simple_argument_sub_parser_t::~simple_argument_sub_parser_t() = default; + +TEST(argument, basic_argument_construct_to_short_argument_name_throws) { + const std::string arg_name; + const std::string arg_desc{"description"}; + argument_test_parser_t tp; + ASSERT_THROW((clapp::argument::string_argument_t{tp, arg_name, arg_desc}), + clapp::exception::argument_exception_t); +} + +TEST( + argument, + basic_argument_construct_variadic_argument_and_register_another_argument_throws) { + const std::string variadic_arg_name{"variadic"}; + const std::string arg_name{"arg"}; + const std::string arg_desc{"description"}; + argument_test_parser_t tp; + clapp::argument::basic_variadic_argument_t<std::int32_t> arg{ + tp, variadic_arg_name, arg_desc}; + ASSERT_THROW((clapp::argument::string_argument_t{tp, arg_name, arg_desc}), + clapp::exception::argument_exception_t); +} + +TEST( + argument, + basic_argument_construct_optional_argument_and_register_mandatory_argument_throws) { + const std::string optional_arg_name{"variadic"}; + const std::string arg_name{"arg"}; + const std::string arg_desc{"description"}; + argument_test_parser_t tp; + clapp::argument::basic_argument_t<std::int32_t> arg{ + tp, optional_arg_name, arg_desc, + argument_test_parser_t::purpose_t::optional}; + ASSERT_THROW((clapp::argument::string_argument_t{tp, arg_name, arg_desc}), + clapp::exception::argument_exception_t); +} + +TEST(argument, + basic_argument_construct_sub_parser_and_register_argument_throws) { + const std::string arg_name{"arg"}; + const std::string arg_desc{"description"}; + argument_test_parser_t tp; + simple_argument_sub_parser_t sub_parser{tp, "sub-parser", + "Sub parser desc"}; + ASSERT_THROW((clapp::argument::string_argument_t{ + tp, arg_name, arg_desc, + argument_test_parser_t::purpose_t::optional}), + clapp::exception::argument_exception_t); +} + +TEST(argument, basic_argument_found_func_long) { + const std::string arg_name{"arg"}; + const std::string arg_desc{"description"}; + + std::stringstream ss; + argument_test_parser_t tp; + clapp::argument::basic_argument_t<std::string> arg{ + tp, arg_name, arg_desc, + clapp::value::found_func_t{[&ss]() { ss << "this is a test"; }}}; + argument_test_parser_t::arguments_vector_t args{tp.get_arguments()}; + argument_test_parser_t::argument_descriptions_vec_t descs{ + tp.get_mandatory_argument_descriptions()}; + + ASSERT_THAT(args.size(), testing::Eq(1)); + ASSERT_THAT(args[0].argument, testing::StrEq(arg_name)); + ASSERT_THAT(args[0].argument_type, + testing::Eq(argument_test_parser_t::argument_type_t::single)); + + args[0].func("argument-value"); + ASSERT_THAT(ss.str(), testing::StrEq("this is a test")); +} + TEST(argument, basic_argument_construct_simple) { const std::string arg_name{"argument name"}; const std::string arg_desc{"description"}; - test_parser_t tp; + argument_test_parser_t tp; clapp::argument::basic_argument_t<std::int32_t> arg{tp, arg_name, arg_desc}; - test_parser_t::arguments_vector_t args{tp.get_arguments()}; - test_parser_t::argument_descriptions_vec_t descs{tp.get_mandatory_argument_descriptions()}; - test_parser_t::validate_func_vec_t validate_funcs{tp.get_validate_functions()}; + argument_test_parser_t::arguments_vector_t args{tp.get_arguments()}; + argument_test_parser_t::argument_descriptions_vec_t descs{ + tp.get_mandatory_argument_descriptions()}; + argument_test_parser_t::validate_func_vec_t validate_funcs{ + tp.get_validate_functions()}; ASSERT_THAT(validate_funcs.size(), testing::Eq(1)); ASSERT_THAT(descs.size(), testing::Eq(1)); ASSERT_THAT(descs[0].argument_string, testing::StrEq(arg_name)); ASSERT_THAT(descs[0].description, testing::StrEq(arg_desc)); - ASSERT_THAT(descs[0].argument_type, testing::Eq(test_parser_t::argument_type_t::single)); + ASSERT_THAT(descs[0].argument_type, + testing::Eq(argument_test_parser_t::argument_type_t::single)); ASSERT_THAT(args.size(), testing::Eq(1)); - ASSERT_THAT(args[0].argument,testing::StrEq(arg_name)); - ASSERT_THAT(args[0].argument_type, testing::Eq(test_parser_t::argument_type_t::single)); + ASSERT_THAT(args[0].argument, testing::StrEq(arg_name)); + ASSERT_THAT(args[0].argument_type, + testing::Eq(argument_test_parser_t::argument_type_t::single)); ASSERT_THAT(static_cast<bool>(arg), testing::Eq(false)); ASSERT_THAT(arg.given(), testing::Eq(false)); - ASSERT_THROW(arg.value(), std::exception); - ASSERT_THROW(validate_funcs[0](), std::runtime_error); + ASSERT_THROW(arg.value(), clapp::exception::value_undefined_t); + ASSERT_THROW(validate_funcs[0](), clapp::exception::argument_exception_t); args[0].func("12345"); ASSERT_THAT(static_cast<bool>(arg), testing::Eq(true)); @@ -50,27 +139,31 @@ TEST(argument, basic_argument_construct_simple) { TEST(argument, basic_argument_construct_mandatory) { const std::string arg_name{"argument name"}; const std::string arg_desc{"description"}; - test_parser_t tp; - clapp::argument::basic_argument_t<std::string> arg{tp, arg_name, arg_desc, - test_parser_t::purpose_t::mandatory}; - test_parser_t::arguments_vector_t args{tp.get_arguments()}; - test_parser_t::argument_descriptions_vec_t descs{tp.get_mandatory_argument_descriptions()}; - test_parser_t::validate_func_vec_t validate_funcs{tp.get_validate_functions()}; + argument_test_parser_t tp; + clapp::argument::basic_argument_t<std::string> arg{ + tp, arg_name, arg_desc, argument_test_parser_t::purpose_t::mandatory}; + argument_test_parser_t::arguments_vector_t args{tp.get_arguments()}; + argument_test_parser_t::argument_descriptions_vec_t descs{ + tp.get_mandatory_argument_descriptions()}; + argument_test_parser_t::validate_func_vec_t validate_funcs{ + tp.get_validate_functions()}; ASSERT_THAT(validate_funcs.size(), testing::Eq(1)); ASSERT_THAT(descs.size(), testing::Eq(1)); ASSERT_THAT(descs[0].argument_string, testing::StrEq(arg_name)); ASSERT_THAT(descs[0].description, testing::StrEq(arg_desc)); - ASSERT_THAT(descs[0].argument_type, testing::Eq(test_parser_t::argument_type_t::single)); + ASSERT_THAT(descs[0].argument_type, + testing::Eq(argument_test_parser_t::argument_type_t::single)); ASSERT_THAT(args.size(), testing::Eq(1)); - ASSERT_THAT(args[0].argument,testing::StrEq(arg_name)); - ASSERT_THAT(args[0].argument_type, testing::Eq(test_parser_t::argument_type_t::single)); + ASSERT_THAT(args[0].argument, testing::StrEq(arg_name)); + ASSERT_THAT(args[0].argument_type, + testing::Eq(argument_test_parser_t::argument_type_t::single)); ASSERT_THAT(static_cast<bool>(arg), testing::Eq(false)); ASSERT_THAT(arg.given(), testing::Eq(false)); - ASSERT_THROW(arg.value(), std::exception); - ASSERT_THROW(validate_funcs[0](), std::runtime_error); + ASSERT_THROW(arg.value(), clapp::exception::value_undefined_t); + ASSERT_THROW(validate_funcs[0](), clapp::exception::argument_exception_t); args[0].func("string-arg"); ASSERT_THAT(static_cast<bool>(arg), testing::Eq(true)); @@ -82,26 +175,30 @@ TEST(argument, basic_argument_construct_mandatory) { TEST(argument, basic_argument_construct_optional) { const std::string arg_name{"argument name"}; const std::string arg_desc{"description"}; - test_parser_t tp; - clapp::argument::basic_argument_t<std::int32_t> arg{tp, arg_name, arg_desc, - test_parser_t::purpose_t::optional}; - test_parser_t::arguments_vector_t args{tp.get_arguments()}; - test_parser_t::argument_descriptions_vec_t descs{tp.get_optional_argument_descriptions()}; - test_parser_t::validate_func_vec_t validate_funcs{tp.get_validate_functions()}; + argument_test_parser_t tp; + clapp::argument::basic_argument_t<std::int32_t> arg{ + tp, arg_name, arg_desc, argument_test_parser_t::purpose_t::optional}; + argument_test_parser_t::arguments_vector_t args{tp.get_arguments()}; + argument_test_parser_t::argument_descriptions_vec_t descs{ + tp.get_optional_argument_descriptions()}; + argument_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].argument_string, testing::StrEq(arg_name)); ASSERT_THAT(descs[0].description, testing::StrEq(arg_desc)); - ASSERT_THAT(descs[0].argument_type, testing::Eq(test_parser_t::argument_type_t::single)); + ASSERT_THAT(descs[0].argument_type, + testing::Eq(argument_test_parser_t::argument_type_t::single)); ASSERT_THAT(args.size(), testing::Eq(1)); - ASSERT_THAT(args[0].argument,testing::StrEq(arg_name)); - ASSERT_THAT(args[0].argument_type, testing::Eq(test_parser_t::argument_type_t::single)); + ASSERT_THAT(args[0].argument, testing::StrEq(arg_name)); + ASSERT_THAT(args[0].argument_type, + testing::Eq(argument_test_parser_t::argument_type_t::single)); ASSERT_THAT(static_cast<bool>(arg), testing::Eq(false)); ASSERT_THAT(arg.given(), testing::Eq(false)); - ASSERT_THROW(arg.value(), std::exception); + ASSERT_THROW(arg.value(), clapp::exception::value_undefined_t); args[0].func("12345"); ASSERT_THAT(static_cast<bool>(arg), testing::Eq(true)); @@ -112,22 +209,30 @@ TEST(argument, basic_argument_construct_optional) { TEST(argument, basic_argument_construct_optional_default_value) { const std::string arg_name{"argument name"}; const std::string arg_desc{"description"}; - test_parser_t tp; + argument_test_parser_t tp; const clapp::value::default_value_t<std::int8_t> default_value{10}; - clapp::argument::basic_argument_t<std::int8_t> arg{tp, arg_name, arg_desc, test_parser_t::purpose_t::optional, default_value}; - test_parser_t::arguments_vector_t args{tp.get_arguments()}; - test_parser_t::argument_descriptions_vec_t descs{tp.get_optional_argument_descriptions()}; - test_parser_t::validate_func_vec_t validate_funcs{tp.get_validate_functions()}; + clapp::argument::basic_argument_t<std::int8_t> arg{ + tp, arg_name, arg_desc, argument_test_parser_t::purpose_t::optional, + default_value}; + argument_test_parser_t::arguments_vector_t args{tp.get_arguments()}; + argument_test_parser_t::argument_descriptions_vec_t descs{ + tp.get_optional_argument_descriptions()}; + argument_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].argument_string, testing::StrEq(arg_name)); - ASSERT_THAT(descs[0].description, testing::StrEq(arg_desc + " (" + default_value.append_restriction_text() + ")")); - ASSERT_THAT(descs[0].argument_type, testing::Eq(test_parser_t::argument_type_t::single)); + ASSERT_THAT(descs[0].description, + testing::StrEq(arg_desc + " (" + + default_value.append_restriction_text() + ")")); + ASSERT_THAT(descs[0].argument_type, + testing::Eq(argument_test_parser_t::argument_type_t::single)); ASSERT_THAT(args.size(), testing::Eq(1)); - ASSERT_THAT(args[0].argument,testing::StrEq(arg_name)); - ASSERT_THAT(args[0].argument_type, testing::Eq(test_parser_t::argument_type_t::single)); + ASSERT_THAT(args[0].argument, testing::StrEq(arg_name)); + ASSERT_THAT(args[0].argument_type, + testing::Eq(argument_test_parser_t::argument_type_t::single)); ASSERT_THAT(static_cast<bool>(arg), testing::Eq(true)); ASSERT_THAT(arg.given(), testing::Eq(false)); @@ -143,28 +248,40 @@ TEST(argument, basic_argument_construct_mandatory_default_value) { const std::string arg_name{"argument name"}; const std::string arg_desc{"desciption"}; const std::uint32_t default_value{1123}; - test_parser_t tp; - clapp::argument::basic_argument_t<std::uint32_t> arg{tp, arg_name, arg_desc, test_parser_t::purpose_t::optional, clapp::value::default_value_t<std::uint32_t>{default_value}}; - test_parser_t::arguments_vector_t args{tp.get_arguments()}; - test_parser_t::argument_descriptions_vec_t descs{tp.get_optional_argument_descriptions()}; - test_parser_t::validate_func_vec_t validate_funcs{tp.get_validate_functions()}; + argument_test_parser_t tp; + clapp::argument::basic_argument_t<std::uint32_t> arg{ + tp, arg_name, arg_desc, argument_test_parser_t::purpose_t::optional, + clapp::value::default_value_t<std::uint32_t>{default_value}}; + argument_test_parser_t::arguments_vector_t args{tp.get_arguments()}; + argument_test_parser_t::argument_descriptions_vec_t descs{ + tp.get_optional_argument_descriptions()}; + argument_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].argument_string, testing::StrEq(arg_name)); - ASSERT_THAT(descs[0].description, testing::StrEq(arg_desc + " (" + clapp::value::default_value_t{default_value}.append_restriction_text() + ")")); - ASSERT_THAT(descs[0].argument_type, testing::Eq(test_parser_t::argument_type_t::single)); + ASSERT_THAT(descs[0].description, + testing::StrEq( + arg_desc + " (" + + clapp::value::default_value_t<std::uint32_t>{default_value} + .append_restriction_text() + + ")")); + ASSERT_THAT(descs[0].argument_type, + testing::Eq(argument_test_parser_t::argument_type_t::single)); ASSERT_THAT(args.size(), testing::Eq(1)); - ASSERT_THAT(args[0].argument,testing::StrEq(arg_name)); - ASSERT_THAT(args[0].argument_type, testing::Eq(test_parser_t::argument_type_t::single)); + ASSERT_THAT(args[0].argument, testing::StrEq(arg_name)); + ASSERT_THAT(args[0].argument_type, + testing::Eq(argument_test_parser_t::argument_type_t::single)); ASSERT_THAT(static_cast<bool>(arg), testing::Eq(true)); ASSERT_THAT(arg.given(), testing::Eq(false)); ASSERT_THAT(arg.value(), testing::Eq(default_value)); - ASSERT_THROW(args[0].func("0x123412341234"), std::out_of_range); - ASSERT_THROW(args[0].func("-0x12341234"), std::out_of_range); + ASSERT_THROW(args[0].func("0x123412341234"), + clapp::exception::out_of_range_t); + ASSERT_THROW(args[0].func("-0x12341234"), clapp::exception::out_of_range_t); args[0].func("0x12341234"); ASSERT_THAT(static_cast<bool>(arg), testing::Eq(true)); @@ -175,24 +292,37 @@ TEST(argument, basic_argument_construct_mandatory_default_value) { TEST(argument, basic_argument_construct_optional_default_value_min_max) { const std::string arg_name{"argument name"}; const std::string arg_desc{"desciption"}; - test_parser_t tp; + argument_test_parser_t tp; const clapp::value::default_value_t<std::int64_t> default_value{0x3}; const clapp::value::min_max_value_t<std::int64_t> min_max{-2, 10}; - clapp::argument::basic_argument_t<std::int64_t> arg{tp, arg_name, arg_desc, test_parser_t::purpose_t::optional, default_value, min_max}; - test_parser_t::arguments_vector_t args{tp.get_arguments()}; - test_parser_t::argument_descriptions_vec_t descs{tp.get_optional_argument_descriptions()}; - test_parser_t::validate_func_vec_t validate_funcs{tp.get_validate_functions()}; + clapp::argument::basic_argument_t<std::int64_t> arg{ + tp, + arg_name, + arg_desc, + argument_test_parser_t::purpose_t::optional, + default_value, + min_max}; + argument_test_parser_t::arguments_vector_t args{tp.get_arguments()}; + argument_test_parser_t::argument_descriptions_vec_t descs{ + tp.get_optional_argument_descriptions()}; + argument_test_parser_t::validate_func_vec_t validate_funcs{ + tp.get_validate_functions()}; ASSERT_THAT(validate_funcs.size(), testing::Eq(1)); ASSERT_NO_THROW(validate_funcs[0]()); ASSERT_THAT(descs.size(), testing::Eq(1)); ASSERT_THAT(descs[0].argument_string, testing::StrEq(arg_name)); - ASSERT_THAT(descs[0].description, testing::StrEq(arg_desc + " (" + default_value.append_restriction_text() + ", " + min_max.append_restriction_text() + ")")); - ASSERT_THAT(descs[0].argument_type, testing::Eq(test_parser_t::argument_type_t::single)); + ASSERT_THAT(descs[0].description, + testing::StrEq(arg_desc + " (" + + default_value.append_restriction_text() + ", " + + min_max.append_restriction_text() + ")")); + ASSERT_THAT(descs[0].argument_type, + testing::Eq(argument_test_parser_t::argument_type_t::single)); ASSERT_THAT(args.size(), testing::Eq(1)); - ASSERT_THAT(args[0].argument,testing::StrEq(arg_name)); - ASSERT_THAT(args[0].argument_type, testing::Eq(test_parser_t::argument_type_t::single)); + ASSERT_THAT(args[0].argument, testing::StrEq(arg_name)); + ASSERT_THAT(args[0].argument_type, + testing::Eq(argument_test_parser_t::argument_type_t::single)); ASSERT_THAT(static_cast<bool>(arg), testing::Eq(true)); ASSERT_THAT(arg.given(), testing::Eq(false)); @@ -205,42 +335,52 @@ TEST(argument, basic_argument_construct_optional_default_value_min_max) { ASSERT_NO_THROW(validate_funcs[0]()); args[0].func("-3"); - ASSERT_THROW(validate_funcs[0](), std::out_of_range); + ASSERT_THROW(validate_funcs[0](), clapp::exception::out_of_range_t); args[0].func("-11"); - ASSERT_THROW(validate_funcs[0](), std::out_of_range); + ASSERT_THROW(validate_funcs[0](), clapp::exception::out_of_range_t); } TEST(argument, basic_argument_construct_mandatory_path_exists) { const std::string arg_name1{"arg"}; const std::string arg_desc1{"desc"}; - test_parser_t tp; - clapp::argument::basic_argument_t<clapp::fs::path> arg{tp, arg_name1, arg_desc1, test_parser_t::purpose_t::optional, clapp::value::path_exists_t{}}; - - test_parser_t::arguments_vector_t args{tp.get_arguments()}; - test_parser_t::argument_descriptions_vec_t descs{tp.get_optional_argument_descriptions()}; - test_parser_t::validate_func_vec_t validate_funcs{tp.get_validate_functions()}; + argument_test_parser_t tp; + clapp::argument::basic_argument_t<clapp::fs::path> arg{ + tp, arg_name1, arg_desc1, argument_test_parser_t::purpose_t::optional, + clapp::value::path_exists_t{}}; + + argument_test_parser_t::arguments_vector_t args{tp.get_arguments()}; + argument_test_parser_t::argument_descriptions_vec_t descs{ + tp.get_optional_argument_descriptions()}; + argument_test_parser_t::validate_func_vec_t validate_funcs{ + tp.get_validate_functions()}; ASSERT_THAT(validate_funcs.size(), testing::Eq(1)); ASSERT_NO_THROW(validate_funcs[0]()); ASSERT_THAT(descs.size(), testing::Eq(1)); ASSERT_THAT(descs[0].argument_string, testing::StrEq(arg_name1)); - ASSERT_THAT(descs[0].description, testing::StrEq(arg_desc1 + " (" + clapp::value::path_exists_t{}.append_restriction_text() + ")")); - ASSERT_THAT(descs[0].argument_type, testing::Eq(test_parser_t::argument_type_t::single)); + ASSERT_THAT( + descs[0].description, + testing::StrEq(arg_desc1 + " (" + + clapp::value::path_exists_t{}.append_restriction_text() + + ")")); + ASSERT_THAT(descs[0].argument_type, + testing::Eq(argument_test_parser_t::argument_type_t::single)); ASSERT_THAT(args.size(), testing::Eq(1)); - ASSERT_THAT(args[0].argument,testing::StrEq(arg_name1)); - ASSERT_THAT(args[0].argument_type, testing::Eq(test_parser_t::argument_type_t::single)); + ASSERT_THAT(args[0].argument, testing::StrEq(arg_name1)); + ASSERT_THAT(args[0].argument_type, + testing::Eq(argument_test_parser_t::argument_type_t::single)); ASSERT_THAT(static_cast<bool>(arg), testing::Eq(false)); ASSERT_THAT(arg.given(), testing::Eq(false)); - ASSERT_THROW(arg.value(), std::bad_optional_access); + ASSERT_THROW(arg.value(), clapp::exception::value_undefined_t); args[0].func("/tmp/xxx/yyy/zz"); ASSERT_THAT(static_cast<bool>(arg), testing::Eq(true)); ASSERT_THAT(arg.given(), testing::Eq(true)); ASSERT_THAT(arg.value().string(), testing::StrEq("/tmp/xxx/yyy/zz")); - ASSERT_THROW(validate_funcs[0](), std::runtime_error); + ASSERT_THROW(validate_funcs[0](), clapp::exception::path_does_not_exist_t); args[0].func("/tmp"); ASSERT_THAT(static_cast<bool>(arg), testing::Eq(true)); @@ -249,25 +389,63 @@ TEST(argument, basic_argument_construct_mandatory_path_exists) { ASSERT_NO_THROW(validate_funcs[0]()); } +TEST(argument, basic_variadic_argument_found_func_long) { + const std::string arg_name{"arg"}; + const std::string arg_desc{"description"}; + + std::stringstream ss; + argument_test_parser_t tp; + clapp::argument::basic_variadic_argument_t<std::string> arg{ + tp, arg_name, arg_desc, + clapp::value::found_func_t{[&ss]() { ss << "this is a test"; }}}; + argument_test_parser_t::arguments_vector_t args{tp.get_arguments()}; + argument_test_parser_t::argument_descriptions_vec_t descs{ + tp.get_mandatory_argument_descriptions()}; + + ASSERT_THAT(args.size(), testing::Eq(1)); + ASSERT_THAT(args[0].argument, testing::StrEq(arg_name)); + ASSERT_THAT(args[0].argument_type, + testing::Eq(argument_test_parser_t::argument_type_t::variadic)); + + args[0].func("argument-value"); + args[0].func("argument-value 2"); + + ASSERT_THAT(static_cast<bool>(arg), testing::Eq(true)); + ASSERT_THAT(arg.given(), testing::Eq(true)); + ASSERT_THAT(arg.value(), testing::Eq(std::vector<std::string>{ + "argument-value", "argument-value 2"})); + ASSERT_THAT(ss.str(), testing::StrEq("this is a testthis is a test")); +} + TEST(argument, basic_variadic_argument_construct_simple) { const std::string arg_name{"argument name"}; const std::string arg_desc{"desc"}; - test_parser_t tp; - clapp::argument::basic_variadic_argument_t<std::int32_t> arg{tp, arg_name, arg_desc}; - test_parser_t::arguments_vector_t args{tp.get_arguments()}; - test_parser_t::argument_descriptions_vec_t descs{tp.get_mandatory_argument_descriptions()}; - test_parser_t::validate_func_vec_t validate_funcs{tp.get_validate_functions()}; + argument_test_parser_t tp; + clapp::argument::basic_variadic_argument_t<std::int32_t> arg{tp, arg_name, + arg_desc}; + argument_test_parser_t::arguments_vector_t args{tp.get_arguments()}; + argument_test_parser_t::argument_descriptions_vec_t descs{ + tp.get_mandatory_argument_descriptions()}; + argument_test_parser_t::validate_func_vec_t validate_funcs{ + tp.get_validate_functions()}; ASSERT_THAT(validate_funcs.size(), testing::Eq(1)); - ASSERT_THROW(validate_funcs[0](), std::runtime_error); + ASSERT_THROW(validate_funcs[0](), clapp::exception::argument_exception_t); ASSERT_THAT(descs.size(), testing::Eq(1)); ASSERT_THAT(descs[0].argument_string, testing::StrEq(arg_name)); - ASSERT_THAT(descs[0].description, testing::StrEq(arg_desc + " (" + clapp::basic_variadic_argument_t<std::int32_t>::variadic_argument_restrictions() + ")")); - ASSERT_THAT(descs[0].argument_type, testing::Eq(test_parser_t::argument_type_t::variadic)); + ASSERT_THAT( + descs[0].description, + testing::StrEq(arg_desc + " (" + + clapp::basic_variadic_argument_t< + std::int32_t>::variadic_argument_restrictions() + + ")")); + ASSERT_THAT(descs[0].argument_type, + testing::Eq(argument_test_parser_t::argument_type_t::variadic)); ASSERT_THAT(args.size(), testing::Eq(1)); - ASSERT_THAT(args[0].argument,testing::StrEq(arg_name)); - ASSERT_THAT(args[0].argument_type, testing::Eq(test_parser_t::argument_type_t::variadic)); + ASSERT_THAT(args[0].argument, testing::StrEq(arg_name)); + ASSERT_THAT(args[0].argument_type, + testing::Eq(argument_test_parser_t::argument_type_t::variadic)); ASSERT_THAT(static_cast<bool>(arg), testing::Eq(false)); ASSERT_THAT(arg.given(), testing::Eq(false)); @@ -283,7 +461,8 @@ TEST(argument, basic_variadic_argument_construct_simple) { args[0].func("1234"); ASSERT_THAT(static_cast<bool>(arg), testing::Eq(true)); ASSERT_THAT(arg.given(), testing::Eq(true)); - ASSERT_THAT(arg.value(), testing::Eq(std::vector<std::int32_t>{12345, 1234})); + ASSERT_THAT(arg.value(), + testing::Eq(std::vector<std::int32_t>{12345, 1234})); ASSERT_NO_THROW(validate_funcs[0]()); } @@ -291,22 +470,31 @@ TEST(argument, basic_variadic_argument_construct_simple) { TEST(argument, basic_variadic_argument_construct_simple_optional) { const std::string arg_name{"argument name"}; const std::string arg_desc{"desc"}; - test_parser_t tp; - clapp::argument::basic_variadic_argument_t<std::string> arg{tp, arg_name, arg_desc, - test_parser_t::purpose_t::optional}; - test_parser_t::arguments_vector_t args{tp.get_arguments()}; - test_parser_t::argument_descriptions_vec_t descs{tp.get_optional_argument_descriptions()}; - test_parser_t::validate_func_vec_t validate_funcs{tp.get_validate_functions()}; + argument_test_parser_t tp; + clapp::argument::basic_variadic_argument_t<std::string> arg{ + tp, arg_name, arg_desc, argument_test_parser_t::purpose_t::optional}; + argument_test_parser_t::arguments_vector_t args{tp.get_arguments()}; + argument_test_parser_t::argument_descriptions_vec_t descs{ + tp.get_optional_argument_descriptions()}; + argument_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].argument_string, testing::StrEq(arg_name)); - ASSERT_THAT(descs[0].description, testing::StrEq(arg_desc + " (" + clapp::basic_variadic_argument_t<std::int32_t>::variadic_argument_restrictions() + ")")); - ASSERT_THAT(descs[0].argument_type, testing::Eq(test_parser_t::argument_type_t::variadic)); + ASSERT_THAT( + descs[0].description, + testing::StrEq(arg_desc + " (" + + clapp::basic_variadic_argument_t< + std::int32_t>::variadic_argument_restrictions() + + ")")); + ASSERT_THAT(descs[0].argument_type, + testing::Eq(argument_test_parser_t::argument_type_t::variadic)); ASSERT_THAT(args.size(), testing::Eq(1)); - ASSERT_THAT(args[0].argument,testing::StrEq(arg_name)); - ASSERT_THAT(args[0].argument_type, testing::Eq(test_parser_t::argument_type_t::variadic)); + ASSERT_THAT(args[0].argument, testing::StrEq(arg_name)); + ASSERT_THAT(args[0].argument_type, + testing::Eq(argument_test_parser_t::argument_type_t::variadic)); ASSERT_THAT(static_cast<bool>(arg), testing::Eq(false)); ASSERT_THAT(arg.given(), testing::Eq(false)); @@ -320,31 +508,42 @@ TEST(argument, basic_variadic_argument_construct_simple_optional) { args[0].func("aba"); ASSERT_THAT(static_cast<bool>(arg), testing::Eq(true)); ASSERT_THAT(arg.given(), testing::Eq(true)); - ASSERT_THAT(arg.value(), testing::Eq(std::vector<std::string>{"ccc", "aba"})); + ASSERT_THAT(arg.value(), + testing::Eq(std::vector<std::string>{"ccc", "aba"})); } TEST(argument, basic_variadic_argument_construct_simple_optional_min_max) { 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}; - test_parser_t tp; - clapp::argument::basic_variadic_argument_t<std::int64_t> arg{tp, arg_name, arg_desc, min_max, - test_parser_t::purpose_t::optional}; - test_parser_t::arguments_vector_t args{tp.get_arguments()}; - test_parser_t::argument_descriptions_vec_t descs{tp.get_optional_argument_descriptions()}; - test_parser_t::validate_func_vec_t validate_funcs{tp.get_validate_functions()}; + argument_test_parser_t tp; + clapp::argument::basic_variadic_argument_t<std::int64_t> arg{ + tp, arg_name, arg_desc, min_max, + argument_test_parser_t::purpose_t::optional}; + argument_test_parser_t::arguments_vector_t args{tp.get_arguments()}; + argument_test_parser_t::argument_descriptions_vec_t descs{ + tp.get_optional_argument_descriptions()}; + argument_test_parser_t::validate_func_vec_t validate_funcs{ + tp.get_validate_functions()}; ASSERT_THAT(validate_funcs.size(), testing::Eq(1)); ASSERT_NO_THROW(validate_funcs[0]()); ASSERT_THAT(descs.size(), testing::Eq(1)); ASSERT_THAT(descs[0].argument_string, testing::StrEq(arg_name)); - ASSERT_THAT(descs[0].description, testing::StrEq(arg_desc + " (" + clapp::basic_variadic_argument_t<std::int64_t>::variadic_argument_restrictions() + ", " + min_max.append_restriction_text() + ")")); - ASSERT_THAT(descs[0].argument_type, testing::Eq(test_parser_t::argument_type_t::variadic)); + ASSERT_THAT( + descs[0].description, + testing::StrEq(arg_desc + " (" + + clapp::basic_variadic_argument_t< + std::int64_t>::variadic_argument_restrictions() + + ", " + min_max.append_restriction_text() + ")")); + ASSERT_THAT(descs[0].argument_type, + testing::Eq(argument_test_parser_t::argument_type_t::variadic)); ASSERT_THAT(args.size(), testing::Eq(1)); - ASSERT_THAT(args[0].argument,testing::StrEq(arg_name)); - ASSERT_THAT(args[0].argument_type, testing::Eq(test_parser_t::argument_type_t::variadic)); + ASSERT_THAT(args[0].argument, testing::StrEq(arg_name)); + ASSERT_THAT(args[0].argument_type, + testing::Eq(argument_test_parser_t::argument_type_t::variadic)); ASSERT_THAT(static_cast<bool>(arg), testing::Eq(false)); ASSERT_THAT(arg.given(), testing::Eq(false)); @@ -367,13 +566,15 @@ TEST(argument, basic_variadic_argument_construct_simple_optional_min_max) { ASSERT_THAT(arg.given(), testing::Eq(true)); ASSERT_THAT(arg.value(), testing::Eq(std::vector<std::int64_t>{8, 6, 12})); - ASSERT_THROW(validate_funcs[0](), std::out_of_range); + ASSERT_THROW(validate_funcs[0](), clapp::exception::out_of_range_t); } TEST(argument, basic_variadic_argument_construct_simple_default_value_throws) { 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}; - test_parser_t tp; - ASSERT_THROW((clapp::argument::basic_variadic_argument_t<std::int64_t>{tp, arg_name, arg_desc, default_value}), std::runtime_error); + argument_test_parser_t tp; + ASSERT_THROW((clapp::argument::basic_variadic_argument_t<std::int64_t>{ + tp, arg_name, arg_desc, default_value}), + clapp::exception::argument_exception_t); } diff --git a/tests/clapp.cpp b/tests/clapp.cpp deleted file mode 100644 index 84862be4..00000000 --- a/tests/clapp.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include <gmock/gmock.h> -#include <cassert> -#include <iterator> -#include <map> -#include <utility> - -TEST(DummyTest, Desc) { - ASSERT_THAT(true, testing::Eq(true)); -} diff --git a/tests/exception.cpp b/tests/exception.cpp new file mode 100644 index 00000000..9499696a --- /dev/null +++ b/tests/exception.cpp @@ -0,0 +1,172 @@ +#include <clapp/exception.h> +#include <gmock/gmock.h> + +TEST(exception, clapp_exception_t) { + const std::string msg{"message"}; + const std::string msg2{"message2"}; + + clapp::exception::clapp_exception_t ce1{msg.c_str()}; + clapp::exception::clapp_exception_t ce2{msg2}; + + clapp::exception::clapp_exception_t ce3{ce1}; + clapp::exception::clapp_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, invalid_value_t) { + const std::string msg{"message"}; + const std::string msg2{"message2"}; + + clapp::exception::invalid_value_t ce1{msg.c_str()}; + clapp::exception::invalid_value_t ce2{msg2}; + + clapp::exception::invalid_value_t ce3{ce1}; + clapp::exception::invalid_value_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, value_undefined_t) { + const std::string msg{"message"}; + const std::string msg2{"message2"}; + + clapp::exception::value_undefined_t ce1{msg.c_str()}; + clapp::exception::value_undefined_t ce2{msg2}; + + clapp::exception::value_undefined_t ce3{ce1}; + clapp::exception::value_undefined_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, out_of_range_t) { + const std::string msg{"message"}; + const std::string msg2{"message2"}; + + clapp::exception::out_of_range_t ce1{msg.c_str()}; + clapp::exception::out_of_range_t ce2{msg2}; + + clapp::exception::out_of_range_t ce3{ce1}; + clapp::exception::out_of_range_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, path_does_not_exist_t) { + const std::string msg{"message"}; + const std::string msg2{"message2"}; + + clapp::exception::path_does_not_exist_t ce1{msg.c_str()}; + clapp::exception::path_does_not_exist_t ce2{msg2}; + + clapp::exception::path_does_not_exist_t ce3{ce1}; + clapp::exception::path_does_not_exist_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, option_exception_t) { + const std::string msg{"message"}; + const std::string msg2{"message2"}; + + clapp::exception::option_exception_t ce1{msg.c_str()}; + clapp::exception::option_exception_t ce2{msg2}; + + clapp::exception::option_exception_t ce3{ce1}; + clapp::exception::option_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, option_param_exception_t) { + const std::string msg{"message"}; + const std::string msg2{"message2"}; + + clapp::exception::option_param_exception_t ce1{msg.c_str()}; + clapp::exception::option_param_exception_t ce2{msg2}; + + clapp::exception::option_param_exception_t ce3{ce1}; + clapp::exception::option_param_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, argument_exception_t) { + const std::string msg{"message"}; + const std::string msg2{"message2"}; + + clapp::exception::argument_exception_t ce1{msg.c_str()}; + clapp::exception::argument_exception_t ce2{msg2}; + + clapp::exception::argument_exception_t ce3{ce1}; + clapp::exception::argument_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, parser_exception_t) { + const std::string msg{"message"}; + const std::string msg2{"message2"}; + + clapp::exception::parser_exception_t ce1{msg.c_str()}; + clapp::exception::parser_exception_t ce2{msg2}; + + clapp::exception::parser_exception_t ce3{ce1}; + clapp::exception::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, sub_parser_exception_t) { + const std::string msg{"message"}; + const std::string msg2{"message2"}; + + clapp::exception::sub_parser_exception_t ce1{msg.c_str()}; + clapp::exception::sub_parser_exception_t ce2{msg2}; + + clapp::exception::sub_parser_exception_t ce3{ce1}; + clapp::exception::sub_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)); +} diff --git a/tests/main_parser.cpp b/tests/main_parser.cpp index 8af92d6a..a210b283 100644 --- a/tests/main_parser.cpp +++ b/tests/main_parser.cpp @@ -4,13 +4,32 @@ #include <gmock/gmock.h> class empty_main_parser_t : public clapp::parser::basic_main_parser_t { + public: + ~empty_main_parser_t() override; }; +empty_main_parser_t::~empty_main_parser_t() = default; + TEST(main_parser, construct_main_parser) { constexpr const char* const argv[]{"main", nullptr}; empty_main_parser_t tp; - tp.parse(1, argv); + 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")); } + +TEST(parser, construct_main_parser_and_gen_help_prefix) { + constexpr const char* const argv[]{"main", nullptr}; + empty_main_parser_t tp; + + tp.parse(1, static_cast<const char* const*>(argv)); + ASSERT_THAT(tp.gen_help_prefix(), testing::StrEq("Usage: \nmain")); +} + +TEST(parser, construct_main_parser_parse_and_validate) { + constexpr const char* const argv[]{"main", nullptr}; + empty_main_parser_t tp; + + tp.parse_and_validate(1, static_cast<const char* const*>(argv)); +} diff --git a/tests/option.cpp b/tests/option.cpp index b5bed478..62a305f9 100644 --- a/tests/option.cpp +++ b/tests/option.cpp @@ -1,8 +1,9 @@ #include <clapp/option.h> #include <gmock/gmock.h> -class test_parser_t : public clapp::parser::basic_parser_t { +class option_test_parser_t : public clapp::parser::basic_parser_t { public: + ~option_test_parser_t() override; using clapp::parser::basic_parser_t::purpose_t; using clapp::parser::basic_parser_t::option_type_t; @@ -27,18 +28,91 @@ class test_parser_t : public clapp::parser::basic_parser_t { using clapp::parser::basic_parser_t::get_validate_functions; }; +option_test_parser_t::~option_test_parser_t() = default; + +class test_option_t : public clapp::option::basic_option_t<std::int32_t> { + public: + template <typename... Params> + explicit test_option_t(clapp::parser::basic_parser_t& parser, + Params... parameters); + ~test_option_t() override; + + inline explicit operator bool() const; + + private: + void found_entry(); + static callbacks_t create_callbacks(test_option_t* inst); +}; + +template <typename... Params> +test_option_t::test_option_t(clapp::basic_parser_t& parser, + Params... parameters) + : clapp::basic_option_t<std::int32_t>{parser, create_callbacks(this), + std::forward<Params>(parameters)...} { +} + +void test_option_t::found_entry() { + _given = true; + _value = _value.value() + 1; + for (auto& found_func : _found) { + found_func.found(); + } +} + +inline test_option_t::operator bool() const { return _value.value() != 0; } + +test_option_t::~test_option_t() = default; + +test_option_t::callbacks_t test_option_t::create_callbacks( + test_option_t* inst) { + return callbacks_t{ + [inst](const std::string_view /*option*/) { inst->found_entry(); }, + [inst](const char /*option*/) { inst->found_entry(); }, + [inst]() { return inst->given(); }, + [inst]() { return static_cast<bool>(*inst); }, + [inst]() { return inst->value(); }}; +} + +TEST(option, basic_option_construct_value_throws) { + const std::string long_opt{"long"}; + const std::string opt_desc{"description"}; + option_test_parser_t tp; + test_option_t opt{tp, long_opt, opt_desc}; + ASSERT_THROW(opt.value(), clapp::exception::value_undefined_t); +} + +TEST(option, bool_option_construct_long_option_twice_throws) { + const std::string long_opt{"long"}; + const std::string opt_desc{"description"}; + option_test_parser_t tp; + clapp::option::bool_option_t opt{tp, long_opt, opt_desc}; + ASSERT_THROW((clapp::option::bool_option_t{tp, long_opt, opt_desc}), + clapp::exception::option_exception_t); +} + +TEST(option, bool_option_construct_short_option_twice_throws) { + const char short_opt{'s'}; + const std::string opt_desc{"description"}; + option_test_parser_t tp; + clapp::option::bool_option_t opt{tp, short_opt, opt_desc}; + ASSERT_THROW((clapp::option::bool_option_t{tp, short_opt, opt_desc}), + clapp::exception::option_exception_t); +} + TEST(option, bool_option_construct_long) { const std::string long_opt_name{"long"}; const std::string opt_desc{"description"}; - test_parser_t tp; + option_test_parser_t tp; clapp::option::bool_option_t opt{tp, long_opt_name, opt_desc}; - test_parser_t::long_options_map_t long_options{tp.get_long_options()}; - test_parser_t::short_options_map_t short_options{tp.get_short_options()}; + 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)); - test_parser_t::option_descriptions_vec_t descs{ + option_test_parser_t::option_descriptions_vec_t descs{ tp.get_optional_option_descriptions()}; - test_parser_t::validate_func_vec_t validate_funcs{ + option_test_parser_t::validate_func_vec_t validate_funcs{ tp.get_validate_functions()}; ASSERT_THAT(validate_funcs.size(), testing::Eq(0)); @@ -47,18 +121,18 @@ TEST(option, bool_option_construct_long) { testing::StrEq(std::string{"--"} + long_opt_name)); ASSERT_THAT(descs[0].description, testing::StrEq(opt_desc)); ASSERT_THAT(descs[0].option_type, - testing::Eq(test_parser_t::option_type_t::scalar)); + 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)); - test_parser_t::long_opt_variant_t long_opt_func_variant{ + option_test_parser_t::long_opt_variant_t long_opt_func_variant{ long_options.find(long_opt_name)->second}; - ASSERT_THAT(std::holds_alternative<test_parser_t::long_opt_func_t>( + ASSERT_THAT(std::holds_alternative<option_test_parser_t::long_opt_func_t>( long_opt_func_variant), testing::Eq(true)); - test_parser_t::long_opt_func_t long_opt_func{ - std::get<test_parser_t::long_opt_func_t>(long_opt_func_variant)}; + 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(opt.given(), testing::Eq(false)); @@ -74,14 +148,16 @@ TEST(option, bool_option_construct_short) { const char short_opt_name{'o'}; const std::string opt_desc{"description"}; - test_parser_t tp; + option_test_parser_t tp; clapp::option::bool_option_t opt{tp, short_opt_name, opt_desc}; - test_parser_t::long_options_map_t long_options{tp.get_long_options()}; - test_parser_t::short_options_map_t short_options{tp.get_short_options()}; + 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)); - test_parser_t::option_descriptions_vec_t descs{ + option_test_parser_t::option_descriptions_vec_t descs{ tp.get_optional_option_descriptions()}; - test_parser_t::validate_func_vec_t validate_funcs{ + option_test_parser_t::validate_func_vec_t validate_funcs{ tp.get_validate_functions()}; ASSERT_THAT(validate_funcs.size(), testing::Eq(0)); @@ -90,18 +166,19 @@ TEST(option, bool_option_construct_short) { testing::StrEq(std::string{"-"} + short_opt_name)); ASSERT_THAT(descs[0].description, testing::StrEq(opt_desc)); ASSERT_THAT(descs[0].option_type, - testing::Eq(test_parser_t::option_type_t::scalar)); + testing::Eq(option_test_parser_t::option_type_t::scalar)); ASSERT_THAT(long_options.size(), testing::Eq(0)); ASSERT_THAT(short_options.size(), testing::Eq(1)); ASSERT_THAT(short_options.count(short_opt_name), testing::Eq(1)); - test_parser_t::short_opt_variant_t short_opt_func_variant{ + option_test_parser_t::short_opt_variant_t short_opt_func_variant{ short_options.find(short_opt_name)->second}; - ASSERT_THAT(std::holds_alternative<test_parser_t::short_opt_func_t>( + ASSERT_THAT(std::holds_alternative<option_test_parser_t::short_opt_func_t>( short_opt_func_variant), testing::Eq(true)); - test_parser_t::short_opt_func_t short_opt_func{ - std::get<test_parser_t::short_opt_func_t>(short_opt_func_variant)}; + option_test_parser_t::short_opt_func_t short_opt_func{ + 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(opt.given(), testing::Eq(false)); @@ -122,18 +199,20 @@ TEST(option, bool_option_construct_long_and_short) { const char short_opt_name2{'n'}; const std::string opt_desc2{"description2"}; - test_parser_t tp; + 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, long_opt_name2, short_opt_name2, opt_desc2}; - test_parser_t::long_options_map_t long_options{tp.get_long_options()}; - test_parser_t::short_options_map_t short_options{tp.get_short_options()}; + 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)); - test_parser_t::option_descriptions_vec_t descs{ + option_test_parser_t::option_descriptions_vec_t descs{ tp.get_optional_option_descriptions()}; - test_parser_t::validate_func_vec_t validate_funcs{ + option_test_parser_t::validate_func_vec_t validate_funcs{ tp.get_validate_functions()}; ASSERT_THAT(validate_funcs.size(), testing::Eq(0)); @@ -143,49 +222,53 @@ TEST(option, bool_option_construct_long_and_short) { long_opt_name1)); ASSERT_THAT(descs[0].description, testing::StrEq(opt_desc1)); ASSERT_THAT(descs[0].option_type, - testing::Eq(test_parser_t::option_type_t::scalar)); + 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(test_parser_t::option_type_t::scalar)); + 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)); - test_parser_t::long_opt_variant_t long_opt_func_variant1{ + option_test_parser_t::long_opt_variant_t long_opt_func_variant1{ long_options.find(long_opt_name1)->second}; - test_parser_t::long_opt_variant_t long_opt_func_variant2{ + option_test_parser_t::long_opt_variant_t long_opt_func_variant2{ long_options.find(long_opt_name2)->second}; - ASSERT_THAT(std::holds_alternative<test_parser_t::long_opt_func_t>( + 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<test_parser_t::long_opt_func_t>( + ASSERT_THAT(std::holds_alternative<option_test_parser_t::long_opt_func_t>( long_opt_func_variant2), testing::Eq(true)); - test_parser_t::long_opt_func_t long_opt_func1{ - std::get<test_parser_t::long_opt_func_t>(long_opt_func_variant1)}; - test_parser_t::long_opt_func_t long_opt_func2{ - std::get<test_parser_t::long_opt_func_t>(long_opt_func_variant2)}; + 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)); - test_parser_t::short_opt_variant_t short_opt_func_variant1{ + option_test_parser_t::short_opt_variant_t short_opt_func_variant1{ short_options.find(short_opt_name1)->second}; - test_parser_t::short_opt_variant_t short_opt_func_variant2{ + option_test_parser_t::short_opt_variant_t short_opt_func_variant2{ short_options.find(short_opt_name2)->second}; - ASSERT_THAT(std::holds_alternative<test_parser_t::short_opt_func_t>( + 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<test_parser_t::short_opt_func_t>( + ASSERT_THAT(std::holds_alternative<option_test_parser_t::short_opt_func_t>( short_opt_func_variant2), testing::Eq(true)); - test_parser_t::short_opt_func_t short_opt_func1{ - std::get<test_parser_t::short_opt_func_t>(short_opt_func_variant1)}; - test_parser_t::short_opt_func_t short_opt_func2{ - std::get<test_parser_t::short_opt_func_t>(short_opt_func_variant2)}; + 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(false)); ASSERT_THAT(opt1.given(), testing::Eq(false)); @@ -206,19 +289,50 @@ TEST(option, bool_option_construct_long_and_short) { ASSERT_THAT(opt2.value(), testing::Eq(true)); } +TEST(option, bool_option_found_func_long) { + const std::string long_opt_name{"long"}; + const std::string opt_desc{"description"}; + + std::stringstream ss; + + option_test_parser_t tp; + clapp::option::bool_option_t opt{ + tp, long_opt_name, opt_desc, option_test_parser_t::purpose_t::mandatory, + clapp::value::found_func_t{[&ss]() { ss << "this is a test"; }}}; + option_test_parser_t::short_options_map_t short_options{ + tp.get_short_options()}; + option_test_parser_t::long_options_map_t long_options{ + tp.get_long_options()}; + 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)}; + long_opt_func(long_opt_name); + ASSERT_THAT(ss.str(), testing::StrEq("this is a test")); +} + TEST(option, bool_option_construct_long_mandatory) { const std::string long_opt_name{"long"}; const std::string opt_desc{"description"}; - test_parser_t tp; - clapp::option::bool_option_t opt{tp, long_opt_name, opt_desc, - test_parser_t::purpose_t::mandatory}; - test_parser_t::long_options_map_t long_options{tp.get_long_options()}; - test_parser_t::short_options_map_t short_options{tp.get_short_options()}; + option_test_parser_t tp; + clapp::option::bool_option_t opt{ + tp, long_opt_name, opt_desc, + option_test_parser_t::purpose_t::mandatory}; + 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_optional_option_descriptions().size(), testing::Eq(0)); - test_parser_t::option_descriptions_vec_t descs{ + option_test_parser_t::option_descriptions_vec_t descs{ tp.get_mandatory_option_descriptions()}; - test_parser_t::validate_func_vec_t validate_funcs{ + option_test_parser_t::validate_func_vec_t validate_funcs{ tp.get_validate_functions()}; ASSERT_THAT(validate_funcs.size(), testing::Eq(1)); @@ -227,23 +341,24 @@ TEST(option, bool_option_construct_long_mandatory) { testing::StrEq(std::string{"--"} + long_opt_name)); ASSERT_THAT(descs[0].description, testing::StrEq(opt_desc)); ASSERT_THAT(descs[0].option_type, - testing::Eq(test_parser_t::option_type_t::scalar)); + 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)); - test_parser_t::long_opt_variant_t long_opt_func_variant{ + option_test_parser_t::long_opt_variant_t long_opt_func_variant{ long_options.find(long_opt_name)->second}; - ASSERT_THAT(std::holds_alternative<test_parser_t::long_opt_func_t>( + ASSERT_THAT(std::holds_alternative<option_test_parser_t::long_opt_func_t>( long_opt_func_variant), testing::Eq(true)); - test_parser_t::long_opt_func_t long_opt_func{ - std::get<test_parser_t::long_opt_func_t>(long_opt_func_variant)}; + 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(opt.given(), testing::Eq(false)); ASSERT_THAT(opt.value(), testing::Eq(false)); - ASSERT_THROW(validate_funcs[0](), std::exception); + ASSERT_THROW(validate_funcs[0](), + clapp::exception::option_param_exception_t); long_opt_func(long_opt_name); ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true)); @@ -252,18 +367,85 @@ TEST(option, bool_option_construct_long_mandatory) { ASSERT_NO_THROW(validate_funcs[0]()); } +TEST(option, help_option_construct_long) { + const std::string long_opt_name{"long"}; + const char short_opt_name{'s'}; + const std::string opt_desc{"description"}; + + option_test_parser_t tp; + clapp::option::help_option_t opt{tp, long_opt_name, short_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()}; + ASSERT_THAT(tp.get_validate_functions().size(), testing::Eq(0)); + + ASSERT_THAT(descs.size(), testing::Eq(1)); + ASSERT_THAT(descs[0].option_string, + testing::StrEq(std::string{"-"} + short_opt_name + "|--" + + 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(1)); + ASSERT_THAT(short_options.count(short_opt_name), testing::Eq(1)); + option_test_parser_t::short_opt_variant_t short_opt_func_variant{ + short_options.find(short_opt_name)->second}; + 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(std::holds_alternative<option_test_parser_t::short_opt_func_t>( + short_opt_func_variant), + testing::Eq(true)); + option_test_parser_t::short_opt_func_t short_opt_func{ + 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(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")); + + 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")); +} + TEST(option, count_option_construct_long) { const std::string long_opt_name{"long"}; const std::string opt_desc{"description 2"}; - test_parser_t tp; + option_test_parser_t tp; clapp::option::count_option_t opt{tp, long_opt_name, opt_desc}; - test_parser_t::long_options_map_t long_options{tp.get_long_options()}; - test_parser_t::short_options_map_t short_options{tp.get_short_options()}; + 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)); - test_parser_t::option_descriptions_vec_t descs{ + option_test_parser_t::option_descriptions_vec_t descs{ tp.get_optional_option_descriptions()}; - test_parser_t::validate_func_vec_t validate_funcs{ + option_test_parser_t::validate_func_vec_t validate_funcs{ tp.get_validate_functions()}; ASSERT_THAT(validate_funcs.size(), testing::Eq(0)); @@ -272,18 +454,18 @@ TEST(option, count_option_construct_long) { testing::StrEq(std::string{"--"} + long_opt_name)); ASSERT_THAT(descs[0].description, testing::StrEq(opt_desc)); ASSERT_THAT(descs[0].option_type, - testing::Eq(test_parser_t::option_type_t::scalar)); + 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)); - test_parser_t::long_opt_variant_t long_opt_func_variant{ + option_test_parser_t::long_opt_variant_t long_opt_func_variant{ long_options.find(long_opt_name)->second}; - ASSERT_THAT(std::holds_alternative<test_parser_t::long_opt_func_t>( + ASSERT_THAT(std::holds_alternative<option_test_parser_t::long_opt_func_t>( long_opt_func_variant), testing::Eq(true)); - test_parser_t::long_opt_func_t long_opt_func{ - std::get<test_parser_t::long_opt_func_t>(long_opt_func_variant)}; + 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(opt.given(), testing::Eq(false)); @@ -304,14 +486,16 @@ TEST(option, count_option_construct_short) { const char short_opt_name{'i'}; const std::string opt_desc{"description 3"}; - test_parser_t tp; + option_test_parser_t tp; clapp::option::count_option_t opt{tp, short_opt_name, opt_desc}; - test_parser_t::long_options_map_t long_options{tp.get_long_options()}; - test_parser_t::short_options_map_t short_options{tp.get_short_options()}; + 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)); - test_parser_t::option_descriptions_vec_t descs{ + option_test_parser_t::option_descriptions_vec_t descs{ tp.get_optional_option_descriptions()}; - test_parser_t::validate_func_vec_t validate_funcs{ + option_test_parser_t::validate_func_vec_t validate_funcs{ tp.get_validate_functions()}; ASSERT_THAT(validate_funcs.size(), testing::Eq(0)); @@ -320,18 +504,19 @@ TEST(option, count_option_construct_short) { testing::StrEq(std::string{"-"} + short_opt_name)); ASSERT_THAT(descs[0].description, testing::StrEq(opt_desc)); ASSERT_THAT(descs[0].option_type, - testing::Eq(test_parser_t::option_type_t::scalar)); + testing::Eq(option_test_parser_t::option_type_t::scalar)); ASSERT_THAT(long_options.size(), testing::Eq(0)); ASSERT_THAT(short_options.size(), testing::Eq(1)); ASSERT_THAT(short_options.count(short_opt_name), testing::Eq(1)); - test_parser_t::short_opt_variant_t short_opt_func_variant{ + option_test_parser_t::short_opt_variant_t short_opt_func_variant{ short_options.find(short_opt_name)->second}; - ASSERT_THAT(std::holds_alternative<test_parser_t::short_opt_func_t>( + ASSERT_THAT(std::holds_alternative<option_test_parser_t::short_opt_func_t>( short_opt_func_variant), testing::Eq(true)); - test_parser_t::short_opt_func_t short_opt_func{ - std::get<test_parser_t::short_opt_func_t>(short_opt_func_variant)}; + option_test_parser_t::short_opt_func_t short_opt_func{ + 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(opt.given(), testing::Eq(false)); @@ -354,18 +539,20 @@ TEST(option, count_option_construct_long_and_short) { const char short_opt_name2{'n'}; const std::string opt_desc2{"description2"}; - test_parser_t tp; + option_test_parser_t tp; clapp::option::count_option_t opt1{tp, long_opt_name1, short_opt_name1, opt_desc1}; clapp::option::count_option_t opt2{tp, long_opt_name2, short_opt_name2, opt_desc2}; - test_parser_t::long_options_map_t long_options{tp.get_long_options()}; - test_parser_t::short_options_map_t short_options{tp.get_short_options()}; + 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)); - test_parser_t::option_descriptions_vec_t descs{ + option_test_parser_t::option_descriptions_vec_t descs{ tp.get_optional_option_descriptions()}; - test_parser_t::validate_func_vec_t validate_funcs{ + option_test_parser_t::validate_func_vec_t validate_funcs{ tp.get_validate_functions()}; ASSERT_THAT(validate_funcs.size(), testing::Eq(0)); @@ -375,49 +562,53 @@ TEST(option, count_option_construct_long_and_short) { long_opt_name1)); ASSERT_THAT(descs[0].description, testing::StrEq(opt_desc1)); ASSERT_THAT(descs[0].option_type, - testing::Eq(test_parser_t::option_type_t::scalar)); + 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(test_parser_t::option_type_t::scalar)); + 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)); - test_parser_t::long_opt_variant_t long_opt_func_variant1{ + option_test_parser_t::long_opt_variant_t long_opt_func_variant1{ long_options.find(long_opt_name1)->second}; - test_parser_t::long_opt_variant_t long_opt_func_variant2{ + option_test_parser_t::long_opt_variant_t long_opt_func_variant2{ long_options.find(long_opt_name2)->second}; - ASSERT_THAT(std::holds_alternative<test_parser_t::long_opt_func_t>( + 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<test_parser_t::long_opt_func_t>( + ASSERT_THAT(std::holds_alternative<option_test_parser_t::long_opt_func_t>( long_opt_func_variant2), testing::Eq(true)); - test_parser_t::long_opt_func_t long_opt_func1{ - std::get<test_parser_t::long_opt_func_t>(long_opt_func_variant1)}; - test_parser_t::long_opt_func_t long_opt_func2{ - std::get<test_parser_t::long_opt_func_t>(long_opt_func_variant2)}; + 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)); - test_parser_t::short_opt_variant_t short_opt_func_variant1{ + option_test_parser_t::short_opt_variant_t short_opt_func_variant1{ short_options.find(short_opt_name1)->second}; - test_parser_t::short_opt_variant_t short_opt_func_variant2{ + option_test_parser_t::short_opt_variant_t short_opt_func_variant2{ short_options.find(short_opt_name2)->second}; - ASSERT_THAT(std::holds_alternative<test_parser_t::short_opt_func_t>( + 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<test_parser_t::short_opt_func_t>( + ASSERT_THAT(std::holds_alternative<option_test_parser_t::short_opt_func_t>( short_opt_func_variant2), testing::Eq(true)); - test_parser_t::short_opt_func_t short_opt_func1{ - std::get<test_parser_t::short_opt_func_t>(short_opt_func_variant1)}; - test_parser_t::short_opt_func_t short_opt_func2{ - std::get<test_parser_t::short_opt_func_t>(short_opt_func_variant2)}; + 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(false)); ASSERT_THAT(opt1.given(), testing::Eq(false)); @@ -446,15 +637,17 @@ TEST(option, count_option_construct_short_min_max) { const char short_opt_name{'i'}; const std::string opt_desc{"description 3"}; - test_parser_t tp; - clapp::value::min_max_value_t min_max{0, 5}; + option_test_parser_t tp; + clapp::value::min_max_value_t<std::uint32_t> min_max{0, 5}; clapp::option::count_option_t opt{tp, short_opt_name, opt_desc, min_max}; - test_parser_t::long_options_map_t long_options{tp.get_long_options()}; - test_parser_t::short_options_map_t short_options{tp.get_short_options()}; + 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)); - test_parser_t::option_descriptions_vec_t descs{ + option_test_parser_t::option_descriptions_vec_t descs{ tp.get_optional_option_descriptions()}; - test_parser_t::validate_func_vec_t validate_funcs{ + option_test_parser_t::validate_func_vec_t validate_funcs{ tp.get_validate_functions()}; ASSERT_THAT(validate_funcs.size(), testing::Eq(1)); @@ -465,18 +658,19 @@ TEST(option, count_option_construct_short_min_max) { testing::StrEq(opt_desc + " (" + min_max.append_restriction_text() + ")")); ASSERT_THAT(descs[0].option_type, - testing::Eq(test_parser_t::option_type_t::scalar)); + testing::Eq(option_test_parser_t::option_type_t::scalar)); ASSERT_THAT(long_options.size(), testing::Eq(0)); ASSERT_THAT(short_options.size(), testing::Eq(1)); ASSERT_THAT(short_options.count(short_opt_name), testing::Eq(1)); - test_parser_t::short_opt_variant_t short_opt_func_variant{ + option_test_parser_t::short_opt_variant_t short_opt_func_variant{ short_options.find(short_opt_name)->second}; - ASSERT_THAT(std::holds_alternative<test_parser_t::short_opt_func_t>( + ASSERT_THAT(std::holds_alternative<option_test_parser_t::short_opt_func_t>( short_opt_func_variant), testing::Eq(true)); - test_parser_t::short_opt_func_t short_opt_func{ - std::get<test_parser_t::short_opt_func_t>(short_opt_func_variant)}; + option_test_parser_t::short_opt_func_t short_opt_func{ + 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(opt.given(), testing::Eq(false)); @@ -502,23 +696,25 @@ TEST(option, count_option_construct_short_min_max) { ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true)); ASSERT_THAT(opt.given(), testing::Eq(true)); ASSERT_THAT(opt.value(), testing::Eq(6)); - ASSERT_THROW(validate_funcs[0](), std::exception); + ASSERT_THROW(validate_funcs[0](), clapp::exception::out_of_range_t); } TEST(option, count_option_construct_long_default) { const std::string long_opt_name{"long"}; const std::string opt_desc{"desc"}; - test_parser_t tp; - clapp::value::default_value_t default_value{4}; + option_test_parser_t tp; + clapp::value::default_value_t<std::uint32_t> default_value{4}; clapp::option::count_option_t opt{tp, long_opt_name, opt_desc, default_value}; - test_parser_t::long_options_map_t long_options{tp.get_long_options()}; - test_parser_t::short_options_map_t short_options{tp.get_short_options()}; + 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)); - test_parser_t::option_descriptions_vec_t descs{ + option_test_parser_t::option_descriptions_vec_t descs{ tp.get_optional_option_descriptions()}; - test_parser_t::validate_func_vec_t validate_funcs{ + option_test_parser_t::validate_func_vec_t validate_funcs{ tp.get_validate_functions()}; ASSERT_THAT(validate_funcs.size(), testing::Eq(0)); @@ -529,18 +725,18 @@ TEST(option, count_option_construct_long_default) { testing::StrEq(opt_desc + " (" + default_value.append_restriction_text() + ")")); ASSERT_THAT(descs[0].option_type, - testing::Eq(test_parser_t::option_type_t::scalar)); + 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)); - test_parser_t::long_opt_variant_t long_opt_func_variant{ + option_test_parser_t::long_opt_variant_t long_opt_func_variant{ long_options.find(long_opt_name)->second}; - ASSERT_THAT(std::holds_alternative<test_parser_t::long_opt_func_t>( + ASSERT_THAT(std::holds_alternative<option_test_parser_t::long_opt_func_t>( long_opt_func_variant), testing::Eq(true)); - test_parser_t::long_opt_func_t long_opt_func{ - std::get<test_parser_t::long_opt_func_t>(long_opt_func_variant)}; + 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.given(), testing::Eq(false)); @@ -552,19 +748,61 @@ TEST(option, count_option_construct_long_default) { ASSERT_THAT(opt.value(), testing::Eq(5)); } +TEST(option, count_option_found_func_short) { + const char short_opt_name{'c'}; + const std::string opt_desc{"description"}; + + std::stringstream ss; + option_test_parser_t tp; + clapp::option::count_option_t opt{ + tp, short_opt_name, opt_desc, + option_test_parser_t::purpose_t::mandatory, + clapp::value::found_func_t{[&ss]() { ss << "this is another test"; }}}; + option_test_parser_t::short_options_map_t short_options{ + tp.get_short_options()}; + option_test_parser_t::long_options_map_t long_options{ + tp.get_long_options()}; + ASSERT_THAT(long_options.size(), testing::Eq(0)); + ASSERT_THAT(short_options.size(), testing::Eq(1)); + ASSERT_THAT(short_options.count(short_opt_name), testing::Eq(1)); + option_test_parser_t::short_opt_variant_t short_opt_func_variant{ + short_options.find(short_opt_name)->second}; + ASSERT_THAT(std::holds_alternative<option_test_parser_t::short_opt_func_t>( + short_opt_func_variant), + testing::Eq(true)); + option_test_parser_t::short_opt_func_t short_opt_func{ + 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(opt.given(), testing::Eq(false)); + ASSERT_THAT(opt.value(), testing::Eq(0)); + ASSERT_THAT(ss.str(), testing::StrEq("")); + + short_opt_func(short_opt_name); + ASSERT_THAT(ss.str(), testing::StrEq("this is another test")); + + ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true)); + ASSERT_THAT(opt.given(), testing::Eq(true)); + ASSERT_THAT(opt.value(), testing::Eq(1)); +} + TEST(option, count_option_construct_short_mandatory) { const char short_opt_name{'i'}; const std::string opt_desc{"description 3"}; - test_parser_t tp; - clapp::option::count_option_t opt{tp, short_opt_name, opt_desc, - test_parser_t::purpose_t::mandatory}; - test_parser_t::long_options_map_t long_options{tp.get_long_options()}; - test_parser_t::short_options_map_t short_options{tp.get_short_options()}; + option_test_parser_t tp; + clapp::option::count_option_t opt{ + tp, short_opt_name, opt_desc, + option_test_parser_t::purpose_t::mandatory}; + 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_optional_option_descriptions().size(), testing::Eq(0)); - test_parser_t::option_descriptions_vec_t descs{ + option_test_parser_t::option_descriptions_vec_t descs{ tp.get_mandatory_option_descriptions()}; - test_parser_t::validate_func_vec_t validate_funcs{ + option_test_parser_t::validate_func_vec_t validate_funcs{ tp.get_validate_functions()}; ASSERT_THAT(validate_funcs.size(), testing::Eq(1)); @@ -573,23 +811,24 @@ TEST(option, count_option_construct_short_mandatory) { testing::StrEq(std::string{"-"} + short_opt_name)); ASSERT_THAT(descs[0].description, testing::StrEq(opt_desc)); ASSERT_THAT(descs[0].option_type, - testing::Eq(test_parser_t::option_type_t::scalar)); + testing::Eq(option_test_parser_t::option_type_t::scalar)); ASSERT_THAT(long_options.size(), testing::Eq(0)); ASSERT_THAT(short_options.size(), testing::Eq(1)); ASSERT_THAT(short_options.count(short_opt_name), testing::Eq(1)); - test_parser_t::short_opt_variant_t short_opt_func_variant{ + option_test_parser_t::short_opt_variant_t short_opt_func_variant{ short_options.find(short_opt_name)->second}; - ASSERT_THAT(std::holds_alternative<test_parser_t::short_opt_func_t>( + ASSERT_THAT(std::holds_alternative<option_test_parser_t::short_opt_func_t>( short_opt_func_variant), testing::Eq(true)); - test_parser_t::short_opt_func_t short_opt_func{ - std::get<test_parser_t::short_opt_func_t>(short_opt_func_variant)}; + option_test_parser_t::short_opt_func_t short_opt_func{ + 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(opt.given(), testing::Eq(false)); ASSERT_THAT(opt.value(), testing::Eq(0)); - ASSERT_THROW(validate_funcs[0](), std::exception); + ASSERT_THROW(validate_funcs[0](), clapp::exception::option_exception_t); short_opt_func(short_opt_name); short_opt_func(short_opt_name); @@ -604,14 +843,16 @@ TEST(option, string_param_option_construct_short) { const char short_opt_name{'s'}; const std::string opt_desc{"string description"}; - test_parser_t tp; + option_test_parser_t tp; clapp::option::string_param_option_t opt{tp, short_opt_name, opt_desc}; - test_parser_t::long_options_map_t long_options{tp.get_long_options()}; - test_parser_t::short_options_map_t short_options{tp.get_short_options()}; + 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)); - test_parser_t::option_descriptions_vec_t descs{ + option_test_parser_t::option_descriptions_vec_t descs{ tp.get_optional_option_descriptions()}; - test_parser_t::validate_func_vec_t validate_funcs{ + option_test_parser_t::validate_func_vec_t validate_funcs{ tp.get_validate_functions()}; ASSERT_THAT(validate_funcs.size(), testing::Eq(0)); @@ -620,23 +861,24 @@ TEST(option, string_param_option_construct_short) { testing::StrEq(std::string{"-"} + short_opt_name + "=<arg>")); ASSERT_THAT(descs[0].description, testing::StrEq(opt_desc)); ASSERT_THAT(descs[0].option_type, - testing::Eq(test_parser_t::option_type_t::scalar)); + testing::Eq(option_test_parser_t::option_type_t::scalar)); ASSERT_THAT(long_options.size(), testing::Eq(0)); ASSERT_THAT(short_options.size(), testing::Eq(1)); ASSERT_THAT(short_options.count(short_opt_name), testing::Eq(1)); - test_parser_t::short_opt_variant_t short_opt_func_variant{ + option_test_parser_t::short_opt_variant_t short_opt_func_variant{ short_options.find(short_opt_name)->second}; - ASSERT_THAT(std::holds_alternative<test_parser_t::short_opt_param_func_t>( - short_opt_func_variant), - testing::Eq(true)); - test_parser_t::short_opt_param_func_t short_opt_func{ - std::get<test_parser_t::short_opt_param_func_t>( + ASSERT_THAT( + std::holds_alternative<option_test_parser_t::short_opt_param_func_t>( + short_opt_func_variant), + testing::Eq(true)); + option_test_parser_t::short_opt_param_func_t short_opt_func{ + std::get<option_test_parser_t::short_opt_param_func_t>( short_opt_func_variant)}; ASSERT_THAT(static_cast<bool>(opt), testing::Eq(false)); ASSERT_THAT(opt.given(), testing::Eq(false)); - ASSERT_THROW(opt.value(), std::exception); + 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)); @@ -644,19 +886,61 @@ TEST(option, string_param_option_construct_short) { ASSERT_THAT(opt.value(), testing::StrEq("string")); } +TEST(option, string_param_option_found_func_short) { + const char short_opt_name{'s'}; + const std::string opt_desc{"description"}; + + std::stringstream ss; + option_test_parser_t tp; + clapp::option::string_param_option_t opt{ + tp, short_opt_name, opt_desc, + option_test_parser_t::purpose_t::mandatory, + clapp::value::found_func_t{ + [&ss]() { ss << "this is another test for params"; }}}; + option_test_parser_t::short_options_map_t short_options{ + tp.get_short_options()}; + option_test_parser_t::long_options_map_t long_options{ + tp.get_long_options()}; + ASSERT_THAT(long_options.size(), testing::Eq(0)); + ASSERT_THAT(short_options.size(), testing::Eq(1)); + ASSERT_THAT(short_options.count(short_opt_name), testing::Eq(1)); + option_test_parser_t::short_opt_variant_t short_opt_func_variant{ + short_options.find(short_opt_name)->second}; + ASSERT_THAT( + std::holds_alternative<option_test_parser_t::short_opt_param_func_t>( + short_opt_func_variant), + testing::Eq(true)); + option_test_parser_t::short_opt_param_func_t short_opt_func{ + std::get<option_test_parser_t::short_opt_param_func_t>( + short_opt_func_variant)}; + + ASSERT_THAT(static_cast<bool>(opt), 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.given(), testing::Eq(true)); + ASSERT_THAT(opt.value(), testing::StrEq("param")); +} + TEST(option, string_param_option_construct_long_mandatory) { const std::string long_opt_name{"string"}; const std::string opt_desc{"string description"}; - test_parser_t tp; + option_test_parser_t tp; clapp::option::string_param_option_t opt{ - tp, long_opt_name, opt_desc, test_parser_t::purpose_t::mandatory}; - test_parser_t::long_options_map_t long_options{tp.get_long_options()}; - test_parser_t::short_options_map_t short_options{tp.get_short_options()}; + tp, long_opt_name, opt_desc, + option_test_parser_t::purpose_t::mandatory}; + 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_optional_option_descriptions().size(), testing::Eq(0)); - test_parser_t::option_descriptions_vec_t descs{ + option_test_parser_t::option_descriptions_vec_t descs{ tp.get_mandatory_option_descriptions()}; - test_parser_t::validate_func_vec_t validate_funcs{ + option_test_parser_t::validate_func_vec_t validate_funcs{ tp.get_validate_functions()}; ASSERT_THAT(validate_funcs.size(), testing::Eq(1)); @@ -665,23 +949,25 @@ TEST(option, string_param_option_construct_long_mandatory) { testing::StrEq(std::string{"--"} + long_opt_name + "=<arg>")); ASSERT_THAT(descs[0].description, testing::StrEq(opt_desc)); ASSERT_THAT(descs[0].option_type, - testing::Eq(test_parser_t::option_type_t::scalar)); + 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)); - test_parser_t::long_opt_variant_t long_opt_func_variant{ + option_test_parser_t::long_opt_variant_t long_opt_func_variant{ long_options.find(long_opt_name)->second}; - ASSERT_THAT(std::holds_alternative<test_parser_t::long_opt_param_func_t>( - long_opt_func_variant), - testing::Eq(true)); - test_parser_t::long_opt_param_func_t long_opt_func{ - std::get<test_parser_t::long_opt_param_func_t>(long_opt_func_variant)}; + ASSERT_THAT( + std::holds_alternative<option_test_parser_t::long_opt_param_func_t>( + long_opt_func_variant), + testing::Eq(true)); + option_test_parser_t::long_opt_param_func_t long_opt_func{ + std::get<option_test_parser_t::long_opt_param_func_t>( + long_opt_func_variant)}; ASSERT_THAT(static_cast<bool>(opt), testing::Eq(false)); ASSERT_THAT(opt.given(), testing::Eq(false)); - ASSERT_THROW(opt.value(), std::exception); - ASSERT_THROW(validate_funcs[0](), std::exception); + 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)); @@ -698,21 +984,23 @@ TEST(option, int_param_option_construct_long_and_short_mandatory) { const int min_value{25}; const int max_value{55}; - test_parser_t tp; + option_test_parser_t tp; clapp::option::int64_param_option_t opt{ tp, long_opt_name, short_opt_name, opt_desc, - test_parser_t::purpose_t::mandatory, - clapp::value::default_value_t{default_value}, - clapp::value::min_max_value_t{min_value, max_value}}; - test_parser_t::long_options_map_t long_options{tp.get_long_options()}; - test_parser_t::short_options_map_t short_options{tp.get_short_options()}; + option_test_parser_t::purpose_t::mandatory, + clapp::value::default_value_t<std::int64_t>{default_value}, + clapp::value::min_max_value_t<std::int64_t>{min_value, max_value}}; + 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_optional_option_descriptions().size(), testing::Eq(0)); - test_parser_t::option_descriptions_vec_t descs{ + option_test_parser_t::option_descriptions_vec_t descs{ tp.get_mandatory_option_descriptions()}; - test_parser_t::validate_func_vec_t validate_funcs{ + option_test_parser_t::validate_func_vec_t validate_funcs{ tp.get_validate_functions()}; ASSERT_THAT(validate_funcs.size(), testing::Eq(1)); @@ -722,48 +1010,52 @@ TEST(option, int_param_option_construct_long_and_short_mandatory) { std::string{"|--"} + long_opt_name + "=<arg>")); ASSERT_THAT( descs[0].description, - testing::StrEq(opt_desc + " (" + - clapp::value::default_value_t{default_value} - .append_restriction_text() + - ", " + - clapp::value::min_max_value_t{min_value, max_value} - .append_restriction_text() + - ")")); + testing::StrEq( + opt_desc + " (" + + clapp::value::default_value_t<std::int64_t>{default_value} + .append_restriction_text() + + ", " + + clapp::value::min_max_value_t<std::int64_t>{min_value, max_value} + .append_restriction_text() + + ")")); ASSERT_THAT(descs[0].option_type, - testing::Eq(test_parser_t::option_type_t::scalar)); + testing::Eq(option_test_parser_t::option_type_t::scalar)); ASSERT_THAT(short_options.size(), testing::Eq(1)); ASSERT_THAT(short_options.count(short_opt_name), testing::Eq(1)); ASSERT_THAT(long_options.size(), testing::Eq(1)); ASSERT_THAT(long_options.count(long_opt_name), testing::Eq(1)); - test_parser_t::short_opt_variant_t short_opt_func_variant{ + option_test_parser_t::short_opt_variant_t short_opt_func_variant{ short_options.find(short_opt_name)->second}; - ASSERT_THAT(std::holds_alternative<test_parser_t::short_opt_param_func_t>( - short_opt_func_variant), - testing::Eq(true)); - test_parser_t::short_opt_param_func_t short_opt_func{ - std::get<test_parser_t::short_opt_param_func_t>( + ASSERT_THAT( + std::holds_alternative<option_test_parser_t::short_opt_param_func_t>( + short_opt_func_variant), + testing::Eq(true)); + option_test_parser_t::short_opt_param_func_t short_opt_func{ + std::get<option_test_parser_t::short_opt_param_func_t>( short_opt_func_variant)}; - test_parser_t::long_opt_variant_t long_opt_func_variant{ + option_test_parser_t::long_opt_variant_t long_opt_func_variant{ long_options.find(long_opt_name)->second}; - ASSERT_THAT(std::holds_alternative<test_parser_t::long_opt_param_func_t>( - long_opt_func_variant), - testing::Eq(true)); - test_parser_t::long_opt_param_func_t long_opt_func{ - std::get<test_parser_t::long_opt_param_func_t>(long_opt_func_variant)}; + ASSERT_THAT( + std::holds_alternative<option_test_parser_t::long_opt_param_func_t>( + long_opt_func_variant), + testing::Eq(true)); + option_test_parser_t::long_opt_param_func_t long_opt_func{ + std::get<option_test_parser_t::long_opt_param_func_t>( + long_opt_func_variant)}; ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true)); ASSERT_THAT(opt.given(), testing::Eq(false)); ASSERT_THAT(opt.value(), testing::Eq(default_value)); - ASSERT_THROW(validate_funcs[0](), std::exception); + 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.given(), testing::Eq(true)); ASSERT_THAT(opt.value(), testing::Eq(123)); - ASSERT_THROW(validate_funcs[0](), std::exception); + 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)); @@ -776,26 +1068,29 @@ TEST(option, vector_string_param_option_construct_with_default_throws) { const char short_opt_name{'s'}; const std::string opt_desc{"string description"}; - test_parser_t tp; - ASSERT_THROW((clapp::option::vector_string_param_option_t{ - tp, short_opt_name, opt_desc, - clapp::value::default_value_t{"default string"}}), - std::exception); + option_test_parser_t tp; + ASSERT_THROW( + (clapp::option::vector_string_param_option_t{ + tp, short_opt_name, opt_desc, + clapp::value::default_value_t<std::string>{"default string"}}), + clapp::exception::option_param_exception_t); } TEST(option, vector_string_param_option_construct_short) { const char short_opt_name{'s'}; const std::string opt_desc{"string description"}; - test_parser_t tp; + option_test_parser_t tp; clapp::option::vector_string_param_option_t opt{tp, short_opt_name, opt_desc}; - test_parser_t::long_options_map_t long_options{tp.get_long_options()}; - test_parser_t::short_options_map_t short_options{tp.get_short_options()}; + 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)); - test_parser_t::option_descriptions_vec_t descs{ + option_test_parser_t::option_descriptions_vec_t descs{ tp.get_optional_option_descriptions()}; - test_parser_t::validate_func_vec_t validate_funcs{ + option_test_parser_t::validate_func_vec_t validate_funcs{ tp.get_validate_functions()}; ASSERT_THAT(validate_funcs.size(), testing::Eq(0)); @@ -805,18 +1100,19 @@ TEST(option, vector_string_param_option_construct_short) { ASSERT_THAT(descs[0].description, testing::StrEq(opt_desc + " (vector option)")); ASSERT_THAT(descs[0].option_type, - testing::Eq(test_parser_t::option_type_t::vector)); + testing::Eq(option_test_parser_t::option_type_t::vector)); ASSERT_THAT(long_options.size(), testing::Eq(0)); ASSERT_THAT(short_options.size(), testing::Eq(1)); ASSERT_THAT(short_options.count(short_opt_name), testing::Eq(1)); - test_parser_t::short_opt_variant_t short_opt_func_variant{ + option_test_parser_t::short_opt_variant_t short_opt_func_variant{ short_options.find(short_opt_name)->second}; - ASSERT_THAT(std::holds_alternative<test_parser_t::short_opt_param_func_t>( - short_opt_func_variant), - testing::Eq(true)); - test_parser_t::short_opt_param_func_t short_opt_func{ - std::get<test_parser_t::short_opt_param_func_t>( + ASSERT_THAT( + std::holds_alternative<option_test_parser_t::short_opt_param_func_t>( + short_opt_func_variant), + testing::Eq(true)); + option_test_parser_t::short_opt_param_func_t short_opt_func{ + std::get<option_test_parser_t::short_opt_param_func_t>( short_opt_func_variant)}; ASSERT_THAT(static_cast<bool>(opt), testing::Eq(false)); @@ -843,15 +1139,18 @@ TEST(option, vector_string_param_option_construct_long_mandatory) { const std::string long_opt_name{"string"}; const std::string opt_desc{"string description"}; - test_parser_t tp; + option_test_parser_t tp; clapp::option::vector_string_param_option_t opt{ - tp, long_opt_name, opt_desc, test_parser_t::purpose_t::mandatory}; - test_parser_t::long_options_map_t long_options{tp.get_long_options()}; - test_parser_t::short_options_map_t short_options{tp.get_short_options()}; + tp, long_opt_name, opt_desc, + option_test_parser_t::purpose_t::mandatory}; + 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_optional_option_descriptions().size(), testing::Eq(0)); - test_parser_t::option_descriptions_vec_t descs{ + option_test_parser_t::option_descriptions_vec_t descs{ tp.get_mandatory_option_descriptions()}; - test_parser_t::validate_func_vec_t validate_funcs{ + option_test_parser_t::validate_func_vec_t validate_funcs{ tp.get_validate_functions()}; ASSERT_THAT(validate_funcs.size(), testing::Eq(1)); @@ -861,23 +1160,26 @@ TEST(option, vector_string_param_option_construct_long_mandatory) { ASSERT_THAT(descs[0].description, testing::StrEq(opt_desc + " (vector option)")); ASSERT_THAT(descs[0].option_type, - testing::Eq(test_parser_t::option_type_t::vector)); + testing::Eq(option_test_parser_t::option_type_t::vector)); 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)); - test_parser_t::long_opt_variant_t long_opt_func_variant{ + option_test_parser_t::long_opt_variant_t long_opt_func_variant{ long_options.find(long_opt_name)->second}; - ASSERT_THAT(std::holds_alternative<test_parser_t::long_opt_param_func_t>( - long_opt_func_variant), - testing::Eq(true)); - test_parser_t::long_opt_param_func_t long_opt_func{ - std::get<test_parser_t::long_opt_param_func_t>(long_opt_func_variant)}; + ASSERT_THAT( + std::holds_alternative<option_test_parser_t::long_opt_param_func_t>( + long_opt_func_variant), + testing::Eq(true)); + option_test_parser_t::long_opt_param_func_t long_opt_func{ + std::get<option_test_parser_t::long_opt_param_func_t>( + long_opt_func_variant)}; ASSERT_THAT(static_cast<bool>(opt), 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](), std::exception); + ASSERT_THROW(validate_funcs[0](), + clapp::exception::option_param_exception_t); long_opt_func(long_opt_name, "string"); ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true)); @@ -904,21 +1206,63 @@ 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) { + const std::string long_opt_name{"long"}; + const std::string opt_desc{"description"}; + + std::stringstream ss; + option_test_parser_t tp; + clapp::option::vector_string_param_option_t opt{ + tp, long_opt_name, opt_desc, option_test_parser_t::purpose_t::mandatory, + clapp::value::found_func_t{ + [&ss]() { ss << "this is another test for params"; }}}; + option_test_parser_t::short_options_map_t short_options{ + tp.get_short_options()}; + option_test_parser_t::long_options_map_t long_options{ + tp.get_long_options()}; + 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_param_func_t>( + long_opt_func_variant), + testing::Eq(true)); + option_test_parser_t::long_opt_param_func_t long_opt_func{ + std::get<option_test_parser_t::long_opt_param_func_t>( + long_opt_func_variant)}; + + ASSERT_THAT(static_cast<bool>(opt), testing::Eq(false)); + ASSERT_THAT(opt.given(), testing::Eq(false)); + ASSERT_THAT(ss.str(), testing::StrEq("")); + ASSERT_THAT(opt.value().size(), testing::Eq(0)); + long_opt_func(long_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.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) { 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 min_max{10, 20}; + clapp::value::min_max_value_t<std::uint8_t> min_max{10, 20}; - test_parser_t tp; + option_test_parser_t tp; clapp::option::vector_uint8_param_option_t opt{ tp, long_opt_name, short_opt_name, opt_desc, min_max}; - test_parser_t::long_options_map_t long_options{tp.get_long_options()}; - test_parser_t::short_options_map_t short_options{tp.get_short_options()}; + 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)); - test_parser_t::option_descriptions_vec_t descs{ + option_test_parser_t::option_descriptions_vec_t descs{ tp.get_optional_option_descriptions()}; - test_parser_t::validate_func_vec_t validate_funcs{ + option_test_parser_t::validate_func_vec_t validate_funcs{ tp.get_validate_functions()}; ASSERT_THAT(validate_funcs.size(), testing::Eq(1)); @@ -930,27 +1274,30 @@ TEST(option, vector_string_param_option_construct_long_and_short_min_max) { testing::StrEq(opt_desc + " (vector option, " + min_max.append_restriction_text() + ")")); ASSERT_THAT(descs[0].option_type, - testing::Eq(test_parser_t::option_type_t::vector)); + testing::Eq(option_test_parser_t::option_type_t::vector)); ASSERT_THAT(long_options.size(), testing::Eq(1)); ASSERT_THAT(long_options.count(long_opt_name), testing::Eq(1)); - test_parser_t::long_opt_variant_t long_opt_func_variant{ + option_test_parser_t::long_opt_variant_t long_opt_func_variant{ long_options.find(long_opt_name)->second}; - ASSERT_THAT(std::holds_alternative<test_parser_t::long_opt_param_func_t>( - long_opt_func_variant), - testing::Eq(true)); - test_parser_t::long_opt_param_func_t long_opt_func{ - std::get<test_parser_t::long_opt_param_func_t>(long_opt_func_variant)}; + ASSERT_THAT( + std::holds_alternative<option_test_parser_t::long_opt_param_func_t>( + long_opt_func_variant), + testing::Eq(true)); + option_test_parser_t::long_opt_param_func_t long_opt_func{ + std::get<option_test_parser_t::long_opt_param_func_t>( + long_opt_func_variant)}; ASSERT_THAT(short_options.size(), testing::Eq(1)); ASSERT_THAT(short_options.count(short_opt_name), testing::Eq(1)); - test_parser_t::short_opt_variant_t short_opt_func_variant{ + option_test_parser_t::short_opt_variant_t short_opt_func_variant{ short_options.find(short_opt_name)->second}; - ASSERT_THAT(std::holds_alternative<test_parser_t::short_opt_param_func_t>( - short_opt_func_variant), - testing::Eq(true)); - test_parser_t::short_opt_param_func_t short_opt_func{ - std::get<test_parser_t::short_opt_param_func_t>( + ASSERT_THAT( + std::holds_alternative<option_test_parser_t::short_opt_param_func_t>( + short_opt_func_variant), + testing::Eq(true)); + option_test_parser_t::short_opt_param_func_t short_opt_func{ + std::get<option_test_parser_t::short_opt_param_func_t>( short_opt_func_variant)}; ASSERT_THAT(static_cast<bool>(opt), testing::Eq(false)); @@ -983,7 +1330,7 @@ TEST(option, vector_string_param_option_construct_long_and_short_min_max) { ASSERT_THAT(vec[0], testing::Eq(0x12)); ASSERT_THAT(vec[1], testing::Eq(12)); ASSERT_THAT(vec[2], testing::Eq(1)); - ASSERT_THROW(validate_funcs[0](), std::exception); + ASSERT_THROW(validate_funcs[0](), clapp::exception::out_of_range_t); } TEST(option, invalid_long_option_construct) { @@ -995,39 +1342,39 @@ TEST(option, invalid_long_option_construct) { const std::string desc{"desc1"}; const char short_opt_name{'a'}; - test_parser_t tp; + option_test_parser_t tp; ASSERT_THROW((clapp::option::bool_option_t{tp, long_opt_name1, desc}), - std::exception); + clapp::exception::option_exception_t); ASSERT_THROW((clapp::option::bool_option_t{tp, long_opt_name1, short_opt_name, desc}), - std::exception); + clapp::exception::option_exception_t); ASSERT_THROW((clapp::option::count_option_t{tp, long_opt_name2, desc}), - std::exception); + clapp::exception::option_exception_t); ASSERT_THROW((clapp::option::count_option_t{tp, long_opt_name2, short_opt_name, desc}), - std::exception); + clapp::exception::option_exception_t); ASSERT_THROW( (clapp::option::string_param_option_t{tp, long_opt_name3, desc}), - std::exception); + clapp::exception::option_exception_t); ASSERT_THROW((clapp::option::string_param_option_t{tp, long_opt_name3, short_opt_name, desc}), - std::exception); + clapp::exception::option_exception_t); ASSERT_THROW( (clapp::option::vector_string_param_option_t{tp, long_opt_name4, desc}), - std::exception); + clapp::exception::option_exception_t); ASSERT_THROW((clapp::option::vector_string_param_option_t{ tp, long_opt_name4, short_opt_name, desc}), - std::exception); + clapp::exception::option_exception_t); ASSERT_THROW( (clapp::option::int32_param_option_t{tp, long_opt_name5, desc}), - std::exception); + clapp::exception::option_exception_t); ASSERT_THROW((clapp::option::int32_param_option_t{tp, long_opt_name5, short_opt_name, desc}), - std::exception); + clapp::exception::option_exception_t); } TEST(option, invalid_short_option_construct) { @@ -1039,39 +1386,39 @@ TEST(option, invalid_short_option_construct) { const std::string desc{"desc1"}; const std::string long_opt_name{'a'}; - test_parser_t tp; + option_test_parser_t tp; ASSERT_THROW((clapp::option::bool_option_t{tp, short_opt_name1, desc}), - std::exception); + clapp::exception::option_exception_t); ASSERT_THROW((clapp::option::bool_option_t{tp, long_opt_name, short_opt_name1, desc}), - std::exception); + clapp::exception::option_exception_t); ASSERT_THROW((clapp::option::count_option_t{tp, short_opt_name2, desc}), - std::exception); + clapp::exception::option_exception_t); ASSERT_THROW((clapp::option::count_option_t{tp, long_opt_name, short_opt_name2, desc}), - std::exception); + clapp::exception::option_exception_t); ASSERT_THROW( (clapp::option::string_param_option_t{tp, short_opt_name3, desc}), - std::exception); + clapp::exception::option_exception_t); ASSERT_THROW((clapp::option::string_param_option_t{tp, long_opt_name, short_opt_name3, desc}), - std::exception); + clapp::exception::option_exception_t); ASSERT_THROW((clapp::option::vector_string_param_option_t{ tp, short_opt_name4, desc}), - std::exception); + clapp::exception::option_exception_t); ASSERT_THROW((clapp::option::vector_string_param_option_t{ tp, long_opt_name, short_opt_name4, desc}), - std::exception); + clapp::exception::option_exception_t); ASSERT_THROW( (clapp::option::int32_param_option_t{tp, short_opt_name5, desc}), - std::exception); + clapp::exception::option_exception_t); ASSERT_THROW((clapp::option::int32_param_option_t{tp, long_opt_name, short_opt_name5, desc}), - std::exception); + clapp::exception::option_exception_t); } struct mock_t { @@ -1089,29 +1436,29 @@ TEST(option, gen_opt_validate_func) { { mock_t mock; - std::optional<test_parser_t::validate_func_t> validate_func{ + 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::vector<int32_validate_func_t>{}, "option string", - test_parser_t::purpose_t::mandatory)}; + 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)(), std::runtime_error); + ASSERT_THROW((*validate_func)(), clapp::exception::option_exception_t); } { mock_t mock; - std::optional<test_parser_t::validate_func_t> validate_func{ + 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", - test_parser_t::purpose_t::mandatory)}; + option_test_parser_t::purpose_t::mandatory)}; ASSERT_THAT(validate_func.has_value(), testing::Eq(true)); EXPECT_CALL(mock, given_func()) .Times(1) @@ -1121,7 +1468,7 @@ TEST(option, gen_opt_validate_func) { { mock_t mock; - std::optional<test_parser_t::validate_func_t> validate_func{ + 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(); }, @@ -1132,7 +1479,7 @@ TEST(option, gen_opt_validate_func) { const std::string& option_string) { mock.validate_func(value, option_string); }}, - "option string", test_parser_t::purpose_t::mandatory)}; + "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()) @@ -1148,7 +1495,7 @@ TEST(option, gen_opt_validate_func) { { mock_t mock; - std::optional<test_parser_t::validate_func_t> validate_func{ + 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, @@ -1158,11 +1505,12 @@ TEST(option, gen_opt_validate_func) { const std::string& option_string) { mock.validate_func(value, option_string); }}, - "option string", test_parser_t::purpose_t::mandatory)}; + "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)(), std::exception); + ASSERT_THROW((*validate_func)(), + clapp::exception::option_param_exception_t); } } diff --git a/tests/parser.cpp b/tests/parser.cpp index 2cba267f..24bed075 100644 --- a/tests/parser.cpp +++ b/tests/parser.cpp @@ -1,93 +1,469 @@ +#include <clapp/argument.h> #include <clapp/main_parser.h> #include <clapp/option.h> #include <clapp/parser.h> +#include <clapp/sub_parser.h> #include <gmock/gmock.h> template <class T, size_t N> -constexpr clapp::parser::arg_t make_arg_t(T (&arg)[N]) { - return clapp::parser::arg_t{arg, N - 1}; +inline clapp::parser::arg_t parser_make_arg_t(T (&arg)[N]) { + return clapp::parser::arg_t{static_cast<const char* const*>(arg), N - 1}; } class empty_basic_parser_t : public clapp::basic_parser_t { public: using clapp::basic_parser_t::basic_parser_t; - ~empty_basic_parser_t(); + ~empty_basic_parser_t() override; }; empty_basic_parser_t::~empty_basic_parser_t() = default; -class param_option_parser_t : public clapp::basic_parser_t { +class simple_test_parser_t : public clapp::basic_parser_t { public: using clapp::basic_parser_t::basic_parser_t; - ~param_option_parser_t(); + ~simple_test_parser_t() override; 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::argument::string_argument_t string_arg{*this, "arg-name", + "Arg desc"}; + + clapp::argument::variadic_string_argument_t variadic_string_arg{ + *this, "variadic-arg-name", "Variadic arg desc", + clapp::basic_parser_t::purpose_t::optional}; +}; + +simple_test_parser_t::~simple_test_parser_t() = default; + +class simple_test_parser2_t : public clapp::basic_parser_t { + public: + using clapp::basic_parser_t::basic_parser_t; + ~simple_test_parser2_t() override; + + clapp::option::bool_option_t count_option{ + *this, "count", 'c', "Count option.", + clapp::basic_parser_t::purpose_t::mandatory}; + + clapp::argument::string_argument_t string_arg{ + *this, "arg-name", "Arg desc", + clapp::basic_parser_t::purpose_t::optional}; +}; + +simple_test_parser2_t::~simple_test_parser2_t() = default; + +class simple_test_parser3_t : public clapp::basic_parser_t { + public: + using clapp::basic_parser_t::basic_parser_t; + ~simple_test_parser3_t() override; + + 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::basic_parser_t::purpose_t::mandatory}; + + clapp::option::vector_string_param_option_t string_option{ + *this, "str", 's', "String option.", + clapp::basic_parser_t::purpose_t::optional}; + + clapp::argument::variadic_string_argument_t variadic_string_arg{ + *this, "variadic-arg-name", "Variadic arg desc", + clapp::basic_parser_t::purpose_t::mandatory}; +}; + +simple_test_parser3_t::~simple_test_parser3_t() = default; + +class sub_parser_container_t : public clapp::basic_parser_t { + public: + using clapp::basic_parser_t::basic_parser_t; + ~sub_parser_container_t() override; + + clapp::option::bool_option_t bool_option{*this, "bool", 'b', + "Bool option."}; + + clapp::option::bool_option_t bool_option2{*this, "second", '2', + "Second bool option."}; + + clapp::argument::string_argument_t string_arg{*this, "arg-name", + "Arg desc"}; + + class simple_sub_parser_t : public clapp::parser::basic_sub_parser_t { + public: + ~simple_sub_parser_t() override; + clapp::option::bool_option_t bool_option{*this, "bool", 'b', + "Bool option."}; + + using clapp::parser::basic_sub_parser_t::basic_sub_parser_t; + clapp::option::string_param_option_t string_option{*this, "string", 's', + "String option."}; + + clapp::argument::string_argument_t string_arg{ + *this, "sub-arg-name", "Sub arg desc", + clapp::basic_parser_t::purpose_t::optional}; + }; + + simple_sub_parser_t sub_parser{*this, "sub-parser", "Sub parser desc"}; }; -param_option_parser_t::~param_option_parser_t() = default; +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; } TEST(parser, construct_empty_basic_parser_and_parse_empty_arguments) { constexpr const char* const argv[]{nullptr}; - const clapp::parser::arg_t arg{make_arg_t(argv)}; + 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) { + constexpr const char* const argv[]{nullptr}; + const clapp::parser::arg_t arg{parser_make_arg_t(argv)}; empty_basic_parser_t ebp; - ebp.parse(arg.cbegin(), arg.cend()); + ASSERT_NO_THROW(ebp.parse(arg.cbegin(), arg.cend())); + ASSERT_NO_THROW(ebp.validate()); +} + +TEST(parser, construct_empty_basic_parser_and_gen_help_prefix) { + empty_basic_parser_t ebp; + ASSERT_THAT(ebp.gen_help_prefix(), testing::StrEq("Usage: \n")); +} + +TEST(parser, construct_empty_basic_parser_and_gen_help_message) { + empty_basic_parser_t ebp; + ASSERT_THAT(ebp.gen_help_msg(), testing::StrEq("\n")); +} + +TEST(parser, gen_func_print_help_and_exit) { + 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")); +} + +TEST(parser, construct_simple_test_parser_and_gen_help_message) { + simple_test_parser_t stp; + ASSERT_THAT( + stp.gen_help_msg(), + 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")); +} + +TEST(parser, construct_simple_test_parser2_and_gen_help_message) { + simple_test_parser2_t stp; + 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")); +} + +TEST(parser, construct_simple_test_parser3_and_gen_help_message) { + simple_test_parser3_t stp; + ASSERT_THAT( + stp.gen_help_msg(), + 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")); +} + +TEST(parser, construct_sub_parser_container_and_gen_help_message) { + 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")); +} + +TEST(parser, construct_sub_parser_container_and_gen_sub_parser_help_message) { + 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")); } TEST(parser, construct_empty_basic_parser_and_parse_unknown_arguments_throws) { constexpr const char* const argv[]{"unknown-argument", nullptr}; - const clapp::parser::arg_t arg{make_arg_t(argv)}; + const clapp::parser::arg_t arg{parser_make_arg_t(argv)}; empty_basic_parser_t ebp; - ASSERT_THROW(ebp.parse(arg.cbegin(), arg.cend()), std::exception); + ASSERT_THROW(ebp.parse(arg.cbegin(), arg.cend()), + clapp::exception::clapp_exception_t); } TEST(parser, construct_empty_basic_parser_and_parse_unknown_long_option_throws) { constexpr const char* const argv[]{"--long-option", nullptr}; - const clapp::parser::arg_t arg{make_arg_t(argv)}; + const clapp::parser::arg_t arg{parser_make_arg_t(argv)}; empty_basic_parser_t ebp; - ASSERT_THROW(ebp.parse(arg.cbegin(), arg.cend()), std::exception); + ASSERT_THROW(ebp.parse(arg.cbegin(), arg.cend()), + clapp::exception::option_exception_t); } TEST(parser, construct_empty_basic_parser_and_parse_unknown_short_option_throws) { constexpr const char* const argv[]{"-s", nullptr}; - const clapp::parser::arg_t arg{make_arg_t(argv)}; + const clapp::parser::arg_t arg{parser_make_arg_t(argv)}; empty_basic_parser_t ebp; - ASSERT_THROW(ebp.parse(arg.cbegin(), arg.cend()), std::exception); + ASSERT_THROW(ebp.parse(arg.cbegin(), arg.cend()), + clapp::exception::option_exception_t); } TEST( parser, - construct_param_option_parser_and_parse_short_option_without_params_equal) { + construct_simple_test_parser_and_parse_long_bool_option_with_param_throws) { constexpr const char* const argv[]{"--bool=param", nullptr}; - const clapp::parser::arg_t arg{make_arg_t(argv)}; - param_option_parser_t ebp; - ASSERT_THROW(ebp.parse(arg.cbegin(), arg.cend()), std::exception); + const clapp::parser::arg_t arg{parser_make_arg_t(argv)}; + simple_test_parser_t ebp; + ASSERT_THROW(ebp.parse(arg.cbegin(), arg.cend()), + clapp::exception::option_param_exception_t); } TEST(parser, - construct_param_option_parser_and_parse_short_option_without_params) { - constexpr const char* const argv[]{"--bool", "param", nullptr}; - const clapp::parser::arg_t arg{make_arg_t(argv)}; - param_option_parser_t ebp; - ASSERT_THROW(ebp.parse(arg.cbegin(), arg.cend()), std::exception); + construct_simple_test_parser_and_parse_short_option_with_param_throws) { + constexpr const char* const argv[]{"-b=param", nullptr}; + const clapp::parser::arg_t arg{parser_make_arg_t(argv)}; + simple_test_parser_t ebp; + ASSERT_THROW(ebp.parse(arg.cbegin(), arg.cend()), + clapp::exception::option_param_exception_t); +} + +TEST( + parser, + construct_simple_test_parser_and_parse_long_int_option_without_param_throws) { + constexpr const char* const argv[]{"--int", nullptr}; + const clapp::parser::arg_t arg{parser_make_arg_t(argv)}; + simple_test_parser_t ebp; + ASSERT_THROW(ebp.parse(arg.cbegin(), arg.cend()), + clapp::exception::option_param_exception_t); +} + +TEST( + parser, + construct_simple_test_parser_and_parse_short_int_option_without_param_throws) { + constexpr const char* const argv[]{"-i", nullptr}; + const clapp::parser::arg_t arg{parser_make_arg_t(argv)}; + simple_test_parser_t ebp; + ASSERT_THROW(ebp.parse(arg.cbegin(), arg.cend()), + clapp::exception::option_param_exception_t); +} + +TEST( + parser, + construct_simple_test_parser_and_parse_short_int_option_without_param_throws2) { + constexpr const char* const argv[]{"-ib", nullptr}; + const clapp::parser::arg_t arg{parser_make_arg_t(argv)}; + simple_test_parser_t ebp; + ASSERT_THROW(ebp.parse(arg.cbegin(), arg.cend()), + clapp::exception::option_param_exception_t); +} + +TEST( + parser, + construct_simple_test_parser_parse_without_argument_and_validate_recursive_throws) { + constexpr const char* const argv[]{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(ebp.get_num_processed_arguments(), testing::Eq(0)); + ASSERT_THROW(ebp.validate_recursive(), + clapp::exception::argument_exception_t); } TEST(parser, - construct_param_option_parser_and_parse_long_option_without_params_equal) { - constexpr const char* const argv[]{"-b=param", nullptr}; - const clapp::parser::arg_t arg{make_arg_t(argv)}; - param_option_parser_t ebp; - ASSERT_THROW(ebp.parse(arg.cbegin(), arg.cend()), std::exception); + construct_simple_test_parser_parse_argument_and_validate_recursive) { + 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.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")); + ASSERT_THAT(static_cast<bool>(ebp.variadic_string_arg), testing::Eq(false)); + ASSERT_THAT(ebp.get_num_processed_arguments(), testing::Eq(1)); + ASSERT_NO_THROW(ebp.validate_recursive()); +} + +TEST( + parser, + construct_simple_test_parser_parse_argument_and_short_option_without_params_and_validate_recursive) { + 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(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")); + ASSERT_THAT(static_cast<bool>(ebp.variadic_string_arg), testing::Eq(false)); + ASSERT_THAT(ebp.get_num_processed_arguments(), testing::Eq(1)); + ASSERT_NO_THROW(ebp.validate_recursive()); +} + +TEST( + parser, + construct_simple_test_parser_parse_argument_and_long_option_with_param_and_validate) { + 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.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)); + ASSERT_THAT(ebp.string_arg.value(), testing::StrEq("arg")); + ASSERT_THAT(static_cast<bool>(ebp.variadic_string_arg), testing::Eq(false)); + ASSERT_THAT(ebp.get_num_processed_arguments(), testing::Eq(1)); + ASSERT_NO_THROW(ebp.validate_recursive()); +} + +TEST( + parser, + construct_simple_test_parser_parse_argument_variadic_argument_and_long_option_with_param_and_validate) { + 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.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)); + ASSERT_THAT(ebp.string_arg.value(), testing::StrEq("arg")); + ASSERT_THAT(static_cast<bool>(ebp.variadic_string_arg), testing::Eq(true)); + ASSERT_THAT(ebp.variadic_string_arg.value().size(), testing::Eq(2)); + ASSERT_THAT(ebp.variadic_string_arg.value()[0], testing::StrEq("varg0")); + ASSERT_THAT(ebp.variadic_string_arg.value()[1], testing::StrEq("varg1")); + ASSERT_THAT(ebp.get_num_processed_arguments(), testing::Eq(3)); + ASSERT_NO_THROW(ebp.validate_recursive()); +} + +TEST( + parser, + construct_simple_test_parser_parse_argument_and_short_options_and_validate) { + 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.value(), testing::Eq(true)); + 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)); + ASSERT_THAT(ebp.string_arg.value(), testing::StrEq("arg")); + ASSERT_THAT(ebp.get_num_processed_arguments(), testing::Eq(1)); + ASSERT_NO_THROW(ebp.validate_recursive()); +} + +TEST( + parser, + construct_simple_test_parser2_and_parse_long_count_option_with_param_throws) { + constexpr const char* const argv[]{"--count=param", nullptr}; + const clapp::parser::arg_t arg{parser_make_arg_t(argv)}; + simple_test_parser2_t ebp; + ASSERT_THROW(ebp.parse(arg.cbegin(), arg.cend()), + clapp::option_param_exception_t); } TEST(parser, - construct_param_option_parser_and_parse_long_option_without_params) { - constexpr const char* const argv[]{"-b", "param", nullptr}; - const clapp::parser::arg_t arg{make_arg_t(argv)}; - param_option_parser_t ebp; - ASSERT_THROW(ebp.parse(arg.cbegin(), arg.cend()), std::exception); + construct_simple_test_parser2_parse_without_mandatory_option_throws) { + 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_THROW(ebp.validate(), clapp::option_param_exception_t); +} + +TEST(parser, + construct_simple_test_parser2_parse_option_without_param_and_validate) { + 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.value(), testing::Eq(1)); + ASSERT_NO_THROW(ebp.validate()); +} + +TEST( + parser, + construct_simple_test_parser2_parse_option_without_param_optional_argument_and_validate) { + 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; + 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.value(), testing::Eq(1)); + ASSERT_THAT(static_cast<bool>(ebp.string_arg), testing::Eq(true)); + ASSERT_THAT(ebp.string_arg.value(), testing::StrEq("opt-arg")); + ASSERT_NO_THROW(ebp.validate()); +} + +TEST(parser, construct_sub_parser_container_parse_subparser_and_validate) { + constexpr const char* const argv[]{"string-arg", "sub-parser", "-bs=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(false)); + ASSERT_THAT(static_cast<bool>(spc.bool_option2), 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(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}; + 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.value(), testing::Eq(true)); + ASSERT_THAT(static_cast<bool>(spc.bool_option2), 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)); + 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()); } diff --git a/tests/sub_parser.cpp b/tests/sub_parser.cpp index c9455385..d0e8a25f 100644 --- a/tests/sub_parser.cpp +++ b/tests/sub_parser.cpp @@ -1,11 +1,13 @@ -#include <clapp/sub_parser.h> +#include <clapp/argument.h> +#include <clapp/exception.h> #include <clapp/option.h> #include <clapp/parser.h> +#include <clapp/sub_parser.h> #include <gmock/gmock.h> template <class T, size_t N> -constexpr clapp::parser::arg_t make_arg_t(T (&arg)[N]) { - return clapp::parser::arg_t{arg, N - 1}; +inline clapp::parser::arg_t sub_parser_make_arg_t(T (&arg)[N]) { + return clapp::parser::arg_t{static_cast<const char* const*>(arg), N - 1}; } class empty_test_parser_t : public clapp::parser::basic_parser_t { @@ -13,40 +15,80 @@ class empty_test_parser_t : public clapp::parser::basic_parser_t { using clapp::parser::basic_parser_t::sub_parser_descriptions_vec_t; using clapp::parser::basic_parser_t::sub_parsers_map_t; - using clapp::parser::basic_parser_t::get_sub_parsers; using clapp::parser::basic_parser_t::get_sub_parser_descriptions; + using clapp::parser::basic_parser_t::get_sub_parsers; + + ~empty_test_parser_t() override; }; +empty_test_parser_t::~empty_test_parser_t() = default; + class empty_sub_parser_t : public clapp::parser::basic_sub_parser_t { - public: + public: using clapp::parser::basic_sub_parser_t::basic_sub_parser_t; + + ~empty_sub_parser_t() override; }; -class simple_test_parser_t : public clapp::parser::basic_parser_t { +empty_sub_parser_t::~empty_sub_parser_t() = default; + +class simple_test_sub_parser_t : public clapp::parser::basic_parser_t { public: using clapp::parser::basic_parser_t::sub_parser_descriptions_vec_t; using clapp::parser::basic_parser_t::sub_parsers_map_t; - using clapp::parser::basic_parser_t::get_sub_parsers; using clapp::parser::basic_parser_t::get_sub_parser_descriptions; + using clapp::parser::basic_parser_t::get_sub_parsers; clapp::option::count_option_t count_option{*this, "count", 'c', - "Count option."}; + "Count option."}; + + ~simple_test_sub_parser_t() override; }; +simple_test_sub_parser_t::~simple_test_sub_parser_t() = default; + class simple_sub_parser_t : public clapp::parser::basic_sub_parser_t { - public: + public: using clapp::parser::basic_sub_parser_t::basic_sub_parser_t; clapp::option::bool_option_t bool_option{*this, "bool", 'b', "Bool option."}; + + ~simple_sub_parser_t() override; +}; + +simple_sub_parser_t::~simple_sub_parser_t() = default; + +class variadic_argument_test_sub_parser_t + : public clapp::parser::basic_parser_t { + public: + clapp::argument::variadic_string_argument_t variadic_string_argument{ + *this, "arg", "Arg"}; + + ~variadic_argument_test_sub_parser_t() override; +}; + +variadic_argument_test_sub_parser_t::~variadic_argument_test_sub_parser_t() = + default; + +class optional_argument_test_sub_parser_t + : public clapp::parser::basic_parser_t { + public: + clapp::argument::string_argument_t string_argument{ + *this, "arg", "Arg", clapp::basic_parser_t::purpose_t::optional}; + + ~optional_argument_test_sub_parser_t() override; }; +optional_argument_test_sub_parser_t::~optional_argument_test_sub_parser_t() = + default; + TEST(sub_parser, construct_empty_sub_parser_and_parse_empty_arguments) { 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{make_arg_t(argv)}; + 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}; @@ -56,10 +98,8 @@ TEST(sub_parser, construct_empty_sub_parser_and_parse_empty_arguments) { 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)); + ASSERT_THAT(descs[0].sub_parser_string, testing::StrEq(sub_parser)); + ASSERT_THAT(descs[0].description, testing::StrEq(description)); ASSERT_THAT(static_cast<bool>(sub), testing::Eq(false)); ASSERT_THAT(sub.get_sub_parser_name(), testing::StrEq(sub_parser)); @@ -72,20 +112,19 @@ TEST(sub_parser, construct_simple_sub_parser_and_parse_sub_option) { const std::string description{"subbb parser"}; constexpr const char* const argv[]{"subbb", "--bool", nullptr}; - const clapp::parser::arg_t arg{make_arg_t(argv)}; + const clapp::parser::arg_t arg{sub_parser_make_arg_t(argv)}; - simple_test_parser_t tp; + simple_test_sub_parser_t tp; simple_sub_parser_t sub{tp, sub_parser, description}; - simple_test_parser_t::sub_parsers_map_t long_options{tp.get_sub_parsers()}; - simple_test_parser_t::sub_parser_descriptions_vec_t descs{ + 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)); + ASSERT_THAT(descs[0].sub_parser_string, testing::StrEq(sub_parser)); + ASSERT_THAT(descs[0].description, testing::StrEq(description)); ASSERT_THAT(static_cast<bool>(sub), testing::Eq(false)); ASSERT_THAT(sub.get_sub_parser_name(), testing::StrEq(sub_parser)); @@ -100,20 +139,19 @@ TEST(sub_parser, construct_simple_sub_parser_and_parse_base_option) { const std::string description{"subbb parser"}; constexpr const char* const argv[]{"subbb", "--count", nullptr}; - const clapp::parser::arg_t arg{make_arg_t(argv)}; + const clapp::parser::arg_t arg{sub_parser_make_arg_t(argv)}; - simple_test_parser_t tp; + simple_test_sub_parser_t tp; simple_sub_parser_t sub{tp, sub_parser, description}; - simple_test_parser_t::sub_parsers_map_t long_options{tp.get_sub_parsers()}; - simple_test_parser_t::sub_parser_descriptions_vec_t descs{ + 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)); + ASSERT_THAT(descs[0].sub_parser_string, testing::StrEq(sub_parser)); + ASSERT_THAT(descs[0].description, testing::StrEq(description)); ASSERT_THAT(static_cast<bool>(sub), testing::Eq(false)); ASSERT_THAT(sub.get_sub_parser_name(), testing::StrEq(sub_parser)); @@ -128,25 +166,64 @@ TEST(sub_parser, construct_simple_sub_parser_and_ignore_main_option) { const std::string description{"subbb parser"}; constexpr const char* const argv[]{"subbb", "--count", nullptr}; - const clapp::parser::arg_t arg{make_arg_t(argv)}; + const clapp::parser::arg_t arg{sub_parser_make_arg_t(argv)}; - simple_test_parser_t tp; + simple_test_sub_parser_t tp; simple_sub_parser_t sub{tp, sub_parser, description, false}; - simple_test_parser_t::sub_parsers_map_t long_options{tp.get_sub_parsers()}; - simple_test_parser_t::sub_parser_descriptions_vec_t descs{ + 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)); + ASSERT_THAT(descs[0].sub_parser_string, testing::StrEq(sub_parser)); + ASSERT_THAT(descs[0].description, testing::StrEq(description)); ASSERT_THAT(static_cast<bool>(sub), testing::Eq(false)); ASSERT_THAT(sub.get_sub_parser_name(), testing::StrEq(sub_parser)); ASSERT_THAT(tp.count_option.value(), testing::Eq(0)); - ASSERT_THROW(tp.parse(arg.cbegin(), arg.cend()), std::exception); + 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()); +} + +TEST(sub_parser, construct_sub_parser_with_same_name_throws) { + 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}), + clapp::exception::sub_parser_exception_t); +} + +TEST(sub_parser, construct_sub_parser_with_variadic_arguments_parser_throws) { + 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); +} + +TEST(sub_parser, construct_sub_parser_with_optional_argument_parser_throws) { + 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); +} + +TEST(parser, construct_sub_parser_and_gen_help_prefix) { + const std::string sub_parser{"sub"}; + const std::string description{"sub parser"}; + + 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")); } diff --git a/tests/value.cpp b/tests/value.cpp index b8acc05f..8eb3072b 100644 --- a/tests/value.cpp +++ b/tests/value.cpp @@ -1,3 +1,4 @@ +#include <clapp/exception.h> #include <clapp/value.h> #include <gmock/gmock.h> @@ -21,7 +22,7 @@ TEST(value, convert_value_uint8_t) { ASSERT_THAT(clapp::value::convert_value<std::uint8_t>("255"), 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{2})); ASSERT_THAT(clapp::value::convert_value<std::uint8_t>("0xab"), testing::Eq(std::uint8_t{0xabu})); ASSERT_THAT(clapp::value::convert_value<std::uint8_t>("0xff"), @@ -29,9 +30,11 @@ TEST(value, convert_value_uint8_t) { ASSERT_THAT(clapp::value::convert_value<std::uint8_t>("077"), testing::Eq(std::uint8_t{077})); ASSERT_THROW(clapp::value::convert_value<std::uint8_t>("256"), - std::out_of_range); + clapp::exception::out_of_range_t); ASSERT_THROW(clapp::value::convert_value<std::uint8_t>("-1"), - std::out_of_range); + clapp::exception::out_of_range_t); + ASSERT_THROW(clapp::value::convert_value<std::uint8_t>("zzz"), + clapp::exception::invalid_value_t); } TEST(value, convert_value_uint16_t) { @@ -42,7 +45,7 @@ TEST(value, convert_value_uint16_t) { ASSERT_THAT(clapp::value::convert_value<std::uint16_t>("65535"), 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{2222})); ASSERT_THAT(clapp::value::convert_value<std::uint16_t>("0xffab"), testing::Eq(std::uint16_t{0xffabu})); ASSERT_THAT(clapp::value::convert_value<std::uint16_t>("0xffff"), @@ -50,9 +53,11 @@ TEST(value, convert_value_uint16_t) { ASSERT_THAT(clapp::value::convert_value<std::uint16_t>("01234"), testing::Eq(std::uint16_t{01234u})); ASSERT_THROW(clapp::value::convert_value<std::uint16_t>("65536"), - std::out_of_range); + clapp::exception::out_of_range_t); ASSERT_THROW(clapp::value::convert_value<std::uint16_t>("-100"), - std::out_of_range); + clapp::exception::out_of_range_t); + ASSERT_THROW(clapp::value::convert_value<std::uint16_t>("z"), + clapp::exception::invalid_value_t); } TEST(value, convert_value_uint32_t) { @@ -69,9 +74,11 @@ TEST(value, convert_value_uint32_t) { ASSERT_THAT(clapp::value::convert_value<std::uint32_t>("0123456"), testing::Eq(std::uint32_t{0123456u})); ASSERT_THROW(clapp::value::convert_value<std::uint32_t>("4294967296"), - std::out_of_range); + clapp::exception::out_of_range_t); ASSERT_THROW(clapp::value::convert_value<std::uint32_t>("-10000"), - std::out_of_range); + clapp::exception::out_of_range_t); + ASSERT_THROW(clapp::value::convert_value<std::uint32_t>("zz"), + clapp::exception::invalid_value_t); } TEST(value, convert_value_uint64_t) { @@ -88,10 +95,12 @@ TEST(value, convert_value_uint64_t) { clapp::value::convert_value<std::uint64_t>("0xffffffffffffffff"), 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"), - std::out_of_range); + clapp::exception::out_of_range_t); + ASSERT_THROW(clapp::value::convert_value<std::uint64_t>("zzzz"), + clapp::exception::invalid_value_t); } TEST(value, convert_value_int8_t) { @@ -112,9 +121,11 @@ TEST(value, convert_value_int8_t) { ASSERT_THAT(clapp::value::convert_value<std::int8_t>("-077"), testing::Eq(std::int8_t{-077})); ASSERT_THROW(clapp::value::convert_value<std::int8_t>("128"), - std::out_of_range); + clapp::exception::out_of_range_t); ASSERT_THROW(clapp::value::convert_value<std::int8_t>("-129"), - std::out_of_range); + clapp::exception::out_of_range_t); + ASSERT_THROW(clapp::value::convert_value<std::int8_t>("zzzz"), + clapp::exception::invalid_value_t); } TEST(value, convert_value_int16_t) { @@ -133,11 +144,13 @@ 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"), - std::out_of_range); + clapp::exception::out_of_range_t); ASSERT_THROW(clapp::value::convert_value<std::int16_t>("-32769"), - std::out_of_range); + clapp::exception::out_of_range_t); + ASSERT_THROW(clapp::value::convert_value<std::int16_t>("zzz"), + clapp::exception::invalid_value_t); } TEST(value, convert_value_int32_t) { @@ -156,11 +169,13 @@ TEST(value, convert_value_int32_t) { 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"), - std::out_of_range); + clapp::exception::out_of_range_t); ASSERT_THROW(clapp::value::convert_value<std::int32_t>("-2147483649"), - std::out_of_range); + clapp::exception::out_of_range_t); + ASSERT_THROW(clapp::value::convert_value<std::int32_t>("zz"), + clapp::exception::invalid_value_t); } TEST(value, convert_value_int64_t) { @@ -183,13 +198,111 @@ TEST(value, convert_value_int64_t) { 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"), - std::out_of_range); + clapp::exception::out_of_range_t); ASSERT_THROW( clapp::value::convert_value<std::int64_t>("-9223372036854775809"), - std::out_of_range); + clapp::exception::out_of_range_t); + ASSERT_THROW(clapp::value::convert_value<std::int64_t>("z"), + clapp::exception::invalid_value_t); +} + +TEST(value, convert_value_double) { + ASSERT_THAT(clapp::value::convert_value<double>("0"), + testing::Eq(double{0})); + ASSERT_THAT(clapp::value::convert_value<double>("10000000000"), + testing::Eq(double{10000000000.})); + ASSERT_THAT(clapp::value::convert_value<double>("0.125"), + testing::Eq(double{0.125})); + ASSERT_THAT(clapp::value::convert_value<double>("9223372036854775807"), + testing::Eq(double{9223372036854775807.})); + ASSERT_THAT(clapp::value::convert_value<double>("-9223372036854775807"), + testing::Eq(double{-9223372036854775807.})); + ASSERT_THAT(clapp::value::convert_value<double>("-9223372036854775808"), + testing::Eq(double{-9223372036854775807. - 1.})); + ASSERT_THAT(clapp::value::convert_value<double>("5e-1"), + testing::Eq(double{.5})); + ASSERT_THROW(clapp::value::convert_value<double>("10e-600"), + clapp::exception::out_of_range_t); + ASSERT_THROW(clapp::value::convert_value<double>("z"), + clapp::exception::invalid_value_t); +} + +TEST(value, convert_value_float) { + ASSERT_THAT(clapp::value::convert_value<float>("0"), + testing::Eq(float{0.f})); + ASSERT_THAT(clapp::value::convert_value<float>("10000000000"), + testing::Eq(float{10000000000.f})); + ASSERT_THAT(clapp::value::convert_value<float>("0.125"), + testing::Eq(float{0.125f})); + ASSERT_THAT(clapp::value::convert_value<float>("5e-1"), + 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) { + ASSERT_THAT(clapp::value::convert_value<std::chrono::nanoseconds>("0"), + testing::Eq(std::chrono::nanoseconds{0u})); + ASSERT_THAT( + clapp::value::convert_value<std::chrono::nanoseconds>("1000000"), + testing::Eq(std::chrono::nanoseconds{1000000u})); +} + +TEST(value, convert_value_chrono_microseconds) { + ASSERT_THAT(clapp::value::convert_value<std::chrono::microseconds>("0"), + testing::Eq(std::chrono::microseconds{0u})); + ASSERT_THAT( + clapp::value::convert_value<std::chrono::microseconds>("1000000"), + testing::Eq(std::chrono::microseconds{1000000u})); +} + +TEST(value, convert_value_chrono_milliseconds) { + ASSERT_THAT(clapp::value::convert_value<std::chrono::milliseconds>("0"), + testing::Eq(std::chrono::milliseconds{0u})); + ASSERT_THAT( + clapp::value::convert_value<std::chrono::milliseconds>("1000000"), + testing::Eq(std::chrono::milliseconds{1000000u})); +} + +TEST(value, convert_value_chrono_seconds) { + ASSERT_THAT(clapp::value::convert_value<std::chrono::seconds>("0"), + testing::Eq(std::chrono::seconds{0u})); + ASSERT_THAT(clapp::value::convert_value<std::chrono::seconds>("1000000"), + testing::Eq(std::chrono::seconds{1000000u})); +} + +TEST(value, convert_value_chrono_minutes) { + ASSERT_THAT(clapp::value::convert_value<std::chrono::minutes>("0"), + testing::Eq(std::chrono::minutes{0u})); + ASSERT_THAT(clapp::value::convert_value<std::chrono::minutes>("1000000"), + testing::Eq(std::chrono::minutes{1000000u})); +} + +TEST(value, convert_value_chrono_hours) { + ASSERT_THAT(clapp::value::convert_value<std::chrono::hours>("0"), + testing::Eq(std::chrono::hours{0u})); + ASSERT_THAT(clapp::value::convert_value<std::chrono::hours>("1000000"), + testing::Eq(std::chrono::hours{1000000u})); +} + +TEST(value, get_chrono_postfix) { + ASSERT_THAT(clapp::value::get_chrono_postfix<std::chrono::nanoseconds>(), + testing::StrEq("ns")); + ASSERT_THAT(clapp::value::get_chrono_postfix<std::chrono::microseconds>(), + testing::StrEq("µs")); + ASSERT_THAT(clapp::value::get_chrono_postfix<std::chrono::milliseconds>(), + testing::StrEq("ms")); + ASSERT_THAT(clapp::value::get_chrono_postfix<std::chrono::seconds>(), + testing::StrEq("s")); + ASSERT_THAT(clapp::value::get_chrono_postfix<std::chrono::minutes>(), + testing::StrEq("m")); + ASSERT_THAT(clapp::value::get_chrono_postfix<std::chrono::hours>(), + testing::StrEq("h")); } TEST(value, default_value_uint8_t) { @@ -228,6 +341,17 @@ TEST(value, default_value_string) { ASSERT_THAT(dv.default_value(), testing::Eq(value)); } +TEST(value, default_value_milliseconds) { + constexpr std::uint64_t value{30}; + clapp::value::default_value_t<std::chrono::milliseconds> dv{ + std::chrono::milliseconds{value}}; + std::stringstream ss; + ss << "default-value: " << value << "ms"; + ASSERT_THAT(dv.append_restriction_text(), testing::StrEq(ss.str())); + ASSERT_THAT(dv.default_value(), + testing::Eq(std::chrono::milliseconds{value})); +} + TEST(value, min_max_value_uint8_t) { constexpr std::uint8_t min{10}; constexpr std::uint8_t max{50}; @@ -239,8 +363,8 @@ TEST(value, min_max_value_uint8_t) { EXPECT_NO_THROW(mmv.validate(min, "option")); EXPECT_NO_THROW(mmv.validate(max, "option")); EXPECT_NO_THROW(mmv.validate(30, "option")); - ASSERT_THROW(mmv.validate(5, "option"), std::out_of_range); - ASSERT_THROW(mmv.validate(55, "option"), std::out_of_range); + ASSERT_THROW(mmv.validate(5, "option"), clapp::exception::out_of_range_t); + ASSERT_THROW(mmv.validate(55, "option"), clapp::exception::out_of_range_t); } TEST(value, min_max_value_int32_t) { @@ -253,8 +377,40 @@ TEST(value, min_max_value_int32_t) { EXPECT_NO_THROW(mmv.validate(min, "option")); EXPECT_NO_THROW(mmv.validate(max, "option")); EXPECT_NO_THROW(mmv.validate(-100, "option")); - ASSERT_THROW(mmv.validate(-2000, "option"), std::out_of_range); - ASSERT_THROW(mmv.validate(55, "option"), std::out_of_range); + ASSERT_THROW(mmv.validate(-2000, "option"), + clapp::exception::out_of_range_t); + ASSERT_THROW(mmv.validate(55, "option"), clapp::exception::out_of_range_t); +} + +TEST(value, min_max_value_double_t) { + const double min{100.}; + const double max{200.5}; + clapp::value::min_max_value_t<double> mmv{min, max}; + std::stringstream ss; + ss << "constraint: [" << min << "," << max << "]"; + ASSERT_THAT(mmv.append_restriction_text(), testing::StrEq(ss.str())); + EXPECT_NO_THROW(mmv.validate(min, "option")); + 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"), + clapp::exception::out_of_range_t); +} + +TEST(value, min_max_value_milliseconds_t) { + 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}; + std::stringstream ss; + ss << "constraint: [" << min.count() << "ms," << max.count() << "ms]"; + ASSERT_THAT(mmv.append_restriction_text(), testing::StrEq(ss.str())); + EXPECT_NO_THROW(mmv.validate(min, "option")); + EXPECT_NO_THROW(mmv.validate(max, "option")); + EXPECT_NO_THROW(mmv.validate(std::chrono::milliseconds{130}, "option")); + ASSERT_THROW(mmv.validate(std::chrono::milliseconds{50}, "option"), + clapp::exception::out_of_range_t); + ASSERT_THROW(mmv.validate(std::chrono::milliseconds{300}, "option"), + clapp::exception::out_of_range_t); } TEST(value, path_exists_t) { @@ -263,5 +419,12 @@ TEST(value, path_exists_t) { EXPECT_NO_THROW(pe.validate(clapp::fs::path{"/tmp"}, "option")); ASSERT_THROW( pe.validate(clapp::fs::path{"/tmp/aba/sadf/aksk/annsp"}, "arg"), - std::runtime_error); + clapp::exception::path_does_not_exist_t); +} + +TEST(value, found_func_t) { + std::stringstream ss; + clapp::value::found_func_t ff{[&ss]() { ss << "called func"; }}; + ff.found(); + ASSERT_THAT(ss.str(), testing::StrEq("called func")); } -- GitLab