feat use argagg
All checks were successful
rpcrypto-build / build (Debug, hisiv510.toolchain.cmake) (push) Successful in 1m4s
rpcrypto-build / build (Debug, himix200.toolchain.cmake) (push) Successful in 1m17s
rpcrypto-build / build (Release, hisiv510.toolchain.cmake) (push) Successful in 1m17s
rpcrypto-build / build (Release, himix200.toolchain.cmake) (push) Successful in 1m24s
linux-hisiv500-gcc / linux-gcc-hisiv500 (push) Successful in 1m25s
linux-mips64-gcc / linux-gcc-mips64el (push) Successful in 1m34s
linux-x64-gcc / linux-gcc (push) Successful in 1m36s
All checks were successful
rpcrypto-build / build (Debug, hisiv510.toolchain.cmake) (push) Successful in 1m4s
rpcrypto-build / build (Debug, himix200.toolchain.cmake) (push) Successful in 1m17s
rpcrypto-build / build (Release, hisiv510.toolchain.cmake) (push) Successful in 1m17s
rpcrypto-build / build (Release, himix200.toolchain.cmake) (push) Successful in 1m24s
linux-hisiv500-gcc / linux-gcc-hisiv500 (push) Successful in 1m25s
linux-mips64-gcc / linux-gcc-mips64el (push) Successful in 1m34s
linux-x64-gcc / linux-gcc (push) Successful in 1m36s
This commit is contained in:
parent
55b28fb58b
commit
cc74cbfda7
1
3party/argagg/.gitignore
vendored
Normal file
1
3party/argagg/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
build
|
16
3party/argagg/.travis.yml
Normal file
16
3party/argagg/.travis.yml
Normal file
@ -0,0 +1,16 @@
|
||||
language: cpp
|
||||
dist: trusty
|
||||
install:
|
||||
- sudo apt-get install lcov
|
||||
- gem install coveralls-lcov
|
||||
script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake .. -DCMAKE_BUILD_TYPE=DEBUG -DCMAKE_CXX_FLAGS="-fprofile-arcs -ftest-coverage"
|
||||
- cmake --build .
|
||||
- ctest -V
|
||||
after_success:
|
||||
- lcov -d . -c -o coverage.info
|
||||
- lcov -r coverage.info "/usr*" "$(readlink -f ../test)/*" -o coverage.info
|
||||
- lcov -l coverage.info
|
||||
- coveralls-lcov --repo-token ${COVERALLS_TOKEN} coverage.info
|
161
3party/argagg/CHANGELOG
Normal file
161
3party/argagg/CHANGELOG
Normal file
@ -0,0 +1,161 @@
|
||||
0.4.6
|
||||
-----
|
||||
|
||||
- Added use of argagg::fmt_ostream in gengetopt_main1 example
|
||||
- Merged pull request #12
|
||||
- replace static_cast<bool> w/ non-zero comparison to avoid compiler
|
||||
warnings on MSVC
|
||||
- Typo fixes
|
||||
|
||||
0.4.5
|
||||
-----
|
||||
|
||||
- Fixed bug in integer and float argument conversion specializations where
|
||||
invalid conversions were not being correctly handled. Invalid conversions
|
||||
(like "garbage" to float) will now throw an exception. Note though if the
|
||||
as<T>(default) overload is used (with a default value specified) then all
|
||||
exceptions in the conversion result in the default being returned.
|
||||
- Changed base exception type for argagg exceptions from std::argument_error to
|
||||
std::runtime_error
|
||||
- Now compiles with -Wpedantic
|
||||
|
||||
0.4.4
|
||||
-----
|
||||
|
||||
- Added some minor error handling to argagg::fmt_string()
|
||||
|
||||
0.4.3
|
||||
-----
|
||||
|
||||
- Added argagg::fmt_string() to format a string using the fmt program when
|
||||
compiling on a __unix__ platform. Degrades to an identity function when not
|
||||
on a __unix__ platform.
|
||||
- Added argagg::fmt_ostream as a convenience stream that will stream the
|
||||
formatted string to the referenced final std::ostream when the
|
||||
argagg::fmt_ostream destructs
|
||||
- Fixed bug when compiling with clang where the non-template implicit bool
|
||||
operator was being selected when implicitly converting to an integer. By
|
||||
making the implicit bool operator a specialization of the templated implicit
|
||||
conversion operator the compiler should now select the correct overload.
|
||||
|
||||
0.4.2
|
||||
-----
|
||||
|
||||
- Fixed missing inline specifiers on argagg::convert::arg specializations,
|
||||
fixes multiple definitions bug
|
||||
|
||||
0.4.1
|
||||
-----
|
||||
|
||||
- Fixed compiler errors when using clang with regards to some initialization
|
||||
list usage for empty containers and a false positive -Wreturn-stack-address
|
||||
treated as an error
|
||||
|
||||
0.4.0
|
||||
-----
|
||||
|
||||
- Changed argagg::definition::help and argagg::definition::flags to use
|
||||
std::string instead of const char*
|
||||
- Fixed compilation error with clang
|
||||
|
||||
0.3.1
|
||||
-----
|
||||
|
||||
- Updated documentation
|
||||
- Added greedy processing examples and tests
|
||||
|
||||
0.3.0
|
||||
-----
|
||||
|
||||
- Added support for POSIX command line option behaviors
|
||||
- Options (short) start with a hyphen (-) and long options start with two
|
||||
hyphens (--)
|
||||
- Multiple short options can be grouped following a single hyphen
|
||||
- -a -b -c can also be written -abc or -bac, etc.
|
||||
- Option names are alpha numeric but long options may include hyphens
|
||||
- -v is valid, --ftest-coverage is valid
|
||||
- -# is not valid, --bad$option is not valid
|
||||
- Short options can be provided arguments with or without whitespace
|
||||
delimiters
|
||||
- -I /usr/local/include and -I/usr/local/include are equally valid
|
||||
- Long options can be provided arguments with whitespace or equal sign
|
||||
delimiters
|
||||
- --output test.txt and --output=test.txt are equivalent
|
||||
- Options and positional arguments can be interleaved
|
||||
- -- can be specified to treat all following arguments as positional
|
||||
arguments (i.e. not options)
|
||||
- Added option definition validation
|
||||
- Checks for malformed flags
|
||||
- Checks for duplicate flags
|
||||
- Added conversion functions for rest of fundamental integer types
|
||||
- Built in conversion functions now utilize strtol(), strtoll(), strtof(), and
|
||||
strtod()
|
||||
- Added char** argv overload of argagg::parser::parse()
|
||||
- Removed argagg::optional, options now either receive arguments or they don't
|
||||
- Added example derived from gengetopt documentation
|
||||
- Expanded unit test coverage to 98%
|
||||
- Added coveralls integration for code coverage reports
|
||||
|
||||
0.2.2
|
||||
-----
|
||||
|
||||
- Actually tried compiled introduction example code
|
||||
- Fixed bugs in the intro example code
|
||||
- Added it to test cases
|
||||
- More detailed comments in the joinargs example
|
||||
- Separated documentation from devel subpackage into doc subpackage in RPM spec
|
||||
|
||||
0.2.1
|
||||
-----
|
||||
|
||||
- Minor documentation fix, listed incorrect exception
|
||||
|
||||
0.2.0
|
||||
-----
|
||||
|
||||
- Redesigned API, removed multiple flag args
|
||||
- Redesigned naming scheme, options instead of flags. Struct names are now
|
||||
clearer, realized the "flags" were actually options
|
||||
- argagg::flag is now argagg::option_result
|
||||
- argagg::flags is now argagg::option_results
|
||||
- argagg::result is now argagg::parser_results
|
||||
- Renamed args field to pos
|
||||
- argagg::flag_spec is now argagg::definition
|
||||
- Flags can now only have zero, one, or an optional argument
|
||||
- Moved argagg::flag_spec::optional to argagg::optional
|
||||
- Added argagg::option_results which represents multiple flag parse results for
|
||||
the parent argagg:definition
|
||||
- Added implicit boolean conversion for argagg::option_results which represents
|
||||
whether or not the parent argagg::definition was found at all
|
||||
- Added implicit boolean conversion for argagg::option_result which represents
|
||||
whether or not that single flag has an argument
|
||||
- Added implicit type conversions for argagg::flag using the same
|
||||
argagg::convert::arg() system
|
||||
- Updated tests and examples accordingly
|
||||
- Renamed argagg_joinargs make target to just joinargs
|
||||
- Removed old Doxygen style.css
|
||||
- Added option_lacks_argument_error exception class
|
||||
- Added associated flag to exception error messages
|
||||
|
||||
0.1.3
|
||||
-----
|
||||
|
||||
- Fixed file specification for empty parent package
|
||||
|
||||
0.1.2
|
||||
-----
|
||||
|
||||
- Fixed documentation installation under RPM building
|
||||
- Added missing build dependencies from RPM specfile
|
||||
|
||||
0.1.1
|
||||
-----
|
||||
|
||||
- Removed gcc color output flag
|
||||
- Fixed grammar in documentation
|
||||
- Updated include guard definition name
|
||||
|
||||
0.1.0
|
||||
-----
|
||||
|
||||
- Initial version
|
170
3party/argagg/CMakeLists.txt
Normal file
170
3party/argagg/CMakeLists.txt
Normal file
@ -0,0 +1,170 @@
|
||||
cmake_minimum_required( VERSION 2.8 )
|
||||
project( argagg CXX )
|
||||
|
||||
option(
|
||||
ARGAGG_BUILD_EXAMPLES
|
||||
"build examples"
|
||||
ON
|
||||
)
|
||||
|
||||
option(
|
||||
ARGAGG_BUILD_TESTS
|
||||
"build tests"
|
||||
ON
|
||||
)
|
||||
|
||||
option(
|
||||
ARGAGG_BUILD_DOCS
|
||||
"build docs"
|
||||
ON
|
||||
)
|
||||
|
||||
set(
|
||||
ARGAGG_TEST_COMPILE_FLAGS
|
||||
"-g -Wall -Wextra -Wpedantic -Werror -std=c++11"
|
||||
CACHE STRING "Compiler flags for all project targets"
|
||||
)
|
||||
|
||||
# When RPM packages are built CMake is invoked with a -DINCLUDE_INSTALL_DIR
|
||||
# that we should respect. If it isn't present then we default it to "include".
|
||||
set(
|
||||
INCLUDE_INSTALL_DIR "include"
|
||||
CACHE STRING "Include install folder name (default: include)"
|
||||
)
|
||||
|
||||
# When RPM packages are built CMake is invoked with a -DLIB_INSTALL_DIR that we
|
||||
# should respect. This is important because this is how the RPM build process
|
||||
# specifies installation into "lib64" instead of "lib". If it isn't present
|
||||
# then we default it to "lib".
|
||||
set(
|
||||
LIB_INSTALL_DIR "lib"
|
||||
CACHE STRING "Library install folder name (default: lib)"
|
||||
)
|
||||
|
||||
# When RPM packages are built CMake is invoked with a -DSHARE_INSTALL_PREFIX
|
||||
# that we should respect. This is particularly important because this is how
|
||||
# the RPM build process specifies installation of shared data files into
|
||||
# "/usr/share". If it isn't present then we default it to "share" relative to
|
||||
# the CMAKE_INSTALL_PREFIX which is incidentally the same path when the install
|
||||
# prefix is "/usr".
|
||||
set(
|
||||
SHARE_INSTALL_PREFIX share
|
||||
CACHE STRING "Shared data install folder name (default: share)"
|
||||
)
|
||||
|
||||
|
||||
# Set up a target to install the program which amounts to copying the single
|
||||
# header file.
|
||||
install(
|
||||
DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include"
|
||||
DESTINATION "${INCLUDE_INSTALL_DIR}/.."
|
||||
)
|
||||
|
||||
|
||||
# Build examples if configured to.
|
||||
if( ARGAGG_BUILD_EXAMPLES )
|
||||
add_executable( joinargs "examples/joinargs.cpp" )
|
||||
set_target_properties(
|
||||
joinargs
|
||||
PROPERTIES
|
||||
COMPILE_FLAGS "${ARGAGG_TEST_COMPILE_FLAGS}"
|
||||
INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/include"
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
|
||||
)
|
||||
|
||||
add_executable( gengetopt_main1 "examples/gengetopt_main1.cpp" )
|
||||
set_target_properties(
|
||||
gengetopt_main1
|
||||
PROPERTIES
|
||||
COMPILE_FLAGS "${ARGAGG_TEST_COMPILE_FLAGS}"
|
||||
INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/include"
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
|
||||
OUTPUT_NAME sample1
|
||||
)
|
||||
endif()
|
||||
|
||||
|
||||
# Build and register the unit tests if configured to.
|
||||
if( ARGAGG_BUILD_TESTS )
|
||||
enable_testing()
|
||||
add_executable( argagg_test "test/test.cpp" )
|
||||
set_target_properties(
|
||||
argagg_test
|
||||
PROPERTIES
|
||||
COMPILE_FLAGS "${ARGAGG_TEST_COMPILE_FLAGS}"
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
|
||||
)
|
||||
add_test(
|
||||
NAME argagg_test
|
||||
COMMAND argagg_test
|
||||
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
|
||||
)
|
||||
endif()
|
||||
|
||||
|
||||
# Build Doxygen documentation if we can find Doxygen and we're configured to
|
||||
# build documentation.
|
||||
find_program( DOXYGEN doxygen )
|
||||
if( ARGAGG_BUILD_DOCS AND DOXYGEN )
|
||||
|
||||
# Everyone loves documentation! Lets set up a target to generate documentation.
|
||||
# First lets collect everything that we think can change the documentation.
|
||||
# This basically means everything inside the "docs" folder along with every
|
||||
# header and source file. If one of those changes then we want to mark the
|
||||
# documentation for regeneration.
|
||||
file(
|
||||
GLOB_RECURSE DOC_INPUTS
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/doc/*"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/include/*"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/test/*"
|
||||
)
|
||||
|
||||
# Like the version.hpp header file we're going to replace some CMake variables
|
||||
# in the doxygen configuration with actual values.
|
||||
configure_file(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/doc/doxygen.cfg.in
|
||||
${CMAKE_BINARY_DIR}/doxygen.cfg
|
||||
@ONLY
|
||||
)
|
||||
|
||||
# Now we add a command for CMake to run to generate documentation.
|
||||
add_custom_command(
|
||||
# We tell CMake that this command will generate the `html` and `xml`
|
||||
# folders inside the project's documentation share folder inside the build
|
||||
# folder. Specifying these outputs explicitly adds them to the "clean"
|
||||
# target. Note that we put the documentation into the "share" folder in the
|
||||
# build folder. This is to structure the build folder as a prefix.
|
||||
OUTPUT
|
||||
"${CMAKE_BINARY_DIR}/share/doc/${PROJECT_NAME}/html"
|
||||
"${CMAKE_BINARY_DIR}/share/doc/${PROJECT_NAME}/xml"
|
||||
# This command makes sure that the output folder exists first.
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_BINARY_DIR}/share/doc/${PROJECT_NAME}"
|
||||
# This command performs the doxygen documentation generation.
|
||||
COMMAND ${DOXYGEN} doxygen.cfg
|
||||
# Run the above commands in the build folder.
|
||||
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
|
||||
# Tell CMake that if any of the documentation input files we collected change
|
||||
# then this command needs to be re-run.
|
||||
DEPENDS
|
||||
${CMAKE_BINARY_DIR}/doxygen.cfg
|
||||
${DOC_INPUTS}
|
||||
)
|
||||
|
||||
# This creates an "empty target" named "docs" that doesn't produce output, but
|
||||
# depends on the generated documentation. The result is we can run "make docs"
|
||||
# and it behaves the way we expect. It also doesn't regenerate documentation if
|
||||
# the outputs specified below are up-to-date.
|
||||
add_custom_target(
|
||||
docs ALL
|
||||
DEPENDS
|
||||
"${CMAKE_BINARY_DIR}/share/doc/${PROJECT_NAME}/html"
|
||||
"${CMAKE_BINARY_DIR}/share/doc/${PROJECT_NAME}/xml"
|
||||
)
|
||||
|
||||
# Finally set up an installation target for the documentation.
|
||||
install(
|
||||
DIRECTORY "${CMAKE_BINARY_DIR}/share/"
|
||||
DESTINATION "${SHARE_INSTALL_PREFIX}"
|
||||
)
|
||||
|
||||
endif()
|
19
3party/argagg/LICENSE
Normal file
19
3party/argagg/LICENSE
Normal file
@ -0,0 +1,19 @@
|
||||
Copyright (c) 2017 Viet The Nguyen
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
252
3party/argagg/README.md
Normal file
252
3party/argagg/README.md
Normal file
@ -0,0 +1,252 @@
|
||||
Argument Aggregator
|
||||
===================
|
||||
|
||||
| Branch | Build | Coverage |
|
||||
| --- | --- | --- |
|
||||
| `master` | [![Build Status](https://api.travis-ci.org/vietjtnguyen/argagg.svg?branch=master)](https://travis-ci.org/vietjtnguyen/argagg) | [![Coverage Status](https://coveralls.io/repos/github/vietjtnguyen/argagg/badge.svg?branch=master)](https://coveralls.io/github/vietjtnguyen/argagg?branch=master) |
|
||||
| `dev` | [![Build Status](https://api.travis-ci.org/vietjtnguyen/argagg.svg?branch=dev)](https://travis-ci.org/vietjtnguyen/argagg/branches) | [![Coverage Status](https://coveralls.io/repos/github/vietjtnguyen/argagg/badge.svg?branch=dev)](https://coveralls.io/github/vietjtnguyen/argagg?branch=dev) |
|
||||
|
||||
This is yet another C++ command line argument/option parser. It was written as a simple and idiomatic alternative to other frameworks like [getopt][], [Boost program options][], [TCLAP][], and others. The goal is to achieve the majority of argument parsing needs in a simple manner with an easy to use API. It operates as a single pass over all arguments, recognizing flags prefixed by `-` (short) or `--` (long) and aggregating them into easy to access structures with lots of convenience functions. It defers processing types until you access them, so the result structures end up just being pointers into the original command line argument C-strings.
|
||||
|
||||
`argagg` supports [POSIX recommended argument syntax conventions](https://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html):
|
||||
|
||||
- Options (short) start with a hyphen (`-`) and long options start with two hyphens (`--`)
|
||||
- Multiple short options can be grouped following a single hyphen
|
||||
- `-a -b -c` can also be written `-abc` or `-bac`, etc.
|
||||
- Option names are alpha numeric but long options may include hyphens
|
||||
- `-v` is valid, `--ftest-coverage` is valid
|
||||
- `-#` is not valid, `--bad$option` is not valid
|
||||
- Short options can be provided arguments with or without whitespace delimiters
|
||||
- `-I /usr/local/include` and `-I/usr/local/include` are equally valid
|
||||
- Long options can be provided arguments with whitespace or equal sign delimiters
|
||||
- `--output test.txt` and `--output=test.txt` are equivalent
|
||||
- Options and positional arguments can be interleaved
|
||||
- `--` can be specified to treat all following arguments as positional arguments (i.e. not options)
|
||||
|
||||
Help message formatting is provided via the `fmt` utility on {Li,U}nix systems.
|
||||
|
||||
[getopt]: https://www.gnu.org/software/libc/manual/html_node/Getopt.html#Getopt
|
||||
[Boost program options]: http://www.boost.org/doc/libs/release/libs/program_options/
|
||||
[TCLAP]: http://tclap.sourceforge.net/
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
To use just create an `argagg::parser` object. The struct doesn't provide any explicit methods for defining options. Instead we define the options using [initialization lists][].
|
||||
|
||||
[initialization lists]: http://en.cppreference.com/w/cpp/language/list_initialization
|
||||
|
||||
```cpp
|
||||
argagg::parser argparser {{
|
||||
{ "help", {"-h", "--help"},
|
||||
"shows this help message", 0},
|
||||
{ "delim", {"-d", "--delim"},
|
||||
"delimiter (default: ,)", 1},
|
||||
{ "num", {"-n", "--num"},
|
||||
"number", 1},
|
||||
}};
|
||||
```
|
||||
|
||||
An option is specified by four things: the name of the option, the strings that activate the option (flags), the option's help message, and the number of arguments the option expects.
|
||||
|
||||
With the parser defined you actually parse the arguments by calling the `argagg::parser::parse()` method. If there are any problems an exception is thrown.
|
||||
|
||||
```cpp
|
||||
argagg::parser_results args;
|
||||
try {
|
||||
args = argparser.parse(argc, argv);
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << e.what() << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
```
|
||||
|
||||
You can check if an option shows up in the command line arguments by accessing the option by name from the parser results and using the implicit boolean conversion. You can write out a simplistic option help message by streaming the `argagg::parser` instance itself.
|
||||
|
||||
```cpp
|
||||
if (args["help"]) {
|
||||
std::cerr << argparser;
|
||||
// -h, --help
|
||||
// shows this help message
|
||||
// -d, --delim
|
||||
// delimiter (default: ,)
|
||||
// -n, --num
|
||||
// number
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
```
|
||||
|
||||
That help message is only for the flags. If you want a usage message it's up to you to provide it.
|
||||
|
||||
```cpp
|
||||
if (args["help"]) {
|
||||
std::cerr << "Usage: program [options] ARG1 ARG2" << std::endl
|
||||
<< argparser;
|
||||
// Usage: program [options] ARG1 ARG2
|
||||
// -h, --help
|
||||
// shows this help message
|
||||
// -d, --delim
|
||||
// delimiter (default: ,)
|
||||
// -n, --num
|
||||
// number
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
```
|
||||
|
||||
A special output stream, `argagg::fmt_ostream`, is provided that will run the usage and help through `fmt` for nice word wrapping (see [`./examples/joinargs.cpp`](./examples/joinargs.cpp) for a better example).
|
||||
|
||||
```cpp
|
||||
if (args["help"]) {
|
||||
argagg::fmt_ostream fmt(std::cerr);
|
||||
fmt << "Usage: program [options] ARG1 ARG2" << std::endl
|
||||
<< argparser;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
```
|
||||
|
||||
Generally `argagg` tries to do a minimal amount of work to leave most of the control with the user.
|
||||
|
||||
If you want to get an option argument but fallback on a default value if it doesn't exist then you can use the `argagg::option_results::as()` API and provide a default value.
|
||||
|
||||
```cpp
|
||||
auto delim = args["delim"].as<std::string>(",");
|
||||
```
|
||||
|
||||
If you don't mind being implicit an implicit conversion operator is provided allowing you to write simple assignments.
|
||||
|
||||
```cpp
|
||||
int x = 0;
|
||||
if (args["num"]) {
|
||||
x = args["num"];
|
||||
}
|
||||
```
|
||||
|
||||
Finally, you can get all of the positional arguments as an `std::vector` using the `argagg::parser_results::pos` member. You can alternatively convert individual positional arguments using the same conversion functions as the option argument conversion methods.
|
||||
|
||||
auto y = 0.0;
|
||||
if (args.pos.size() > 0) {
|
||||
y = args.as<double>(0);
|
||||
}
|
||||
|
||||
One can also specify `--` on the command line in order to treat all following arguments as not options.
|
||||
|
||||
For a more detailed treatment take a look at the [examples](./examples) or [test cases](./test/test.cpp).
|
||||
|
||||
Mental Model
|
||||
------------
|
||||
|
||||
The parser just returns a structure of pointers to the C-strings in the original `argv` array. The `parse()` method returns a `parser_results` object which has two things: position arguments and option results. The position arguments are just a `std::vector` of `const char*`. The option results are a mapping from option name (`std::string`) to `option_results` objects. The `option_results` objects are just an `std::vector` of `option_result` objects. Each instance of an `option_result` represents the option showing up on the command line. If there was an argument associated with it then the `option_result`'s `arg` member will *not* be `nullptr`.
|
||||
|
||||
Consider the following command:
|
||||
|
||||
```sh
|
||||
gcc -g -I/usr/local/include -I. -o test main.o foo.o -L/usr/local/lib -lz bar.o -lpng
|
||||
```
|
||||
|
||||
This would produce a structure like follows, written in psuedo-YAML, where each string is actually a `const char*` pointing to some part of a string in the original `argv` array:
|
||||
|
||||
```yaml
|
||||
parser_results:
|
||||
program: "gcc"
|
||||
pos: ["main.o", "foo.o", "bar.o"]
|
||||
options:
|
||||
version:
|
||||
debug:
|
||||
all:
|
||||
- arg: null
|
||||
include_path:
|
||||
all:
|
||||
- arg: "/usr/local/include"
|
||||
- arg: "."
|
||||
library_path:
|
||||
all:
|
||||
- arg: "/usr/local/lib"
|
||||
library:
|
||||
all:
|
||||
- arg: "z"
|
||||
- arg: "png"
|
||||
output:
|
||||
all:
|
||||
- arg: "test"
|
||||
```
|
||||
|
||||
Conversion to types occurs at the very end when the `as<T>()` API is used. Up to that point `argagg` is just dealing with C-strings.
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
Doxygen documentation can be found [here](https://vietjtnguyen.github.io/argagg/latest/).
|
||||
|
||||
Quick Reference
|
||||
---------------
|
||||
|
||||
## Structs
|
||||
|
||||
- `option_result`
|
||||
- `const char* arg`
|
||||
- `option_results`
|
||||
- `std::vector<option_result> all`
|
||||
- `parser_results`
|
||||
- `const char* program`
|
||||
- `std::unordered_map<std::string, option_results> options`
|
||||
- `std::vector<const char*> pos`
|
||||
- `definition`
|
||||
- `const char* name`
|
||||
- `std::vector<std::string> flag`
|
||||
- `std::string help`
|
||||
- `unsigned int num_args`
|
||||
- `parser_map`
|
||||
- `std::array<const definition*, 256> short_map`
|
||||
- `std::unordered_map<std::string, const definition*> long_map`
|
||||
- `parser`
|
||||
- `std::vector<definition> definitions`
|
||||
|
||||
## Exceptions
|
||||
|
||||
- `unexpected_argument_error`
|
||||
- `unexpected_option_error`
|
||||
- `option_lacks_argument_error`
|
||||
- `invalid_flag`
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
There is just a single header file ([`argagg.hpp`](./include/argagg/argagg.hpp)) so you can copy that whereever you want. If you want to properly install it you can use the CMake script. The CMake script exists primarily to build the tests and documentation, but an install target for the header is provided.
|
||||
|
||||
The standard installation dance using CMake and `make` is as follows:
|
||||
|
||||
```sh
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_INSTALL_PREFIX=/usr/local ..
|
||||
make install
|
||||
ctest -V # optionally run tests
|
||||
```
|
||||
|
||||
Override [`CMAKE_INSTALL_PREFIX`](https://cmake.org/cmake/help/v2.8.12/cmake.html#variable:CMAKE_INSTALL_PREFIX) to change the installation location. By default (on UNIX variants) it will install to `/usr/local` resulting in the header being copied to `/usr/local/include/argagg/argagg.hpp`.
|
||||
|
||||
If you have [Doxygen](http://www.stack.nl/~dimitri/doxygen/) it should build and install documentation as well.
|
||||
|
||||
There are no dependencies other than the standard library.
|
||||
|
||||
Edge Cases
|
||||
----------
|
||||
|
||||
There are some interesting edge cases that show up in option parsing. I used the behavior of `gcc` as my target reference in these cases.
|
||||
|
||||
### Greedy Arguments
|
||||
|
||||
Remember that options that require arguments will greedily process arguments.
|
||||
|
||||
Say we have the following options: `-a`, `-b`, `-c`, and `-o`. They all don't accept arguments except `-o`. Below is a list of permutations for short flag grouping and the results:
|
||||
|
||||
- `-abco foo`: `-o`'s argument is `foo`
|
||||
- `-aboc foo`: `-o`'s argument is `c`, `foo` is a positional argument
|
||||
- `-aobc foo`: `-o`'s argument is `bc`, `foo` is a positional argument
|
||||
- `-oabc foo`: `-o`'s argument is `abc`, `foo` is a positional argument
|
||||
|
||||
For whitespace delimited arguments the greedy processing means the next argument element (in `argv`) will be treated as an argument for the previous option, regardless of whether or not it looks like a flag or some other special entry. That means you get behavior like below:
|
||||
|
||||
- `--output=foo -- --bar`: `--output`'s argument is `foo`, `--bar` is a positional argument
|
||||
- `--output -- --bar`: `--output`'s argument is `--`, `--bar` is treated as a flag
|
||||
- `--output --bar`: `--output`'s argument is `--bar`
|
3
3party/argagg/doc/.gitignore
vendored
Normal file
3
3party/argagg/doc/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
_build
|
||||
doxygen
|
||||
gh-pages
|
35489
3party/argagg/doc/cppreference-doxygen-web.tag.xml
Normal file
35489
3party/argagg/doc/cppreference-doxygen-web.tag.xml
Normal file
File diff suppressed because it is too large
Load Diff
263
3party/argagg/doc/doxygen.cfg.in
Normal file
263
3party/argagg/doc/doxygen.cfg.in
Normal file
@ -0,0 +1,263 @@
|
||||
# Doxyfile 1.6.1
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Project related configuration options
|
||||
#---------------------------------------------------------------------------
|
||||
DOXYFILE_ENCODING = UTF-8
|
||||
PROJECT_NAME = @PROJECT_NAME@
|
||||
PROJECT_NUMBER =
|
||||
OUTPUT_DIRECTORY = @CMAKE_BINARY_DIR@/share/doc/@PROJECT_NAME@
|
||||
CREATE_SUBDIRS = NO
|
||||
OUTPUT_LANGUAGE = English
|
||||
BRIEF_MEMBER_DESC = YES
|
||||
REPEAT_BRIEF = YES
|
||||
ABBREVIATE_BRIEF =
|
||||
ALWAYS_DETAILED_SEC = NO
|
||||
INLINE_INHERITED_MEMB = NO
|
||||
FULL_PATH_NAMES = YES
|
||||
STRIP_FROM_PATH =
|
||||
STRIP_FROM_INC_PATH =
|
||||
SHORT_NAMES = NO
|
||||
JAVADOC_AUTOBRIEF = NO
|
||||
QT_AUTOBRIEF = NO
|
||||
MULTILINE_CPP_IS_BRIEF = NO
|
||||
INHERIT_DOCS = YES
|
||||
SEPARATE_MEMBER_PAGES = NO
|
||||
TAB_SIZE = 2
|
||||
ALIASES += "rst=\verbatim embed:rst:leading-asterisk"
|
||||
ALIASES += "endrst=\endverbatim"
|
||||
OPTIMIZE_OUTPUT_FOR_C = NO
|
||||
OPTIMIZE_OUTPUT_JAVA = NO
|
||||
OPTIMIZE_FOR_FORTRAN = NO
|
||||
OPTIMIZE_OUTPUT_VHDL = NO
|
||||
EXTENSION_MAPPING =
|
||||
BUILTIN_STL_SUPPORT = YES
|
||||
CPP_CLI_SUPPORT = NO
|
||||
SIP_SUPPORT = NO
|
||||
IDL_PROPERTY_SUPPORT = YES
|
||||
DISTRIBUTE_GROUP_DOC = NO
|
||||
SUBGROUPING = YES
|
||||
TYPEDEF_HIDES_STRUCT = NO
|
||||
SYMBOL_CACHE_SIZE = 0
|
||||
|
||||
MARKDOWN_SUPPORT = YES
|
||||
AUTOLINK_SUPPORT = YES
|
||||
#---------------------------------------------------------------------------
|
||||
# Build related configuration options
|
||||
#---------------------------------------------------------------------------
|
||||
EXTRACT_ALL = YES
|
||||
EXTRACT_PRIVATE = NO
|
||||
EXTRACT_STATIC = YES
|
||||
EXTRACT_LOCAL_CLASSES = NO
|
||||
EXTRACT_LOCAL_METHODS = NO
|
||||
EXTRACT_ANON_NSPACES = NO
|
||||
HIDE_UNDOC_MEMBERS = NO
|
||||
HIDE_UNDOC_CLASSES = NO
|
||||
HIDE_FRIEND_COMPOUNDS = NO
|
||||
HIDE_IN_BODY_DOCS = NO
|
||||
INTERNAL_DOCS = NO
|
||||
CASE_SENSE_NAMES = YES
|
||||
HIDE_SCOPE_NAMES = NO
|
||||
SHOW_INCLUDE_FILES = YES
|
||||
INLINE_INFO = YES
|
||||
SORT_MEMBER_DOCS = YES
|
||||
SORT_BRIEF_DOCS = NO
|
||||
SORT_MEMBERS_CTORS_1ST = NO
|
||||
SORT_GROUP_NAMES = NO
|
||||
SORT_BY_SCOPE_NAME = NO
|
||||
GENERATE_TODOLIST = YES
|
||||
GENERATE_TESTLIST = YES
|
||||
GENERATE_BUGLIST = YES
|
||||
GENERATE_DEPRECATEDLIST= YES
|
||||
ENABLED_SECTIONS =
|
||||
MAX_INITIALIZER_LINES = 30
|
||||
SHOW_USED_FILES = YES
|
||||
SHOW_DIRECTORIES = NO
|
||||
SHOW_FILES = YES
|
||||
SHOW_NAMESPACES = YES
|
||||
FILE_VERSION_FILTER =
|
||||
LAYOUT_FILE =
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to warning and progress messages
|
||||
#---------------------------------------------------------------------------
|
||||
QUIET = NO
|
||||
WARNINGS = YES
|
||||
WARN_IF_UNDOCUMENTED = YES
|
||||
WARN_IF_DOC_ERROR = YES
|
||||
WARN_NO_PARAMDOC = NO
|
||||
WARN_FORMAT = "$file:$line: $text"
|
||||
WARN_LOGFILE =
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the input files
|
||||
#---------------------------------------------------------------------------
|
||||
INPUT = @CMAKE_CURRENT_SOURCE_DIR@/doc @CMAKE_CURRENT_SOURCE_DIR@/include
|
||||
INPUT_ENCODING = UTF-8
|
||||
FILE_PATTERNS = *.md *.h *.hh *.hpp *.hxx *.i *.ii *.ipp *.ixx *.impl *.c *.cc *.cpp *.cxx
|
||||
RECURSIVE = YES
|
||||
EXCLUDE =
|
||||
EXCLUDE_SYMLINKS = NO
|
||||
EXCLUDE_PATTERNS = gh-pages
|
||||
EXCLUDE_SYMBOLS =
|
||||
EXAMPLE_PATH =
|
||||
EXAMPLE_PATTERNS =
|
||||
EXAMPLE_RECURSIVE = NO
|
||||
IMAGE_PATH =
|
||||
INPUT_FILTER =
|
||||
FILTER_PATTERNS =
|
||||
FILTER_SOURCE_FILES = NO
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to source browsing
|
||||
#---------------------------------------------------------------------------
|
||||
SOURCE_BROWSER = YES
|
||||
INLINE_SOURCES = NO
|
||||
STRIP_CODE_COMMENTS = YES
|
||||
REFERENCED_BY_RELATION = NO
|
||||
REFERENCES_RELATION = NO
|
||||
REFERENCES_LINK_SOURCE = YES
|
||||
USE_HTAGS = NO
|
||||
VERBATIM_HEADERS = YES
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the alphabetical class index
|
||||
#---------------------------------------------------------------------------
|
||||
ALPHABETICAL_INDEX = NO
|
||||
COLS_IN_ALPHA_INDEX = 5
|
||||
IGNORE_PREFIX =
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the HTML output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_HTML = YES
|
||||
HTML_OUTPUT = html
|
||||
HTML_FILE_EXTENSION = .html
|
||||
HTML_HEADER =
|
||||
HTML_FOOTER =
|
||||
HTML_TIMESTAMP = NO
|
||||
HTML_STYLESHEET =
|
||||
HTML_ALIGN_MEMBERS = YES
|
||||
HTML_DYNAMIC_SECTIONS = NO
|
||||
GENERATE_DOCSET = NO
|
||||
DOCSET_FEEDNAME = "Doxygen generated docs"
|
||||
DOCSET_BUNDLE_ID = org.doxygen.Project
|
||||
GENERATE_HTMLHELP = NO
|
||||
CHM_FILE =
|
||||
HHC_LOCATION =
|
||||
GENERATE_CHI = NO
|
||||
CHM_INDEX_ENCODING =
|
||||
BINARY_TOC = NO
|
||||
TOC_EXPAND = NO
|
||||
GENERATE_QHP = NO
|
||||
QCH_FILE =
|
||||
QHP_NAMESPACE =
|
||||
QHP_VIRTUAL_FOLDER = doc
|
||||
QHP_CUST_FILTER_NAME =
|
||||
QHP_CUST_FILTER_ATTRS =
|
||||
QHP_SECT_FILTER_ATTRS =
|
||||
QHG_LOCATION =
|
||||
DISABLE_INDEX = NO
|
||||
ENUM_VALUES_PER_LINE = 4
|
||||
GENERATE_TREEVIEW = NO
|
||||
USE_INLINE_TREES = NO
|
||||
TREEVIEW_WIDTH = 250
|
||||
FORMULA_FONTSIZE = 10
|
||||
SEARCHENGINE = YES
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the LaTeX output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_LATEX = NO
|
||||
LATEX_OUTPUT = latex
|
||||
LATEX_CMD_NAME = latex
|
||||
MAKEINDEX_CMD_NAME = makeindex
|
||||
COMPACT_LATEX = NO
|
||||
PAPER_TYPE = a4wide
|
||||
EXTRA_PACKAGES =
|
||||
LATEX_HEADER =
|
||||
PDF_HYPERLINKS = YES
|
||||
USE_PDFLATEX = YES
|
||||
LATEX_BATCHMODE = NO
|
||||
LATEX_HIDE_INDICES = NO
|
||||
LATEX_SOURCE_CODE = NO
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the RTF output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_RTF = NO
|
||||
RTF_OUTPUT = rtf
|
||||
COMPACT_RTF = NO
|
||||
RTF_HYPERLINKS = NO
|
||||
RTF_STYLESHEET_FILE =
|
||||
RTF_EXTENSIONS_FILE =
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the man page output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_MAN = NO
|
||||
MAN_OUTPUT = man
|
||||
MAN_EXTENSION = .3
|
||||
MAN_LINKS = NO
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the XML output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_XML = YES
|
||||
XML_OUTPUT = xml
|
||||
XML_SCHEMA =
|
||||
XML_DTD =
|
||||
XML_PROGRAMLISTING = YES
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options for the AutoGen Definitions output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_AUTOGEN_DEF = NO
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the Perl module output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_PERLMOD = NO
|
||||
PERLMOD_LATEX = NO
|
||||
PERLMOD_PRETTY = YES
|
||||
PERLMOD_MAKEVAR_PREFIX =
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the preprocessor
|
||||
#---------------------------------------------------------------------------
|
||||
ENABLE_PREPROCESSING = YES
|
||||
MACRO_EXPANSION = NO
|
||||
EXPAND_ONLY_PREDEF = NO
|
||||
SEARCH_INCLUDES = YES
|
||||
INCLUDE_PATH =
|
||||
INCLUDE_FILE_PATTERNS =
|
||||
PREDEFINED = __unix__
|
||||
EXPAND_AS_DEFINED =
|
||||
SKIP_FUNCTION_MACROS = YES
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration::additions related to external references
|
||||
#---------------------------------------------------------------------------
|
||||
TAGFILES =
|
||||
TAGFILES += "@CMAKE_CURRENT_SOURCE_DIR@/doc/cppreference-doxygen-web.tag.xml=http://en.cppreference.com/w/"
|
||||
GENERATE_TAGFILE =
|
||||
ALLEXTERNALS = NO
|
||||
EXTERNAL_GROUPS = YES
|
||||
PERL_PATH = @PERL_EXE@
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the dot tool
|
||||
#---------------------------------------------------------------------------
|
||||
CLASS_DIAGRAMS = YES
|
||||
MSCGEN_PATH =
|
||||
HIDE_UNDOC_RELATIONS = YES
|
||||
HAVE_DOT = NO
|
||||
DOT_FONTNAME = FreeSans
|
||||
DOT_FONTSIZE = 10
|
||||
DOT_FONTPATH =
|
||||
CLASS_GRAPH = YES
|
||||
COLLABORATION_GRAPH = YES
|
||||
GROUP_GRAPHS = YES
|
||||
UML_LOOK = NO
|
||||
TEMPLATE_RELATIONS = NO
|
||||
INCLUDE_GRAPH = YES
|
||||
INCLUDED_BY_GRAPH = YES
|
||||
CALL_GRAPH = YES
|
||||
CALLER_GRAPH = NO
|
||||
GRAPHICAL_HIERARCHY = YES
|
||||
DIRECTORY_GRAPH = YES
|
||||
DOT_IMAGE_FORMAT = png
|
||||
DOT_PATH =
|
||||
DOTFILE_DIRS =
|
||||
DOT_GRAPH_MAX_NODES = 50
|
||||
MAX_DOT_GRAPH_DEPTH = 0
|
||||
DOT_TRANSPARENT = NO
|
||||
DOT_MULTI_TARGETS = NO
|
||||
GENERATE_LEGEND = YES
|
||||
DOT_CLEANUP = YES
|
165
3party/argagg/doc/root.md
Normal file
165
3party/argagg/doc/root.md
Normal file
@ -0,0 +1,165 @@
|
||||
Argument Aggregator {#mainpage}
|
||||
===================
|
||||
|
||||
This is the Doxygen documentation for Argument Aggregator, a simple C++11 argument parser.
|
||||
|
||||
To use just create an argagg::parser object. However, the struct doesn't provide any explicit methods for defining flags. Instead we define the flags using initialization lists.
|
||||
|
||||
argagg::parser argparser {{
|
||||
{ "help", {"-h", "--help"},
|
||||
"shows this help message", 0},
|
||||
{ "delim", {"-d", "--delim"},
|
||||
"delimiter (default: ,)", 1},
|
||||
{ "num", {"-n", "--num"},
|
||||
"number", 1},
|
||||
}};
|
||||
|
||||
An option is specified by four things: the name of the option, the strings that activate the option (flags), the option's help message, and the number of arguments the option expects.
|
||||
|
||||
With the parser defined you actually parse the arguments by calling the argagg::parser::parse() method. If there are any problems then an exception is thrown.
|
||||
|
||||
argagg::parser_results args;
|
||||
try {
|
||||
args = argparser.parse(argc, argv);
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << e.what() << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
You can check if an option shows up in the command line arguments by accessing the option by name from the parser results and using the implicit boolean conversion. You can write out a simplistic option help message by streaming the argagg::parser instance itself.
|
||||
|
||||
if (args["help"]) {
|
||||
std::cerr << argparser;
|
||||
// -h, --help
|
||||
// shows this help message
|
||||
// -d, --delim
|
||||
// delimiter (default: ,)
|
||||
// -n, --num
|
||||
// number
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
That help message is only for the flags. If you want a usage message it's up to you to provide it.
|
||||
|
||||
if (args["help"]) {
|
||||
std::cerr << "Usage: program [options] ARG1 ARG2" << std::endl
|
||||
<< argparser;
|
||||
// Usage: program [options] ARG1 ARG2
|
||||
// -h, --help
|
||||
// shows this help message
|
||||
// -d, --delim
|
||||
// delimiter (default: ,)
|
||||
// -n, --num
|
||||
// number
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
A special output stream, argagg::fmt_ostream, is provided that will run the usage and help through `fmt` for nice word wrapping (see `./examples/joinargs.cpp` for a better example).
|
||||
|
||||
if (args["help"]) {
|
||||
argagg::fmt_ostream fmt(std::cerr);
|
||||
fmt << "Usage: program [options] ARG1 ARG2" << std::endl
|
||||
<< argparser;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
Generally argagg tries to do a minimal amount of work to leave most of the control with the user.
|
||||
|
||||
If you want to get an option argument but fallback on a default value if it doesn't exist then you can use the argagg::option_results::as() API and provide a default value.
|
||||
|
||||
auto delim = args["delim"].as<std::string>(",");
|
||||
|
||||
If you don't mind being implicit an implicit conversion operator is provided allowing you to write simple assignments.
|
||||
|
||||
int x = 0;
|
||||
if (args["num"]) {
|
||||
x = args["num"];
|
||||
}
|
||||
|
||||
Finally, you can get all of the positional arguments as an std::vector using the argagg::parser_results::pos member. You can alternatively convert individual positional arguments using the same conversion functions as the option argument conversion methods.
|
||||
|
||||
auto y = 0.0;
|
||||
if (args.pos.size() > 0) {
|
||||
y = args.as<double>(0);
|
||||
}
|
||||
|
||||
One can also specify `--` on the command line in order to treat all following arguments as not options.
|
||||
|
||||
For a more detailed treatment take a look at the examples or test cases.
|
||||
|
||||
Mental Model
|
||||
------------
|
||||
|
||||
The parser just returns a structure of pointers to the C-strings in the original `argv` array. The @ref argagg::parser::parse() method returns a @ref argagg::parser_results object which has two things: position arguments and option results. The position arguments are just a @ref std::vector of `const char*`. The option results are a mapping from option name (@ref std::string) to @ref argagg::option_results objects. The @ref argagg::option_results objects are just an @ref std::vector of @ref argagg::option_result objects. Each instance of an @ref argagg::option_result represents the option showing up on the command line. If there was an argument associated with it then the @ref argagg::option_result::arg member will *not* be `nullptr`.
|
||||
|
||||
Consider the following command:
|
||||
|
||||
gcc -g -I/usr/local/include -I. -o test main.o foo.o -L/usr/local/lib -lz bar.o -lpng
|
||||
|
||||
This would produce a structure like follows, written in psuedo-YAML, where each string is actually a `const char*` pointing to some part of a string in the original `argv` array:
|
||||
|
||||
parser_results:
|
||||
program: "gcc"
|
||||
pos: ["main.o", "foo.o", "bar.o"]
|
||||
options:
|
||||
version:
|
||||
debug:
|
||||
all:
|
||||
- arg: null
|
||||
include_path:
|
||||
all:
|
||||
- arg: "/usr/local/include"
|
||||
- arg: "."
|
||||
library_path:
|
||||
all:
|
||||
- arg: "/usr/local/lib"
|
||||
library:
|
||||
all:
|
||||
- arg: "z"
|
||||
- arg: "png"
|
||||
output:
|
||||
all:
|
||||
- arg: "test"
|
||||
|
||||
Conversion to types occurs at the very end when the `as<T>()` API is used. Up to that point `argagg` is just dealing with C-strings.
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
There is just a single header file (`argagg.hpp`) so you can copy that whereever you want. If you want to properly install it you can use the CMake script. The CMake script exists primarily to build the tests and documentation, but an install target for the header is provided.
|
||||
|
||||
The standard installation dance using CMake and `make` is as follows:
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_INSTALL_PREFIX=/usr/local ..
|
||||
make install
|
||||
ctest -V # optionally run tests
|
||||
|
||||
Override [`CMAKE_INSTALL_PREFIX`](https://cmake.org/cmake/help/v2.8.12/cmake.html#variable:CMAKE_INSTALL_PREFIX) to change the installation location. By default (on UNIX variants) it will install to `/usr/local` resulting in the header being copied to `/usr/local/include/argagg/argagg.hpp`.
|
||||
|
||||
If you have [Doxygen](http://www.stack.nl/~dimitri/doxygen/) it should build and install documentation as well.
|
||||
|
||||
There are no dependencies other than the standard library.
|
||||
|
||||
Edge Cases
|
||||
----------
|
||||
|
||||
There are some interesting edge cases that show up in option parsing. I used the behavior of `gcc` as my target reference in these cases.
|
||||
|
||||
### Greedy Arguments
|
||||
|
||||
Remember that options that require arguments will greedily process arguments.
|
||||
|
||||
Say we have the following options: `-a`, `-b`, `-c`, and `-o`. They all don't accept arguments except `-o`. Below is a list of permutations for short flag grouping and the results:
|
||||
|
||||
- `-abco foo`: `-o`'s argument is `foo`
|
||||
- `-aboc foo`: `-o`'s argument is `c`, `foo` is a positional argument
|
||||
- `-aobc foo`: `-o`'s argument is `bc`, `foo` is a positional argument
|
||||
- `-oabc foo`: `-o`'s argument is `abc`, `foo` is a positional argument
|
||||
|
||||
For whitespace delimited arguments the greedy processing means the next argument element (in `argv`) will be treated as an argument for the previous option, regardless of whether or not it looks like a flag or some other special entry. That means you get behavior like below:
|
||||
|
||||
- `--output=foo -- --bar`: `--output`'s argument is `foo`, `--bar` is a positional argument
|
||||
- `--output -- --bar`: `--output`'s argument is `--`, `--bar` is treated as a flag
|
||||
- `--output --bar`: `--output`'s argument is `--bar`
|
145
3party/argagg/examples/gengetopt_main1.cpp
Normal file
145
3party/argagg/examples/gengetopt_main1.cpp
Normal file
@ -0,0 +1,145 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief
|
||||
* This example is an adaptation of the example that can be found in the
|
||||
* gengetopt documentation:
|
||||
* https://www.gnu.org/software/gengetopt/gengetopt.html. It does not reach
|
||||
* feature parity because argagg does not implement dependent options, option
|
||||
* sections, word wrapping, and a few other features.
|
||||
*/
|
||||
#include <argagg/argagg.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
using argagg::parser_results;
|
||||
using argagg::parser;
|
||||
using std::cerr;
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
using std::ofstream;
|
||||
using std::ostream;
|
||||
using std::ostringstream;
|
||||
using std::string;
|
||||
|
||||
parser argparser {{
|
||||
{
|
||||
"help", {"-h", "--help"},
|
||||
"Print help and exit", 0},
|
||||
{
|
||||
"version", {"-V", "--version"},
|
||||
"Print version and exit", 0},
|
||||
{
|
||||
"str-opt", {"-s", "--str-opt"},
|
||||
"A string option, for a filename", 1},
|
||||
{
|
||||
"my-opt", {"-m", "--my-opt"},
|
||||
"Another integer option", 1},
|
||||
{
|
||||
"int-opt", {"-i", "--int-opt"},
|
||||
"A int option", 1},
|
||||
{
|
||||
"flag-opt", {"--flag-opt"},
|
||||
"A flag option (default: off)", 0},
|
||||
{
|
||||
"funct-opt", {"-F", "--funct-opt"},
|
||||
"A function option", 0},
|
||||
{
|
||||
"long-opt", {"--long-opt"},
|
||||
"A long option", 1},
|
||||
{
|
||||
"def-opt", {"--def-opt"},
|
||||
"A string option with default (default: 'Hello')", 1},
|
||||
{
|
||||
"enum-opt", {"--enum-opt"},
|
||||
"A string option with list of values (possible values=\"foo\", "
|
||||
"\"bar\", \"hello\", \"bye\"; default=\"hello\")", 1},
|
||||
{
|
||||
"dependant", {"--dependant"},
|
||||
"option that depends on str-opt", 1},
|
||||
}};
|
||||
|
||||
cout << "This one is from a C++ program" << endl ;
|
||||
cout << "Try to launch me with some options" << endl ;
|
||||
cout << "(type " << argv[0] << " --help for the complete list)" << endl ;
|
||||
cout << "For example: " << argv[0] << " *.* --funct-opt" << endl ;
|
||||
|
||||
// Define our usage text.
|
||||
ostringstream usage;
|
||||
usage
|
||||
<< argv[0] << " 2.0" << endl
|
||||
<< endl
|
||||
<< "Usage: " << argv[0] << " [OPTIONS]... [FILES]..." << endl
|
||||
<< endl;
|
||||
|
||||
// Use our argument parser to... parse the command line arguments. If there
|
||||
// are any problems then just spit out the usage and help text and exit.
|
||||
argagg::parser_results args;
|
||||
try {
|
||||
args = argparser.parse(argc, argv);
|
||||
} catch (const std::exception& e) {
|
||||
argagg::fmt_ostream fmt(cerr);
|
||||
fmt << usage.str() << argparser << endl
|
||||
<< "Encountered exception while parsing arguments: " << e.what()
|
||||
<< endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// If the help flag was specified then spit out the usage and help text and
|
||||
// exit.
|
||||
if (args["help"]) {
|
||||
argagg::fmt_ostream fmt(cerr);
|
||||
fmt << usage.str() << argparser;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
if (args["version"]) {
|
||||
cerr << "2.0" << endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
if (!args["int-opt"]) {
|
||||
cerr << args.program << ": '--int-opt' ('-i') option required" << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
cout << "Here are the options you passed..." << endl;
|
||||
|
||||
for (const auto& file : args.pos) {
|
||||
cout << "file: " << file << endl;
|
||||
}
|
||||
|
||||
if (args["funct-opt"]) {
|
||||
cout << "You chose --funct-opt or -F." << endl;
|
||||
}
|
||||
|
||||
if (args["str-opt"]) {
|
||||
cout << "You inserted " << args["str-opt"].as<std::string>() << " for " <<
|
||||
"--str-opt option." << endl;
|
||||
}
|
||||
|
||||
if (args["int-opt"]) {
|
||||
cout << "This is the integer you input: " << args["int-opt"].as<int>()
|
||||
<< "." << endl;
|
||||
}
|
||||
|
||||
string flag_opt_arg = "off";
|
||||
if (args["flag-opt"]) {
|
||||
cout << "The flag option was given!" << endl;
|
||||
flag_opt_arg = "on";
|
||||
}
|
||||
cout << "The flag is " << flag_opt_arg << "." << endl;
|
||||
|
||||
if (args["enum-opt"]) {
|
||||
cout << "enum-opt value: " << args["enum-opt"].as<string>() << endl;
|
||||
cout << "enum-opt (original specified) value: " << "hello" << endl;
|
||||
}
|
||||
|
||||
cout << "def_opt: " << args["def-opt"].as<string>("Hello") << "! ";
|
||||
|
||||
cout << "Have a nice day! :-)" << endl;
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
145
3party/argagg/examples/joinargs.cpp
Normal file
145
3party/argagg/examples/joinargs.cpp
Normal file
@ -0,0 +1,145 @@
|
||||
#include <argagg/argagg.hpp>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
static std::ofstream g_dev_null;
|
||||
|
||||
int main(
|
||||
int argc,
|
||||
const char** argv)
|
||||
{
|
||||
using argagg::parser_results;
|
||||
using argagg::parser;
|
||||
using std::cerr;
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
using std::ofstream;
|
||||
using std::ostream;
|
||||
using std::ostringstream;
|
||||
using std::string;
|
||||
|
||||
// Use an initializer list to define the argument parser. The first brace
|
||||
// starts the initializer list, the second brace starts the initializer list
|
||||
// for the definitions vector in the argagg::parser struct.
|
||||
parser argparser {{
|
||||
|
||||
// Each entry here is an initializer list for an `argagg::definition`
|
||||
// struct.
|
||||
{
|
||||
// Name of the option. This is the key used to retrieve the flag parser
|
||||
// results.
|
||||
"help",
|
||||
|
||||
// The strings ("flags") that must be matched to activate this option.
|
||||
{"-h", "--help"},
|
||||
|
||||
// The help string that is streamed out when the argagg::parser object
|
||||
// itself is streamed out.
|
||||
"displays help information",
|
||||
|
||||
// Number of arguments needed by this option. Should be 0 or 1.
|
||||
0},
|
||||
{
|
||||
"verbose", {"-v", "--verbose"},
|
||||
"increases verbosity", 0},
|
||||
{
|
||||
"lorem-ipsum", {"--lorem-ipsum"},
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do "
|
||||
"eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim "
|
||||
"ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut "
|
||||
"aliquip ex ea commodo consequat. Duis aute irure dolor in "
|
||||
"reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla "
|
||||
"pariatur. Excepteur sint occaecat cupidatat non proident, sunt in "
|
||||
"culpa qui officia deserunt mollit anim id est laborum.", 0},
|
||||
{
|
||||
"sep", {"-s", "--sep"},
|
||||
"separator (default ',')", 1},
|
||||
{
|
||||
"output", {"-o", "--output"},
|
||||
"output filename (stdout if not specified)", 1},
|
||||
}};
|
||||
|
||||
// Define our usage text.
|
||||
ostringstream usage;
|
||||
usage
|
||||
<< "Joins all positional arguments together with a separator" << endl
|
||||
<< endl
|
||||
<< "Usage: " << argv[0] << " [options] ARG [ARG...]" << endl
|
||||
<< endl;
|
||||
|
||||
// Use our argument parser to... parse the command line arguments. If there
|
||||
// are any problems then just spit out the usage and help text and exit.
|
||||
argagg::parser_results args;
|
||||
try {
|
||||
args = argparser.parse(argc, argv);
|
||||
} catch (const std::exception& e) {
|
||||
argagg::fmt_ostream help(cerr);
|
||||
help << usage.str() << argparser << endl
|
||||
<< "Encountered exception while parsing arguments: " << e.what()
|
||||
<< endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// If the help flag was specified then spit out the usage and help text and
|
||||
// exit.
|
||||
if (args["help"]) {
|
||||
argagg::fmt_ostream help(cerr);
|
||||
help << usage.str() << argparser;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
// Respect verbosity. Okay, the logging here is a little ludicrous. The point
|
||||
// I want to show here is that you can quickly get the number of times an
|
||||
// option shows up.
|
||||
int verbose_level = args["verbose"].count();
|
||||
|
||||
// Set up our verbose log output stream selector that selects stderr if the
|
||||
// requested log level is lower than or equal to the currently set verbose
|
||||
// level.
|
||||
g_dev_null.open("/dev/null"); // portable? eh... simple? yes!
|
||||
auto vlog = [&](int level) -> ostream& {
|
||||
return verbose_level >= level ? cerr : g_dev_null;
|
||||
};
|
||||
|
||||
vlog(1) << "verbose log level: " << verbose_level << endl;
|
||||
|
||||
// Use comma as the separator unless one was specified.
|
||||
auto sep = args["sep"].as<string>(",");
|
||||
vlog(1) << "set separator to '" << sep << "'" << endl;
|
||||
|
||||
// Determine output stream.
|
||||
ofstream output_file;
|
||||
ostream* output = &std::cout;
|
||||
if (args["output"]) {
|
||||
string filename = args["output"];
|
||||
output_file.open(filename);
|
||||
output = &output_file;
|
||||
vlog(1) << "outputting to file at '" << filename << "'" << endl;
|
||||
} else {
|
||||
vlog(1) << "outputting to stdout" << endl;
|
||||
}
|
||||
|
||||
// Join the arguments.
|
||||
if (args.count() < 1) {
|
||||
vlog(0) << usage.str() << argparser << endl
|
||||
<< "Not enough arguments" << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
for (auto& arg : args.pos) {
|
||||
vlog(2) << "writing argument" << endl;
|
||||
vlog(4) << "argument is '" << arg << "'" << endl;
|
||||
*output << arg;
|
||||
if (arg != args.pos.back()) {
|
||||
vlog(3) << "writing separator" << endl;
|
||||
*output << sep;
|
||||
}
|
||||
}
|
||||
vlog(4) << "writing endl" << endl;
|
||||
*output << endl;
|
||||
|
||||
vlog(4) << "everything a-okay" << endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
1548
3party/argagg/include/argagg/argagg.hpp
Normal file
1548
3party/argagg/include/argagg/argagg.hpp
Normal file
File diff suppressed because it is too large
Load Diff
101
3party/argagg/packaging/rpm/argagg.spec
Normal file
101
3party/argagg/packaging/rpm/argagg.spec
Normal file
@ -0,0 +1,101 @@
|
||||
%global debug_package %{nil}
|
||||
|
||||
Name: argagg
|
||||
Version: 0.4.6
|
||||
Release: 1%{?dist}
|
||||
Summary: Simple C++ command line argument/option parser
|
||||
|
||||
License: MIT
|
||||
URL: https://github.com/vietjtnguyen/argagg/
|
||||
Source0: https://github.com/vietjtnguyen/argagg/archive/%{version}.tar.gz#/%{name}-%{version}.tar.gz
|
||||
|
||||
BuildRequires: cmake
|
||||
BuildRequires: doxygen
|
||||
|
||||
%description
|
||||
This is yet another C++ command line argument/option parser. It was written as
|
||||
a simple and idiomatic alternative to other frameworks like getopt, Boost
|
||||
program options, TCLAP, and others. The goal is to achieve the majority of
|
||||
argument parsing needs in a simple manner with an easy to use API. It operates
|
||||
as a single pass over all arguments, recognizing flags prefixed by - (short) or
|
||||
-- (long) and aggregating them into easy to access structures with lots of
|
||||
convenience functions. It defers processing types until you access them, so the
|
||||
result structures end up just being pointers into the original command line
|
||||
argument C-strings. argagg supports POSIX recommended argument syntax
|
||||
conventions.
|
||||
|
||||
%package devel
|
||||
Summary: Development files for %{name}
|
||||
|
||||
%description devel
|
||||
The %{name}-devel package contains the header files for developing applications
|
||||
that use %{name}.
|
||||
|
||||
%package doc
|
||||
Summary: Developer documentation for %{name}
|
||||
|
||||
%description doc
|
||||
The %{name}-doc package contains the documentation for developing applications
|
||||
that use %{name}.
|
||||
|
||||
%prep
|
||||
%setup -q
|
||||
|
||||
%build
|
||||
%cmake
|
||||
make %{?_smp_mflags}
|
||||
|
||||
%install
|
||||
%make_install
|
||||
|
||||
%check
|
||||
ctest -V %{?_smp_mflags}
|
||||
|
||||
%files
|
||||
|
||||
%files devel
|
||||
%{_includedir}/*
|
||||
|
||||
%files doc
|
||||
%doc %{_datadir}/doc/%{name}
|
||||
|
||||
%changelog
|
||||
* Fri May 26 2017 Viet The Nguyen <vietjtnguyen@gmail.com>
|
||||
- Updated version to 0.4.6
|
||||
|
||||
* Fri Apr 28 2017 Viet The Nguyen <vietjtnguyen@gmail.com>
|
||||
- Updated version to 0.4.5
|
||||
|
||||
* Wed Apr 25 2017 Viet The Nguyen <vietjtnguyen@gmail.com>
|
||||
- Updated version to 0.4.4
|
||||
|
||||
* Tue Apr 25 2017 Viet The Nguyen <vietjtnguyen@gmail.com>
|
||||
- Updated version to 0.4.3
|
||||
|
||||
* Tue Apr 25 2017 Viet The Nguyen <vietjtnguyen@gmail.com>
|
||||
- Updated version to 0.4.2
|
||||
|
||||
* Sun Mar 05 2017 Viet The Nguyen <vietjtnguyen@gmail.com>
|
||||
- Updated description
|
||||
- Remove dependence on empty root package
|
||||
|
||||
* Sun Feb 19 2017 Viet The Nguyen <vietjtnguyen@gmail.com>
|
||||
- Disabled creation of debuginfo package
|
||||
|
||||
* Mon Feb 13 2017 Viet The Nguyen <vietjtnguyen@gmail.com>
|
||||
- Fixed License field and doc subpackage description typo
|
||||
|
||||
* Sat Feb 11 2017 Viet The Nguyen <vietjtnguyen@gmail.com>
|
||||
- Updated version to 0.2.2
|
||||
|
||||
* Fri Feb 10 2017 Viet The Nguyen <vietjtnguyen@gmail.com>
|
||||
- Separated documentation into a separate package
|
||||
|
||||
* Fri Feb 10 2017 Viet The Nguyen <vietjtnguyen@gmail.com>
|
||||
- Packaged version 0.2.1
|
||||
|
||||
* Mon Jan 30 2017 Viet The Nguyen <vietjtnguyen@gmail.com>
|
||||
- Added missing files specification for empty parent package
|
||||
|
||||
* Sun Jan 29 2017 Viet The Nguyen <vietjtnguyen@gmail.com>
|
||||
- Initial packaging
|
3461
3party/argagg/test/doctest.h
Normal file
3461
3party/argagg/test/doctest.h
Normal file
File diff suppressed because it is too large
Load Diff
986
3party/argagg/test/test.cpp
Normal file
986
3party/argagg/test/test.cpp
Normal file
@ -0,0 +1,986 @@
|
||||
#include "../include/argagg/argagg.hpp"
|
||||
|
||||
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
|
||||
#include "doctest.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
|
||||
TEST_CASE("cmd_line_arg_is_option_flag")
|
||||
{
|
||||
CHECK(argagg::cmd_line_arg_is_option_flag("") == false);
|
||||
CHECK(argagg::cmd_line_arg_is_option_flag("a") == false);
|
||||
CHECK(argagg::cmd_line_arg_is_option_flag("abc") == false);
|
||||
CHECK(argagg::cmd_line_arg_is_option_flag("-") == false);
|
||||
CHECK(argagg::cmd_line_arg_is_option_flag("-a") == true);
|
||||
CHECK(argagg::cmd_line_arg_is_option_flag("-abc") == true);
|
||||
CHECK(argagg::cmd_line_arg_is_option_flag("-I/usr/local/include") == true);
|
||||
CHECK(argagg::cmd_line_arg_is_option_flag("--") == false);
|
||||
CHECK(argagg::cmd_line_arg_is_option_flag("---a") == false);
|
||||
CHECK(argagg::cmd_line_arg_is_option_flag("--a") == true);
|
||||
CHECK(argagg::cmd_line_arg_is_option_flag("--abc") == true);
|
||||
CHECK(argagg::cmd_line_arg_is_option_flag("---abc") == false);
|
||||
CHECK(argagg::cmd_line_arg_is_option_flag("--a@b") == false);
|
||||
CHECK(argagg::cmd_line_arg_is_option_flag("--a+b") == false);
|
||||
CHECK(argagg::cmd_line_arg_is_option_flag("--foo-bar") == true);
|
||||
CHECK(argagg::cmd_line_arg_is_option_flag("--output=~/out.txt") == true);
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("is_valid_flag_definition")
|
||||
{
|
||||
CHECK(argagg::is_valid_flag_definition("") == false);
|
||||
CHECK(argagg::is_valid_flag_definition("a") == false);
|
||||
CHECK(argagg::is_valid_flag_definition("abc") == false);
|
||||
CHECK(argagg::is_valid_flag_definition("-") == false);
|
||||
CHECK(argagg::is_valid_flag_definition("-a") == true);
|
||||
CHECK(argagg::is_valid_flag_definition("-abc") == false);
|
||||
CHECK(argagg::is_valid_flag_definition("-I/usr/local/include") == false);
|
||||
CHECK(argagg::is_valid_flag_definition("--") == false);
|
||||
CHECK(argagg::is_valid_flag_definition("---a") == false);
|
||||
CHECK(argagg::is_valid_flag_definition("--a") == true);
|
||||
CHECK(argagg::is_valid_flag_definition("--abc") == true);
|
||||
CHECK(argagg::is_valid_flag_definition("---abc") == false);
|
||||
CHECK(argagg::is_valid_flag_definition("--a@b") == false);
|
||||
CHECK(argagg::is_valid_flag_definition("--a+b") == false);
|
||||
CHECK(argagg::is_valid_flag_definition("--foo-bar") == true);
|
||||
CHECK(argagg::is_valid_flag_definition("--output=~/out.txt") == false);
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("flag_is_short")
|
||||
{
|
||||
CHECK(argagg::flag_is_short("-a") == true);
|
||||
CHECK(argagg::flag_is_short("-abc") == true);
|
||||
CHECK(argagg::flag_is_short("--a") == false);
|
||||
CHECK(argagg::flag_is_short("--abc") == false);
|
||||
CHECK(argagg::flag_is_short("--a-b") == false);
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("intro example")
|
||||
{
|
||||
argagg::parser argparser {{
|
||||
{ "help", {"-h", "--help"},
|
||||
"shows this help message", 0},
|
||||
{ "delim", {"-d", "--delim"},
|
||||
"delimiter (default: ,)", 1},
|
||||
{ "num", {"-n", "--num"},
|
||||
"number", 1},
|
||||
}};
|
||||
std::vector<const char*> argv {
|
||||
"test", "3.141", "foo", "-h", "bar", "300", "-n", "100", "-d", "-", "-",
|
||||
"--", "-b", "--blah"};
|
||||
argagg::parser_results args;
|
||||
try {
|
||||
args = argparser.parse(argv.size(), &(argv.front()));
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << e.what() << std::endl;
|
||||
}
|
||||
CHECK(args.has_option("help") == true);
|
||||
CHECK(static_cast<bool>(args["help"]) == true);
|
||||
CHECK(args.has_option("delim") == true);
|
||||
CHECK(static_cast<bool>(args["delim"]) == true);
|
||||
auto delim = args["delim"].as<std::string>(",");
|
||||
CHECK(delim == "-");
|
||||
CHECK(args.has_option("num") == true);
|
||||
CHECK(static_cast<bool>(args["num"]) == true);
|
||||
int x = 0;
|
||||
if (args["num"]) {
|
||||
x = args["num"];
|
||||
}
|
||||
CHECK(x == 100);
|
||||
auto y = 0.0;
|
||||
if (args.pos.size() > 0) {
|
||||
y = args.as<double>(0);
|
||||
}
|
||||
CHECK(y == doctest::Approx(3.141));
|
||||
CHECK(args.as<std::string>(1) == "foo");
|
||||
CHECK(args.as<std::string>(2) == "bar");
|
||||
CHECK(args.as<int>(3) == 300);
|
||||
CHECK(args.as<std::string>(4) == "-");
|
||||
CHECK(args.as<std::string>(5) == "-b");
|
||||
CHECK(args.as<std::string>(6) == "--blah");
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("no definitions")
|
||||
{
|
||||
argagg::parser parser {{
|
||||
}};
|
||||
SUBCASE("no arguments") {
|
||||
std::vector<const char*> argv {
|
||||
"test"};
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
CHECK(args.has_option("help") == false);
|
||||
CHECK(args.has_option("verbose") == false);
|
||||
CHECK(args.has_option("output") == false);
|
||||
CHECK(args.count() == 0);
|
||||
}
|
||||
SUBCASE("with arguments") {
|
||||
std::vector<const char*> argv {
|
||||
"test", "foo", "bar", "baz"};
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
CHECK(args.has_option("help") == false);
|
||||
CHECK(args.has_option("verbose") == false);
|
||||
CHECK(args.has_option("output") == false);
|
||||
CHECK(args.count() == 3);
|
||||
CHECK(::std::string(args.pos[0]) == "foo");
|
||||
CHECK(::std::string(args.pos[1]) == "bar");
|
||||
CHECK(::std::string(args.pos[2]) == "baz");
|
||||
}
|
||||
SUBCASE("with flags") {
|
||||
std::vector<const char*> argv {
|
||||
"test", "--verbose", "-o", "baz"};
|
||||
CHECK_THROWS_AS({
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
}, argagg::unexpected_option_error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("invalid definitions")
|
||||
{
|
||||
std::vector<const char*> argv {
|
||||
"test"};
|
||||
SUBCASE("no flags") {
|
||||
argagg::parser parser {{
|
||||
{"bad", {}, "bad", 0},
|
||||
}};
|
||||
CHECK_THROWS_AS({
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
}, argagg::invalid_flag);
|
||||
}
|
||||
SUBCASE("too short") {
|
||||
argagg::parser parser {{
|
||||
{"bad", {"-"}, "bad", 0},
|
||||
}};
|
||||
CHECK_THROWS_AS({
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
}, argagg::invalid_flag);
|
||||
}
|
||||
SUBCASE("too short 2") {
|
||||
argagg::parser parser {{
|
||||
{"bad", {"a"}, "bad", 0},
|
||||
}};
|
||||
CHECK_THROWS_AS({
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
}, argagg::invalid_flag);
|
||||
}
|
||||
SUBCASE("no hyphen") {
|
||||
argagg::parser parser {{
|
||||
{"bad", {"bad"}, "bad", 0},
|
||||
}};
|
||||
CHECK_THROWS_AS({
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
}, argagg::invalid_flag);
|
||||
}
|
||||
SUBCASE("short flag group") {
|
||||
argagg::parser parser {{
|
||||
{"bad", {"-bad"}, "bad", 0},
|
||||
}};
|
||||
CHECK_THROWS_AS({
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
}, argagg::invalid_flag);
|
||||
}
|
||||
SUBCASE("invalid character") {
|
||||
argagg::parser parser {{
|
||||
{"bad", {"-b ad"}, "bad", 0},
|
||||
}};
|
||||
CHECK_THROWS_AS({
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
}, argagg::invalid_flag);
|
||||
}
|
||||
SUBCASE("too many hyphens") {
|
||||
argagg::parser parser {{
|
||||
{"bad", {"---bad"}, "bad", 0},
|
||||
}};
|
||||
CHECK_THROWS_AS({
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
}, argagg::invalid_flag);
|
||||
}
|
||||
SUBCASE("long flag equal assignment") {
|
||||
argagg::parser parser {{
|
||||
{"bad", {"--bad=still-bad"}, "bad", 0},
|
||||
}};
|
||||
CHECK_THROWS_AS({
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
}, argagg::invalid_flag);
|
||||
}
|
||||
SUBCASE("duplicate short flags") {
|
||||
argagg::parser parser {{
|
||||
{"bad", {"-b"}, "bad", 0},
|
||||
{"bad2", {"-b"}, "bad2", 0},
|
||||
}};
|
||||
CHECK_THROWS_AS({
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
}, argagg::invalid_flag);
|
||||
}
|
||||
SUBCASE("duplicate long flags") {
|
||||
argagg::parser parser {{
|
||||
{"bad", {"--bad"}, "bad", 0},
|
||||
{"bad2", {"--bad"}, "bad2", 0},
|
||||
}};
|
||||
CHECK_THROWS_AS({
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
}, argagg::invalid_flag);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("simple")
|
||||
{
|
||||
argagg::parser parser {{
|
||||
{"verbose", {"-v", "--verbose"}, "be verbose", 0},
|
||||
{"output", {"-o", "--output"}, "output filename", 1},
|
||||
}};
|
||||
SUBCASE("no arguments") {
|
||||
std::vector<const char*> argv {
|
||||
"test"};
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
CHECK(args.has_option("help") == false);
|
||||
CHECK(args.has_option("verbose") == false);
|
||||
CHECK(args.has_option("output") == false);
|
||||
CHECK(args.count() == 0);
|
||||
}
|
||||
SUBCASE("no flags") {
|
||||
std::vector<const char*> argv {
|
||||
"test", "foo", "bar", "baz"};
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
CHECK(args.has_option("help") == false);
|
||||
CHECK(args.has_option("verbose") == false);
|
||||
CHECK_THROWS_AS({
|
||||
args["verbose"].as<int>();
|
||||
}, std::out_of_range);
|
||||
CHECK(args["verbose"].as<int>(999) == 999);
|
||||
CHECK(args.has_option("output") == false);
|
||||
CHECK(args.count() == 3);
|
||||
CHECK(args.as<std::string>(0) == "foo");
|
||||
CHECK(args.as<std::string>(1) == "bar");
|
||||
CHECK(args.as<std::string>(2) == "baz");
|
||||
}
|
||||
SUBCASE("only flags") {
|
||||
std::vector<const char*> argv {
|
||||
"test", "--verbose", "--output", "foo", "-v", "-o", "bar"};
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
CHECK(args.has_option("help") == false);
|
||||
CHECK(args.has_option("verbose") == true);
|
||||
CHECK(args["verbose"].count() == 2);
|
||||
CHECK(args["verbose"][0].arg == nullptr);
|
||||
CHECK(args["verbose"][1].arg == nullptr);
|
||||
CHECK_THROWS_AS({
|
||||
args["verbose"][0].as<std::string>();
|
||||
}, argagg::option_lacks_argument_error);
|
||||
CHECK_THROWS_AS({
|
||||
args["verbose"][0].as<int>();
|
||||
}, argagg::option_lacks_argument_error);
|
||||
CHECK(args.has_option("output") == true);
|
||||
CHECK(args["output"].count() == 2);
|
||||
CHECK(args["output"][0].as<std::string>() == "foo");
|
||||
CHECK(args["output"][1].as<std::string>() == "bar");
|
||||
CHECK(args.count() == 0);
|
||||
}
|
||||
SUBCASE("simple mixed") {
|
||||
std::vector<const char*> argv {
|
||||
"test", "-v", "--output", "foo", "bar"};
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
CHECK(args.has_option("help") == false);
|
||||
CHECK(args.has_option("verbose") == true);
|
||||
CHECK(args["verbose"].count() == 1);
|
||||
CHECK(args["verbose"][0].arg == nullptr);
|
||||
CHECK(args.has_option("output") == true);
|
||||
CHECK(args["output"].count() == 1);
|
||||
CHECK(args["output"].as<std::string>() == "foo");
|
||||
CHECK(args.count() == 1);
|
||||
CHECK(args.as<std::string>(0) == "bar");
|
||||
}
|
||||
SUBCASE("trailing flags") {
|
||||
std::vector<const char*> argv {
|
||||
"test", "foo", "bar", "-v", "--output", "baz"};
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
CHECK(args.has_option("help") == false);
|
||||
CHECK(args.has_option("verbose") == true);
|
||||
CHECK(args["verbose"].count() == 1);
|
||||
CHECK(args["verbose"][0].arg == nullptr);
|
||||
CHECK(args.has_option("output") == true);
|
||||
CHECK(args["output"].count() == 1);
|
||||
CHECK(args["output"].as<std::string>() == "baz");
|
||||
CHECK(args.count() == 2);
|
||||
CHECK(args.as<std::string>(0) == "foo");
|
||||
CHECK(args.as<std::string>(1) == "bar");
|
||||
}
|
||||
SUBCASE("interleaved positional arguments") {
|
||||
std::vector<const char*> argv {
|
||||
"test", "foo", "-v", "bar", "--verbose", "baz", "--output", "dog", "cat"};
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
CHECK(args.has_option("help") == false);
|
||||
CHECK(args.has_option("verbose") == true);
|
||||
CHECK(args["verbose"].count() == 2);
|
||||
CHECK(args["verbose"][0].arg == nullptr);
|
||||
CHECK(args.has_option("output") == true);
|
||||
CHECK(args["output"].count() == 1);
|
||||
CHECK(args["output"].as<std::string>() == "dog");
|
||||
CHECK(args.count() == 4);
|
||||
CHECK(args.as<std::string>(0) == "foo");
|
||||
CHECK(args.as<std::string>(1) == "bar");
|
||||
CHECK(args.as<std::string>(2) == "baz");
|
||||
CHECK(args.as<std::string>(3) == "cat");
|
||||
}
|
||||
SUBCASE("unused short flag") {
|
||||
std::vector<const char*> argv {
|
||||
"test", "--output", "foo", "-h", "bar", "-v"};
|
||||
CHECK_THROWS_AS({
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
}, argagg::unexpected_option_error);
|
||||
}
|
||||
SUBCASE("unused long flag") {
|
||||
std::vector<const char*> argv {
|
||||
"test", "--output", "foo", "--help", "bar", "-v"};
|
||||
CHECK_THROWS_AS({
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
}, argagg::unexpected_option_error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("long flag equal format for arguments")
|
||||
{
|
||||
argagg::parser parser {{
|
||||
{"verbose", {"-v", "--verbose"}, "be verbose", 0},
|
||||
{"delim", {"-d", "--delim"}, "delimiter", 1},
|
||||
{"output", {"-o", "--output"}, "output", 1},
|
||||
}};
|
||||
SUBCASE("basic") {
|
||||
std::vector<const char*> argv {
|
||||
"test", "-v", "--output=foo", "--delim=bar", "baz"};
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
CHECK(args.has_option("verbose") == true);
|
||||
CHECK(args.has_option("output") == true);
|
||||
CHECK(args["output"].as<std::string>() == "foo");
|
||||
CHECK(args.has_option("delim") == true);
|
||||
CHECK(args["delim"].as<std::string>() == "bar");
|
||||
CHECK(args.count() == 1);
|
||||
CHECK(args.as<std::string>(0) == "baz");
|
||||
}
|
||||
SUBCASE("empty") {
|
||||
std::vector<const char*> argv {
|
||||
"test", "-v", "--output=", "--delim=", "baz"};
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
CHECK(args.has_option("verbose") == true);
|
||||
CHECK(args.has_option("output") == true);
|
||||
CHECK(args["output"].as<std::string>() == "");
|
||||
CHECK(args.has_option("delim") == true);
|
||||
CHECK(args["delim"].as<std::string>() == "");
|
||||
CHECK(args.count() == 1);
|
||||
CHECK(args.as<std::string>(0) == "baz");
|
||||
}
|
||||
SUBCASE("symbols") {
|
||||
std::vector<const char*> argv {
|
||||
"test", "-v", "--output=--foo!!", "--delim=,", "baz"};
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
CHECK(args.has_option("verbose") == true);
|
||||
CHECK(args.has_option("output") == true);
|
||||
CHECK(args["output"].as<std::string>() == "--foo!!");
|
||||
CHECK(args.has_option("delim") == true);
|
||||
CHECK(args["delim"].as<std::string>() == ",");
|
||||
CHECK(args.count() == 1);
|
||||
CHECK(args.as<std::string>(0) == "baz");
|
||||
}
|
||||
SUBCASE("unnecessary argument") {
|
||||
std::vector<const char*> argv {
|
||||
"test", "--verbose=bad"};
|
||||
CHECK_THROWS_AS({
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
}, argagg::unexpected_argument_error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("short flag groups")
|
||||
{
|
||||
argagg::parser parser {{
|
||||
{"verbose", {"-v", "--verbose"}, "be verbose", 0},
|
||||
{"help", {"-h", "--help"}, "help", 0},
|
||||
{"foo", {"-f", "--foo"}, "foo", 0},
|
||||
{"output", {"-o", "--output"}, "output", 1},
|
||||
}};
|
||||
SUBCASE("basic") {
|
||||
std::vector<const char*> argv {
|
||||
"test", "-vhf", "bar"};
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
CHECK(args.has_option("verbose") == true);
|
||||
CHECK(args.has_option("help") == true);
|
||||
CHECK(args.has_option("foo") == true);
|
||||
CHECK(args.has_option("output") == false);
|
||||
CHECK(args.count() == 1);
|
||||
CHECK(args.as<std::string>(0) == "bar");
|
||||
}
|
||||
SUBCASE("basic 2") {
|
||||
std::vector<const char*> argv {
|
||||
"test", "-fvh", "bar"};
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
CHECK(args.has_option("verbose") == true);
|
||||
CHECK(args.has_option("help") == true);
|
||||
CHECK(args.has_option("foo") == true);
|
||||
CHECK(args.has_option("output") == false);
|
||||
CHECK(args.count() == 1);
|
||||
CHECK(args.as<std::string>(0) == "bar");
|
||||
}
|
||||
SUBCASE("basic 3") {
|
||||
std::vector<const char*> argv {
|
||||
"test", "-fh", "-v", "bar"};
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
CHECK(args.has_option("verbose") == true);
|
||||
CHECK(args.has_option("help") == true);
|
||||
CHECK(args.has_option("foo") == true);
|
||||
CHECK(args.has_option("output") == false);
|
||||
CHECK(args.count() == 1);
|
||||
CHECK(args.as<std::string>(0) == "bar");
|
||||
}
|
||||
SUBCASE("basic 4") {
|
||||
std::vector<const char*> argv {
|
||||
"test", "--vfh", "bar"};
|
||||
CHECK_THROWS_AS({
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
}, argagg::unexpected_option_error);
|
||||
}
|
||||
SUBCASE("unexpected symbol") {
|
||||
std::vector<const char*> argv {
|
||||
"test", "-v-fh", "bar"};
|
||||
CHECK_THROWS_AS({
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
}, std::domain_error);
|
||||
}
|
||||
SUBCASE("trailing flag with argument") {
|
||||
std::vector<const char*> argv {
|
||||
"test", "-vhfo", "bar"};
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
CHECK(args.has_option("verbose") == true);
|
||||
CHECK(args.has_option("help") == true);
|
||||
CHECK(args.has_option("foo") == true);
|
||||
CHECK(args.has_option("output") == true);
|
||||
CHECK(args["output"].as<std::string>() == "bar");
|
||||
CHECK(args.count() == 0);
|
||||
}
|
||||
SUBCASE("leading flag with argument") {
|
||||
std::vector<const char*> argv {
|
||||
"test", "-ohfv", "bar"};
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
CHECK(args.has_option("verbose") == false);
|
||||
CHECK(args.has_option("help") == false);
|
||||
CHECK(args.has_option("foo") == false);
|
||||
CHECK(args.has_option("output") == true);
|
||||
CHECK(args["output"].as<std::string>() == "hfv");
|
||||
CHECK(args.count() == 1);
|
||||
CHECK(args.as<std::string>(0) == "bar");
|
||||
}
|
||||
SUBCASE("middling flag with argument") {
|
||||
std::vector<const char*> argv {
|
||||
"test", "-vfoh", "bar"};
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
CHECK(args.has_option("verbose") == true);
|
||||
CHECK(args.has_option("help") == false);
|
||||
CHECK(args.has_option("foo") == true);
|
||||
CHECK(args.has_option("output") == true);
|
||||
CHECK(args["output"].as<std::string>() == "h");
|
||||
CHECK(args.count() == 1);
|
||||
CHECK(args.as<std::string>(0) == "bar");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("flag stop")
|
||||
{
|
||||
argagg::parser parser {{
|
||||
{"verbose", {"-v", "--verbose"}, "be verbose", 0},
|
||||
{"delim", {"-d", "--delim"}, "delimiter", 1},
|
||||
}};
|
||||
SUBCASE("ignore flags after stop") {
|
||||
std::vector<const char*> argv {
|
||||
"test", "-v", "--", "bar", "--verbose", "baz",
|
||||
"--delim", "dog", "-d", "cat"};
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
CHECK(args.has_option("help") == false);
|
||||
CHECK(args.has_option("verbose") == true);
|
||||
CHECK(args["verbose"].count() == 1);
|
||||
CHECK(args["verbose"][0].arg == nullptr);
|
||||
CHECK(args.has_option("output") == false);
|
||||
CHECK(args.count() == 7);
|
||||
CHECK(args.as<std::string>(0) == "bar");
|
||||
CHECK(args.as<std::string>(1) == "--verbose");
|
||||
CHECK(args.as<std::string>(2) == "baz");
|
||||
CHECK(args.as<std::string>(3) == "--delim");
|
||||
CHECK(args.as<std::string>(4) == "dog");
|
||||
CHECK(args.as<std::string>(5) == "-d");
|
||||
CHECK(args.as<std::string>(6) == "cat");
|
||||
}
|
||||
SUBCASE("flag stop consumed as argument for option") {
|
||||
std::vector<const char*> argv {
|
||||
"test", "-d", "--", "--", "-", "boo"};
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
CHECK(args.has_option("help") == false);
|
||||
CHECK(args.has_option("verbose") == false);
|
||||
CHECK(args.has_option("delim") == true);
|
||||
CHECK(args["delim"].as<std::string>() == "--");
|
||||
CHECK(args.count() == 2);
|
||||
CHECK(args.as<std::string>(0) == "-");
|
||||
CHECK(args.as<std::string>(1) == "boo");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("option requires argument")
|
||||
{
|
||||
argagg::parser parser {{
|
||||
{"verbose", {"-v", "--verbose"}, "be verbose", 0},
|
||||
{"number", {"-n", "--number"}, "number", 1},
|
||||
}};
|
||||
SUBCASE("arguments provided") {
|
||||
std::vector<const char*> argv {
|
||||
"test", "-n", "1", "2", "-n", "4"};
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
CHECK(args.has_option("verbose") == false);
|
||||
CHECK(args.has_option("number") == true);
|
||||
CHECK(args["number"].count() == 2);
|
||||
CHECK(args["number"][0].as<int>() == 1);
|
||||
CHECK(args["number"][1].as<int>() == 4);
|
||||
CHECK(args.count() == 1);
|
||||
CHECK(args.as<int>(0) == 2);
|
||||
}
|
||||
SUBCASE("negative numbers") {
|
||||
std::vector<const char*> argv {
|
||||
"test", "-n", "-1", "-n", "-4444", "--", "-22"};
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
CHECK(args.has_option("verbose") == false);
|
||||
CHECK(args.has_option("number") == true);
|
||||
CHECK(args["number"].count() == 2);
|
||||
CHECK(args["number"][0].as<int>() == -1);
|
||||
CHECK(args["number"][1].as<int>() == -4444);
|
||||
CHECK(args.count() == 1);
|
||||
CHECK(args.as<int>(0) == -22);
|
||||
}
|
||||
SUBCASE("use flag as argument even though it is a valid flag")
|
||||
{
|
||||
std::vector<const char*> argv {
|
||||
"test", "-n", "-v"};
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
CHECK(args.has_option("number") == true);
|
||||
CHECK(args["number"].count() == 1);
|
||||
CHECK(args["number"][0].as<std::string>() == "-v");
|
||||
}
|
||||
SUBCASE("interrupted by unused flag")
|
||||
{
|
||||
std::vector<const char*> argv {
|
||||
"test", "-n", "1", "2", "-c"};
|
||||
CHECK_THROWS_AS({
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
}, argagg::unexpected_option_error);
|
||||
}
|
||||
SUBCASE("given zero, end of args") {
|
||||
std::vector<const char*> argv {
|
||||
"test", "-n"};
|
||||
CHECK_THROWS_AS({
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
}, argagg::option_lacks_argument_error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("greedy processing")
|
||||
{
|
||||
argagg::parser parser {{
|
||||
{"a", {"-a"}, "a", 0},
|
||||
{"b", {"-b", "--bar"}, "b", 0},
|
||||
{"c", {"-c"}, "c", 0},
|
||||
{"output", {"-o", "--output"}, "output", 1},
|
||||
}};
|
||||
SUBCASE("short group example 1") {
|
||||
std::vector<const char*> argv {
|
||||
"test", "-abco", "foo"};
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
CHECK(args.has_option("a") == true);
|
||||
CHECK(args.has_option("b") == true);
|
||||
CHECK(args.has_option("c") == true);
|
||||
CHECK(args.has_option("output") == true);
|
||||
CHECK(args["output"].as<std::string>() == "foo");
|
||||
CHECK(args.count() == 0);
|
||||
}
|
||||
SUBCASE("short group example 2") {
|
||||
std::vector<const char*> argv {
|
||||
"test", "-aboc", "foo"};
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
CHECK(args.has_option("a") == true);
|
||||
CHECK(args.has_option("b") == true);
|
||||
CHECK(args.has_option("c") == false);
|
||||
CHECK(args.has_option("output") == true);
|
||||
CHECK(args["output"].as<std::string>() == "c");
|
||||
CHECK(args.count() == 1);
|
||||
CHECK(args.as<std::string>(0) == "foo");
|
||||
}
|
||||
SUBCASE("short group example 3") {
|
||||
std::vector<const char*> argv {
|
||||
"test", "-aobc", "foo"};
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
CHECK(args.has_option("a") == true);
|
||||
CHECK(args.has_option("b") == false);
|
||||
CHECK(args.has_option("c") == false);
|
||||
CHECK(args.has_option("output") == true);
|
||||
CHECK(args["output"].as<std::string>() == "bc");
|
||||
CHECK(args.count() == 1);
|
||||
CHECK(args.as<std::string>(0) == "foo");
|
||||
}
|
||||
SUBCASE("short group example 4") {
|
||||
std::vector<const char*> argv {
|
||||
"test", "-oabc", "foo"};
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
CHECK(args.has_option("a") == false);
|
||||
CHECK(args.has_option("b") == false);
|
||||
CHECK(args.has_option("c") == false);
|
||||
CHECK(args.has_option("output") == true);
|
||||
CHECK(args["output"].as<std::string>() == "abc");
|
||||
CHECK(args.count() == 1);
|
||||
CHECK(args.as<std::string>(0) == "foo");
|
||||
}
|
||||
SUBCASE("long example 1") {
|
||||
std::vector<const char*> argv {
|
||||
"test", "--output=foo", "--", "--bar"};
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
CHECK(args.has_option("a") == false);
|
||||
CHECK(args.has_option("b") == false);
|
||||
CHECK(args.has_option("c") == false);
|
||||
CHECK(args.has_option("output") == true);
|
||||
CHECK(args["output"].as<std::string>() == "foo");
|
||||
CHECK(args.count() == 1);
|
||||
CHECK(args.as<std::string>(0) == "--bar");
|
||||
}
|
||||
SUBCASE("long example 2") {
|
||||
std::vector<const char*> argv {
|
||||
"test", "--output", "--", "--bar"};
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
CHECK(args.has_option("a") == false);
|
||||
CHECK(args.has_option("b") == true);
|
||||
CHECK(args.has_option("c") == false);
|
||||
CHECK(args.has_option("output") == true);
|
||||
CHECK(args["output"].as<std::string>() == "--");
|
||||
CHECK(args.count() == 0);
|
||||
}
|
||||
SUBCASE("long example 3") {
|
||||
std::vector<const char*> argv {
|
||||
"test", "--output", "--bar"};
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
CHECK(args.has_option("a") == false);
|
||||
CHECK(args.has_option("b") == false);
|
||||
CHECK(args.has_option("c") == false);
|
||||
CHECK(args.has_option("output") == true);
|
||||
CHECK(args["output"].as<std::string>() == "--bar");
|
||||
CHECK(args.count() == 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("gcc example")
|
||||
{
|
||||
argagg::parser parser {{
|
||||
{"verbose", {"-v", "--verbose"}, "be verbose", 0},
|
||||
{"version", {"--version"}, "print version", 0},
|
||||
{"include path", {"-I"}, "include path", 1},
|
||||
{"library path", {"-L"}, "library path", 1},
|
||||
{"library", {"-l"}, "library", 1},
|
||||
{"output", {"-o"}, "output", 1},
|
||||
}};
|
||||
SUBCASE("version") {
|
||||
std::vector<const char*> argv {
|
||||
"gcc", "--version"};
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
CHECK(args.has_option("verbose") == false);
|
||||
CHECK(args.has_option("version") == true);
|
||||
CHECK(args.has_option("include path") == false);
|
||||
CHECK(args.has_option("library path") == false);
|
||||
CHECK(args.has_option("library") == false);
|
||||
CHECK(args.has_option("output") == false);
|
||||
CHECK(args.count() == 0);
|
||||
}
|
||||
SUBCASE("simple") {
|
||||
std::vector<const char*> argv {
|
||||
"gcc", "test.c"};
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
CHECK(args.has_option("verbose") == false);
|
||||
CHECK(args.has_option("version") == false);
|
||||
CHECK(args.has_option("include path") == false);
|
||||
CHECK(args.has_option("library path") == false);
|
||||
CHECK(args.has_option("library") == false);
|
||||
CHECK(args.has_option("output") == false);
|
||||
CHECK(args.count() == 1);
|
||||
CHECK(args.as<std::string>(0) == "test.c");
|
||||
}
|
||||
SUBCASE("simple 2") {
|
||||
std::vector<const char*> argv {
|
||||
"gcc", "-I/usr/local/include", "test.c", "-otest"};
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
CHECK(args.has_option("verbose") == false);
|
||||
CHECK(args.has_option("version") == false);
|
||||
CHECK(args.has_option("include path") == true);
|
||||
CHECK(args["include path"].count() == 1);
|
||||
CHECK(args["include path"].as<std::string>() == "/usr/local/include");
|
||||
CHECK(args.has_option("library path") == false);
|
||||
CHECK(args.has_option("library") == false);
|
||||
CHECK(args.has_option("output") == true);
|
||||
CHECK(args["output"].count() == 1);
|
||||
CHECK(args["output"].as<std::string>() == "test");
|
||||
CHECK(args.count() == 1);
|
||||
CHECK(args.as<std::string>(0) == "test.c");
|
||||
}
|
||||
SUBCASE("simple 3") {
|
||||
std::vector<const char*> argv {
|
||||
"gcc", "-I/usr/local/include", "-I.", "-L/usr/local/lib", "-lz", "-lm",
|
||||
"test.c", "-otest"};
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
CHECK(args.has_option("verbose") == false);
|
||||
CHECK(args.has_option("version") == false);
|
||||
CHECK(args.has_option("include path") == true);
|
||||
CHECK(args["include path"].count() == 2);
|
||||
CHECK(args["include path"][0].as<std::string>() == "/usr/local/include");
|
||||
CHECK(args["include path"][1].as<std::string>() == ".");
|
||||
CHECK(args.has_option("library path") == true);
|
||||
CHECK(args["library path"].count() == 1);
|
||||
CHECK(args["library path"][0].as<std::string>() == "/usr/local/lib");
|
||||
CHECK(args.has_option("library") == true);
|
||||
CHECK(args["library"].count() == 2);
|
||||
CHECK(args["library"][0].as<std::string>() == "z");
|
||||
CHECK(args["library"][1].as<std::string>() == "m");
|
||||
CHECK(args.has_option("output") == true);
|
||||
CHECK(args["output"].count() == 1);
|
||||
CHECK(args["output"].as<std::string>() == "test");
|
||||
CHECK(args.count() == 1);
|
||||
CHECK(args.as<std::string>(0) == "test.c");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("argument conversions")
|
||||
{
|
||||
argagg::parser parser {{
|
||||
{"number", {"-n", "--num", "--number"}, "number", 1},
|
||||
}};
|
||||
SUBCASE("positional integer") {
|
||||
std::vector<const char*> argv {
|
||||
"test", "1", "2"};
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
CHECK(args.pos.size() == 2);
|
||||
CHECK(args.as<char>() == 1);
|
||||
CHECK(args.as<unsigned char>() == 1);
|
||||
CHECK(args.as<signed char>() == 1);
|
||||
CHECK(args.as<short>() == 1);
|
||||
CHECK(args.as<unsigned short>() == 1);
|
||||
CHECK(args.as<signed short>() == 1);
|
||||
CHECK(args.as<int>() == 1);
|
||||
CHECK(args.as<unsigned int>() == 1);
|
||||
CHECK(args.as<signed int>() == 1);
|
||||
CHECK(args.as<long>() == 1);
|
||||
CHECK(args.as<unsigned long>() == 1);
|
||||
CHECK(args.as<signed long>() == 1);
|
||||
CHECK(args.as<long long>() == 1);
|
||||
CHECK(args.as<unsigned long long>() == 1);
|
||||
CHECK(args.as<signed long long>() == 1);
|
||||
CHECK(std::strcmp(args.as<const char*>(), "1") == 0);
|
||||
CHECK(args.as<char>(1) == 2);
|
||||
CHECK(args.as<unsigned char>(1) == 2);
|
||||
CHECK(args.as<signed char>(1) == 2);
|
||||
CHECK(args.as<short>(1) == 2);
|
||||
CHECK(args.as<unsigned short>(1) == 2);
|
||||
CHECK(args.as<signed short>(1) == 2);
|
||||
CHECK(args.as<int>(1) == 2);
|
||||
CHECK(args.as<unsigned int>(1) == 2);
|
||||
CHECK(args.as<signed int>(1) == 2);
|
||||
CHECK(args.as<long>(1) == 2);
|
||||
CHECK(args.as<unsigned long>(1) == 2);
|
||||
CHECK(args.as<signed long>(1) == 2);
|
||||
CHECK(args.as<long long>(1) == 2);
|
||||
CHECK(args.as<unsigned long long>(1) == 2);
|
||||
CHECK(args.as<signed long long>(1) == 2);
|
||||
CHECK(args.as<std::string>(1) == "2");
|
||||
CHECK(std::strcmp(args.as<const char*>(1), "2") == 0);
|
||||
}
|
||||
SUBCASE("positional floating point") {
|
||||
std::vector<const char*> argv {
|
||||
"test", "3.141592653", "2.71828182846"};
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
CHECK(args.pos.size() == 2);
|
||||
CHECK(args.as<float>() == doctest::Approx(3.141592653f));
|
||||
CHECK(args.as<double>() == doctest::Approx(3.141592653));
|
||||
CHECK(args.as<std::string>() == "3.141592653");
|
||||
CHECK(args.as<float>(1) == doctest::Approx(2.71828182846f));
|
||||
CHECK(args.as<double>(1) == doctest::Approx(2.71828182846));
|
||||
CHECK(args.as<std::string>(1) == "2.71828182846");
|
||||
}
|
||||
SUBCASE("positional vector") {
|
||||
std::vector<const char*> argv {
|
||||
"test", "0", "1", "2"};
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
CHECK(args.pos.size() == 3);
|
||||
auto v = args.all_as<int>();
|
||||
CHECK(v[0] == 0);
|
||||
CHECK(v[1] == 1);
|
||||
CHECK(v[2] == 2);
|
||||
}
|
||||
SUBCASE("option integer") {
|
||||
std::vector<const char*> argv {
|
||||
"test", "-n", "1"};
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
CHECK(args.has_option("number") == true);
|
||||
CHECK(args["number"].count() == 1);
|
||||
CHECK(args["number"].as<char>() == 1);
|
||||
CHECK(args["number"].as<unsigned char>() == 1);
|
||||
CHECK(args["number"].as<signed char>() == 1);
|
||||
CHECK(args["number"].as<short>() == 1);
|
||||
CHECK(args["number"].as<unsigned short>() == 1);
|
||||
CHECK(args["number"].as<signed short>() == 1);
|
||||
CHECK(args["number"].as<int>() == 1);
|
||||
CHECK(args["number"].as<unsigned int>() == 1);
|
||||
CHECK(args["number"].as<signed int>() == 1);
|
||||
CHECK(args["number"].as<long>() == 1);
|
||||
CHECK(args["number"].as<unsigned long>() == 1);
|
||||
CHECK(args["number"].as<signed long>() == 1);
|
||||
CHECK(args["number"].as<long long>() == 1);
|
||||
CHECK(args["number"].as<unsigned long long>() == 1);
|
||||
CHECK(args["number"].as<signed long long>() == 1);
|
||||
CHECK(args["number"].as<std::string>() == "1");
|
||||
}
|
||||
SUBCASE("option floating point") {
|
||||
std::vector<const char*> argv {
|
||||
"test", "-n", "3.141592653"};
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
CHECK(args.has_option("number") == true);
|
||||
CHECK(args["number"].count() == 1);
|
||||
CHECK(args["number"].as<float>() == doctest::Approx(3.141592653f));
|
||||
CHECK(args["number"].as<double>() == doctest::Approx(3.141592653));
|
||||
CHECK(args["number"].as<std::string>() == "3.141592653");
|
||||
}
|
||||
SUBCASE("option implicit conversions") {
|
||||
std::vector<const char*> argv {
|
||||
"test", "-n", "3.141592653", "-n", "2"};
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
CHECK(args.has_option("number") == true);
|
||||
CHECK(args["number"].count() == 2);
|
||||
float x = args["number"][0];
|
||||
int y = args["number"][1];
|
||||
CHECK(x == doctest::Approx(3.141592653f));
|
||||
CHECK(y == 2);
|
||||
}
|
||||
SUBCASE("exception on bad conversion") {
|
||||
std::vector<const char*> argv {
|
||||
"test", "-n", "not-an-number"};
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
CHECK(args.has_option("number") == true);
|
||||
CHECK(args["number"].count() == 1);
|
||||
CHECK_THROWS_AS({
|
||||
args["number"].as<int>();
|
||||
}, std::invalid_argument);
|
||||
CHECK_THROWS_AS({
|
||||
args["number"].as<double>();
|
||||
}, std::invalid_argument);
|
||||
CHECK(args["number"].as<int>(-1) == -1);
|
||||
CHECK(args["number"].as<double>(3.141) == doctest::Approx(3.141));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Define a custom conversion function for the test that follows
|
||||
namespace argagg {
|
||||
namespace convert {
|
||||
template <>
|
||||
std::vector<std::string> arg(const char* s)
|
||||
{
|
||||
std::vector<std::string> ret {};
|
||||
if (std::strlen(s) == 0) {
|
||||
return ret;
|
||||
}
|
||||
while (true) {
|
||||
const char* token = std::strchr(s, ',');
|
||||
if (token == nullptr) {
|
||||
ret.emplace_back(s, std::strlen(s));
|
||||
break;
|
||||
}
|
||||
std::size_t len = token - s;
|
||||
ret.emplace_back(s, len);
|
||||
s += len + 1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
} // namespace convert
|
||||
} // namespace argagg
|
||||
|
||||
|
||||
TEST_CASE("custom conversion function")
|
||||
{
|
||||
argagg::parser parser {{
|
||||
{"words", {"-w", "--words"}, "words", 1},
|
||||
}};
|
||||
std::vector<const char*> argv {
|
||||
"test", "-w", "hello,world,foo,bar,baz"};
|
||||
argagg::parser_results args = parser.parse(argv.size(), &(argv.front()));
|
||||
CHECK(args.has_option("words") == true);
|
||||
auto v = args["words"].as<std::vector<std::string>>();
|
||||
CHECK(v.size() == 5);
|
||||
CHECK(v[0] == "hello");
|
||||
CHECK(v[1] == "world");
|
||||
CHECK(v[2] == "foo");
|
||||
CHECK(v[3] == "bar");
|
||||
CHECK(v[4] == "baz");
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("write options help")
|
||||
{
|
||||
argagg::parser parser {{
|
||||
{"help", {"-h", "--help"}, "print help", 0},
|
||||
{"verbose", {"-v", "--verbose"}, "be verbose", 0},
|
||||
{"output", {"-o", "--output"}, "output filename", 1},
|
||||
}};
|
||||
// Just checking for no exceptions for now.
|
||||
std::cout << parser;
|
||||
}
|
||||
|
||||
|
||||
static const std::string ipsum =
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod "
|
||||
"tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam"
|
||||
", quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo "
|
||||
"consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse "
|
||||
"cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat "
|
||||
"non proident, sunt in culpa qui officia deserunt mollit anim id est "
|
||||
"laborum.";
|
||||
|
||||
|
||||
#ifdef __unix__
|
||||
static const std::string fmt_ipsum =
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod\n"
|
||||
"tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim\n"
|
||||
"veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea\n"
|
||||
"commodo consequat. Duis aute irure dolor in reprehenderit in voluptate\n"
|
||||
"velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat\n"
|
||||
"cupidatat non proident, sunt in culpa qui officia deserunt mollit anim\n"
|
||||
"id est laborum.\n";
|
||||
#else // #ifdef __unix__
|
||||
static const std::string fmt_ipsum(ipsum);
|
||||
#endif // #ifdef __unix__
|
||||
|
||||
|
||||
TEST_CASE("fmt_ostream")
|
||||
{
|
||||
std::ostringstream os;
|
||||
{
|
||||
argagg::fmt_ostream test(os);
|
||||
test << ipsum;
|
||||
}
|
||||
CHECK(os.str() == fmt_ipsum);
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("fmt_string")
|
||||
{
|
||||
std::string test_formatted = argagg::fmt_string(ipsum);
|
||||
CHECK(test_formatted == fmt_ipsum);
|
||||
}
|
@ -1,113 +0,0 @@
|
||||
// Copyright 2018-2022 René Ferdinand Rivera Morell
|
||||
// Copyright 2017 Two Blue Cubes Ltd. All rights reserved.
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef LYRA_ARG_HPP
|
||||
#define LYRA_ARG_HPP
|
||||
|
||||
#include "lyra/detail/print.hpp"
|
||||
#include "lyra/parser.hpp"
|
||||
|
||||
namespace lyra {
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_arg]
|
||||
= `lyra::arg`
|
||||
|
||||
A parser for regular arguments, i.e. not `--` or `-` prefixed. This is simply
|
||||
a way to get values of arguments directly specified in the cli.
|
||||
|
||||
Is-a <<lyra_bound_parser>>.
|
||||
|
||||
*/ // end::reference[]
|
||||
class arg : public bound_parser<arg>
|
||||
{
|
||||
public:
|
||||
using bound_parser::bound_parser;
|
||||
|
||||
virtual std::string get_usage_text(const option_style &) const override
|
||||
{
|
||||
std::ostringstream oss;
|
||||
if (!m_hint.empty())
|
||||
{
|
||||
auto c = cardinality();
|
||||
if (c.is_required())
|
||||
{
|
||||
for (size_t i = 0; i < c.minimum; ++i)
|
||||
oss << (i > 0 ? " " : "") << "<" << m_hint << ">";
|
||||
if (c.is_unbounded())
|
||||
oss << (c.is_required() ? " " : "") << "[<" << m_hint
|
||||
<< ">...]";
|
||||
}
|
||||
else if (c.is_unbounded())
|
||||
{
|
||||
oss << "[<" << m_hint << ">...]";
|
||||
}
|
||||
else
|
||||
{
|
||||
oss << "<" << m_hint << ">";
|
||||
}
|
||||
}
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
virtual help_text get_help_text(const option_style & style) const override
|
||||
{
|
||||
return { { get_usage_text(style), m_description } };
|
||||
}
|
||||
|
||||
using parser::parse;
|
||||
|
||||
parse_result parse(detail::token_iterator const & tokens,
|
||||
const option_style & style) const override
|
||||
{
|
||||
(void)style;
|
||||
LYRA_PRINT_SCOPE("arg::parse");
|
||||
auto validationResult = validate();
|
||||
if (!validationResult) return parse_result(validationResult);
|
||||
|
||||
if (!tokens)
|
||||
{
|
||||
// Nothing to match against.
|
||||
return parse_result::ok(
|
||||
detail::parse_state(parser_result_type::no_match, tokens));
|
||||
}
|
||||
|
||||
auto const & token = tokens.argument();
|
||||
|
||||
auto valueRef = static_cast<detail::BoundValueRefBase *>(m_ref.get());
|
||||
|
||||
if (value_choices)
|
||||
{
|
||||
auto choice_result = value_choices->contains_value(token.name);
|
||||
if (!choice_result)
|
||||
{
|
||||
LYRA_PRINT_DEBUG(
|
||||
"(!)", get_usage_text(style), "!=", token.name);
|
||||
return parse_result(choice_result);
|
||||
}
|
||||
}
|
||||
|
||||
auto set_result = valueRef->setValue(token.name);
|
||||
if (!set_result)
|
||||
{
|
||||
LYRA_PRINT_DEBUG("(!)", get_usage_text(style), "!=", token.name);
|
||||
return parse_result(set_result);
|
||||
}
|
||||
else
|
||||
{
|
||||
LYRA_PRINT_DEBUG("(=)", get_usage_text(style), "==", token.name);
|
||||
auto remainingTokens = tokens;
|
||||
remainingTokens.pop(token);
|
||||
return parse_result::ok(detail::parse_state(
|
||||
parser_result_type::matched, remainingTokens));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace lyra
|
||||
|
||||
#endif
|
@ -1,68 +0,0 @@
|
||||
// Copyright 2018-2022 René Ferdinand Rivera Morell
|
||||
// Copyright 2017 Two Blue Cubes Ltd. All rights reserved.
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef LYRA_ARGS_HPP
|
||||
#define LYRA_ARGS_HPP
|
||||
|
||||
#include <initializer_list>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace lyra {
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_args]
|
||||
= `lyra::args`
|
||||
|
||||
Transport for raw args (copied from main args, supplied via init list, or from
|
||||
a pair of iterators).
|
||||
|
||||
*/ // end::reference[]
|
||||
class args
|
||||
{
|
||||
public:
|
||||
// Construct from usual main() function arguments.
|
||||
args(int argc, char const * const * argv)
|
||||
: m_exeName(argv[0])
|
||||
, m_args(argv + 1, argv + argc)
|
||||
{}
|
||||
|
||||
// Construct directly from an initializer '{}'.
|
||||
args(std::initializer_list<std::string> args_list)
|
||||
: m_exeName(*args_list.begin())
|
||||
, m_args(args_list.begin() + 1, args_list.end())
|
||||
{}
|
||||
|
||||
// Construct from iterators.
|
||||
template <typename It>
|
||||
args(const It & start, const It & end)
|
||||
: m_exeName(*start)
|
||||
, m_args(start + 1, end)
|
||||
{}
|
||||
|
||||
// The executable name taken from argument zero.
|
||||
std::string exe_name() const { return m_exeName; }
|
||||
|
||||
// Arguments, excluding the exe name.
|
||||
std::vector<std::string>::const_iterator begin() const
|
||||
{
|
||||
return m_args.begin();
|
||||
}
|
||||
|
||||
std::vector<std::string>::const_iterator end() const
|
||||
{
|
||||
return m_args.end();
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_exeName;
|
||||
std::vector<std::string> m_args;
|
||||
};
|
||||
|
||||
} // namespace lyra
|
||||
|
||||
#endif
|
@ -1,527 +0,0 @@
|
||||
// Copyright 2018-2022 René Ferdinand Rivera Morell
|
||||
// Copyright 2017 Two Blue Cubes Ltd. All rights reserved.
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef LYRA_ARGUMENTS_HPP
|
||||
#define LYRA_ARGUMENTS_HPP
|
||||
|
||||
#include "lyra/detail/print.hpp"
|
||||
#include "lyra/exe_name.hpp"
|
||||
#include "lyra/parser.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <sstream>
|
||||
|
||||
namespace lyra {
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_arguments]
|
||||
= `lyra::arguments`
|
||||
|
||||
A Combined parser made up of any number of parsers. Creating and using
|
||||
one of these as a basis one can incrementally compose other parsers into this
|
||||
one. For example:
|
||||
|
||||
[source]
|
||||
----
|
||||
auto p = lyra::arguments();
|
||||
std::string what;
|
||||
float when = 0;
|
||||
std::string where;
|
||||
p |= lyra::opt(what, "what")["--make-it-so"]("Make it so.").required();
|
||||
p |= lyra::opt(when. "when")["--time"]("When to do <what>.").optional();
|
||||
p.add_argument(lyra::opt(where, "where").name("--where")
|
||||
.help("There you are.").optional());
|
||||
----
|
||||
|
||||
*/ // end::reference[]
|
||||
class arguments : public parser
|
||||
{
|
||||
public:
|
||||
// How to evaluate the collection of arguments within the limits of the
|
||||
// cardinality.
|
||||
enum evaluation
|
||||
{
|
||||
// Any of the arguments, in any order, are valid. I.e. an inclusive-or.
|
||||
any = 0,
|
||||
// All arguments, in sequence, matched. I.e. conjunctive-and.
|
||||
sequence = 1
|
||||
};
|
||||
|
||||
arguments() = default;
|
||||
|
||||
arguments(evaluation e)
|
||||
: eval_mode(e)
|
||||
{}
|
||||
|
||||
// Copy construction, needs to copy the the composed parsers.
|
||||
arguments(const arguments & other);
|
||||
|
||||
// Compose a regular parser.
|
||||
arguments & add_argument(parser const & p);
|
||||
arguments & operator|=(parser const & p);
|
||||
|
||||
// Compose the parsers from another `arguments`.
|
||||
arguments & add_argument(arguments const & other);
|
||||
arguments & operator|=(arguments const & other);
|
||||
|
||||
// Concat composition.
|
||||
template <typename T>
|
||||
arguments operator|(T const & other) const
|
||||
{
|
||||
return arguments(*this) |= other;
|
||||
}
|
||||
|
||||
// Parsing mode.
|
||||
arguments & sequential();
|
||||
arguments & inclusive();
|
||||
|
||||
// Access.
|
||||
template <typename T>
|
||||
T & get(size_t i);
|
||||
|
||||
// Internal..
|
||||
|
||||
virtual std::string get_usage_text(
|
||||
const option_style & style) const override
|
||||
{
|
||||
std::ostringstream os;
|
||||
for (auto const & p : parsers)
|
||||
{
|
||||
std::string usage_text = p->get_usage_text(style);
|
||||
if (usage_text.size() > 0)
|
||||
{
|
||||
if (os.tellp() != std::ostringstream::pos_type(0)) os << " ";
|
||||
if (p->is_group() && p->is_optional())
|
||||
os << "[ " << usage_text << " ]";
|
||||
else if (p->is_group())
|
||||
os << "{ " << usage_text << " }";
|
||||
else if (p->is_optional())
|
||||
os << "[" << usage_text << "]";
|
||||
else
|
||||
os << usage_text;
|
||||
}
|
||||
}
|
||||
return os.str();
|
||||
}
|
||||
|
||||
virtual std::string get_description_text(
|
||||
const option_style & style) const override
|
||||
{
|
||||
std::ostringstream os;
|
||||
for (auto const & p : parsers)
|
||||
{
|
||||
if (p->is_group()) continue;
|
||||
auto child_description = p->get_description_text(style);
|
||||
if (!child_description.empty()) os << child_description << "\n";
|
||||
}
|
||||
return os.str();
|
||||
}
|
||||
|
||||
// Return a container of the individual help text for the composed parsers.
|
||||
virtual help_text get_help_text(const option_style & style) const override
|
||||
{
|
||||
help_text text;
|
||||
for (auto const & p : parsers)
|
||||
{
|
||||
if (p->is_group()) text.push_back({ "", "" });
|
||||
auto child_help = p->get_help_text(style);
|
||||
text.insert(text.end(), child_help.begin(), child_help.end());
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
virtual detail::parser_cardinality cardinality() const override
|
||||
{
|
||||
return { 0, 0 };
|
||||
}
|
||||
|
||||
virtual result validate() const override
|
||||
{
|
||||
for (auto const & p : parsers)
|
||||
{
|
||||
auto parse_valid = p->validate();
|
||||
if (!parse_valid) return parse_valid;
|
||||
}
|
||||
return result::ok();
|
||||
}
|
||||
|
||||
parse_result parse(detail::token_iterator const & tokens,
|
||||
const option_style & style) const override
|
||||
{
|
||||
switch (eval_mode)
|
||||
{
|
||||
case any: return parse_any(tokens, style);
|
||||
case sequence: return parse_sequence(tokens, style);
|
||||
}
|
||||
return parse_result::error(
|
||||
detail::parse_state(parser_result_type::no_match, tokens),
|
||||
"Unknown evaluation mode; not one of 'any', or 'sequence'.");
|
||||
}
|
||||
|
||||
// Match in any order, any number of times. Returns an error if nothing
|
||||
// matched.
|
||||
parse_result parse_any(
|
||||
detail::token_iterator const & tokens, const option_style & style) const
|
||||
{
|
||||
LYRA_PRINT_SCOPE("arguments::parse_any");
|
||||
LYRA_PRINT_DEBUG("(?)", get_usage_text(style),
|
||||
"?=", tokens ? tokens.argument().name : "", "..");
|
||||
|
||||
struct ParserInfo
|
||||
{
|
||||
parser const * parser_p = nullptr;
|
||||
size_t count = 0;
|
||||
};
|
||||
std::vector<ParserInfo> parser_info(parsers.size());
|
||||
{
|
||||
size_t i = 0;
|
||||
for (auto const & p : parsers) parser_info[i++].parser_p = p.get();
|
||||
}
|
||||
|
||||
auto p_result = parse_result::ok(
|
||||
detail::parse_state(parser_result_type::matched, tokens));
|
||||
auto error_result = parse_result::ok(
|
||||
detail::parse_state(parser_result_type::no_match, tokens));
|
||||
while (p_result.value().remainingTokens())
|
||||
{
|
||||
bool token_parsed = false;
|
||||
|
||||
for (auto & parse_info : parser_info)
|
||||
{
|
||||
auto parser_cardinality = parse_info.parser_p->cardinality();
|
||||
if (parser_cardinality.is_unbounded()
|
||||
|| parse_info.count < parser_cardinality.maximum)
|
||||
{
|
||||
auto subparse_result = parse_info.parser_p->parse(
|
||||
p_result.value().remainingTokens(), style);
|
||||
if (!subparse_result)
|
||||
{
|
||||
LYRA_PRINT_DEBUG("(!)", get_usage_text(style), "!=",
|
||||
p_result.value().remainingTokens().argument().name);
|
||||
// Is the subparse error bad enough to trigger an
|
||||
// immediate return? For example for an option syntax
|
||||
// error.
|
||||
if (subparse_result.has_value()
|
||||
&& subparse_result.value().type()
|
||||
== parser_result_type::short_circuit_all)
|
||||
return subparse_result;
|
||||
// For not severe errors, we save the error if it's
|
||||
// the first so that in case no other parsers match
|
||||
// we can report the earliest problem, as that's
|
||||
// the likeliest issue.
|
||||
if (error_result)
|
||||
error_result = parse_result(subparse_result);
|
||||
}
|
||||
else if (subparse_result
|
||||
&& subparse_result.value().type()
|
||||
!= parser_result_type::no_match)
|
||||
{
|
||||
LYRA_PRINT_DEBUG("(=)", get_usage_text(style), "==",
|
||||
p_result.value().remainingTokens().argument().name,
|
||||
"==>", subparse_result.value().type());
|
||||
p_result = parse_result(subparse_result);
|
||||
token_parsed = true;
|
||||
parse_info.count += 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (p_result.value().type() == parser_result_type::short_circuit_all)
|
||||
return p_result;
|
||||
// If something signaled and error, and hence we didn't match/parse
|
||||
// anything, we indicate the error.
|
||||
if (!token_parsed && !error_result) return error_result;
|
||||
if (!token_parsed) break;
|
||||
}
|
||||
// Check missing required options. For bounded arguments we check
|
||||
// bound min and max bounds against what we parsed. For the loosest
|
||||
// required arguments we check for only the minimum. As the upper
|
||||
// bound could be infinite.
|
||||
for (auto & parseInfo : parser_info)
|
||||
{
|
||||
auto parser_cardinality = parseInfo.parser_p->cardinality();
|
||||
if ((parser_cardinality.is_bounded()
|
||||
&& (parseInfo.count < parser_cardinality.minimum
|
||||
|| parser_cardinality.maximum < parseInfo.count))
|
||||
|| (parser_cardinality.is_required()
|
||||
&& (parseInfo.count < parser_cardinality.minimum)))
|
||||
{
|
||||
return parse_result::error(p_result.value(),
|
||||
"Expected: " + parseInfo.parser_p->get_usage_text(style));
|
||||
}
|
||||
}
|
||||
return p_result;
|
||||
}
|
||||
|
||||
parse_result parse_sequence(
|
||||
detail::token_iterator const & tokens, const option_style & style) const
|
||||
{
|
||||
LYRA_PRINT_SCOPE("arguments::parse_sequence");
|
||||
LYRA_PRINT_DEBUG("(?)", get_usage_text(style),
|
||||
"?=", tokens ? tokens.argument().name : "", "..");
|
||||
|
||||
struct ParserInfo
|
||||
{
|
||||
parser const * parser_p = nullptr;
|
||||
size_t count = 0;
|
||||
};
|
||||
std::vector<ParserInfo> parser_info(parsers.size());
|
||||
{
|
||||
size_t i = 0;
|
||||
for (auto const & p : parsers) parser_info[i++].parser_p = p.get();
|
||||
}
|
||||
|
||||
auto p_result = parse_result::ok(
|
||||
detail::parse_state(parser_result_type::matched, tokens));
|
||||
|
||||
// Sequential parsing means we walk through the given parsers in order
|
||||
// and exhaust the tokens as we match persers.
|
||||
for (std::size_t parser_i = 0; parser_i < parsers.size(); ++parser_i)
|
||||
{
|
||||
auto & parse_info = parser_info[parser_i];
|
||||
auto parser_cardinality = parse_info.parser_p->cardinality();
|
||||
// This is a greedy sequential parsing algo. As it parsers the
|
||||
// current argument as much as possible.
|
||||
do
|
||||
{
|
||||
auto subresult = parse_info.parser_p->parse(
|
||||
p_result.value().remainingTokens(), style);
|
||||
if (!subresult)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (subresult.value().type()
|
||||
== parser_result_type::short_circuit_all)
|
||||
{
|
||||
return subresult;
|
||||
}
|
||||
if (subresult.value().type() != parser_result_type::no_match)
|
||||
{
|
||||
LYRA_PRINT_DEBUG("(=)", get_usage_text(style), "==",
|
||||
p_result.value().remainingTokens()
|
||||
? p_result.value().remainingTokens().argument().name
|
||||
: "",
|
||||
"==>", subresult.value().type());
|
||||
p_result = subresult;
|
||||
parse_info.count += 1;
|
||||
}
|
||||
}
|
||||
while (p_result.value().have_tokens()
|
||||
&& (parser_cardinality.is_unbounded()
|
||||
|| parse_info.count < parser_cardinality.maximum));
|
||||
// Check missing required options immediately as for sequential the
|
||||
// argument is greedy and will fully match here. For bounded
|
||||
// arguments we check bound min and max bounds against what we
|
||||
// parsed. For the loosest required arguments we check for only the
|
||||
// minimum. As the upper bound could be infinite.
|
||||
if ((parser_cardinality.is_bounded()
|
||||
&& (parse_info.count < parser_cardinality.minimum
|
||||
|| parser_cardinality.maximum < parse_info.count))
|
||||
|| (parser_cardinality.is_required()
|
||||
&& (parse_info.count < parser_cardinality.minimum)))
|
||||
{
|
||||
return parse_result::error(p_result.value(),
|
||||
"Expected: " + parse_info.parser_p->get_usage_text(style));
|
||||
}
|
||||
}
|
||||
// The return is just the last state as it contains any remaining tokens
|
||||
// to parse.
|
||||
return p_result;
|
||||
}
|
||||
|
||||
virtual std::unique_ptr<parser> clone() const override
|
||||
{
|
||||
return make_clone<arguments>(this);
|
||||
}
|
||||
|
||||
friend std::ostream & operator<<(
|
||||
std::ostream & os, arguments const & parser)
|
||||
{
|
||||
const option_style & s
|
||||
= parser.opt_style ? *parser.opt_style : option_style::posix();
|
||||
parser.print_help_text(os, s);
|
||||
return os;
|
||||
}
|
||||
|
||||
virtual const parser * get_named(const std::string & n) const override
|
||||
{
|
||||
for (auto & p : parsers)
|
||||
{
|
||||
const parser * p_result = p->get_named(n);
|
||||
if (p_result) return p_result;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
protected:
|
||||
std::shared_ptr<option_style> opt_style;
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<parser>> parsers;
|
||||
evaluation eval_mode = any;
|
||||
};
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_arguments_ctor]
|
||||
== Construction
|
||||
|
||||
end::reference[] */
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_arguments_ctor_default]
|
||||
=== Default
|
||||
|
||||
[source]
|
||||
----
|
||||
arguments() = default;
|
||||
----
|
||||
|
||||
Default constructing a `arguments` is the starting point to adding arguments
|
||||
and options for parsing a arguments line.
|
||||
|
||||
end::reference[] */
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_arguments_ctor_copy]
|
||||
=== Copy
|
||||
|
||||
[source]
|
||||
----
|
||||
arguments::arguments(const arguments& other);
|
||||
----
|
||||
|
||||
end::reference[] */
|
||||
inline arguments::arguments(const arguments & other)
|
||||
: parser(other)
|
||||
, opt_style(other.opt_style)
|
||||
, eval_mode(other.eval_mode)
|
||||
{
|
||||
for (auto & other_parser : other.parsers)
|
||||
{
|
||||
parsers.push_back(other_parser->clone());
|
||||
}
|
||||
}
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_arguments_specification]
|
||||
== Specification
|
||||
|
||||
end::reference[] */
|
||||
|
||||
// ==
|
||||
|
||||
/* tag::reference[]
|
||||
[#lyra_arguments_add_argument]
|
||||
=== `lyra::arguments::add_argument`
|
||||
|
||||
[source]
|
||||
----
|
||||
arguments& arguments::add_argument(parser const& p);
|
||||
arguments& arguments::operator|=(parser const& p);
|
||||
arguments& arguments::add_argument(arguments const& other);
|
||||
arguments& arguments::operator|=(arguments const& other);
|
||||
----
|
||||
|
||||
Adds the given argument parser to the considered arguments for this
|
||||
`arguments`. Depending on the parser given it will be: directly added as an
|
||||
argument (for `parser`), or add the parsers from another `arguments` to
|
||||
this one.
|
||||
|
||||
end::reference[] */
|
||||
inline arguments & arguments::add_argument(parser const & p)
|
||||
{
|
||||
parsers.push_back(p.clone());
|
||||
return *this;
|
||||
}
|
||||
inline arguments & arguments::operator|=(parser const & p)
|
||||
{
|
||||
return this->add_argument(p);
|
||||
}
|
||||
inline arguments & arguments::add_argument(arguments const & other)
|
||||
{
|
||||
if (other.is_group())
|
||||
{
|
||||
parsers.push_back(other.clone());
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto & p : other.parsers)
|
||||
{
|
||||
parsers.push_back(p->clone());
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
inline arguments & arguments::operator|=(arguments const & other)
|
||||
{
|
||||
return this->add_argument(other);
|
||||
}
|
||||
|
||||
/* tag::reference[]
|
||||
=== `lyra::arguments::sequential`
|
||||
|
||||
[source]
|
||||
----
|
||||
arguments & arguments::sequential();
|
||||
----
|
||||
|
||||
Sets the parsing mode for the arguments to "sequential". When parsing the
|
||||
arguments they will be, greedily, consumed in the order they where added.
|
||||
This is useful for sub-commands and structured command lines.
|
||||
|
||||
end::reference[] */
|
||||
inline arguments & arguments::sequential()
|
||||
{
|
||||
eval_mode = sequence;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/* tag::reference[]
|
||||
=== `lyra::arguments::inclusive`
|
||||
|
||||
[source]
|
||||
----
|
||||
arguments & arguments::inclusive();
|
||||
----
|
||||
|
||||
Sets the parsing mode for the arguments to "inclusively any". This is the
|
||||
default that attempts to match each parsed argument with all the available
|
||||
parsers. This means that there is no ordering enforced.
|
||||
|
||||
end::reference[] */
|
||||
inline arguments & arguments::inclusive()
|
||||
{
|
||||
eval_mode = any;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/* tag::reference[]
|
||||
=== `lyra::arguments::get`
|
||||
|
||||
[source]
|
||||
----
|
||||
template <typename T>
|
||||
T & arguments::get(size_t i);
|
||||
----
|
||||
|
||||
Get a modifyable reference to one of the parsers specified.
|
||||
|
||||
end::reference[] */
|
||||
template <typename T>
|
||||
T & arguments::get(size_t i)
|
||||
{
|
||||
return static_cast<T &>(*parsers.at(i));
|
||||
}
|
||||
|
||||
} // namespace lyra
|
||||
|
||||
#endif
|
@ -1,376 +0,0 @@
|
||||
// Copyright 2018-2022 René Ferdinand Rivera Morell
|
||||
// Copyright 2017 Two Blue Cubes Ltd. All rights reserved.
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef LYRA_CLI_HPP
|
||||
#define LYRA_CLI_HPP
|
||||
|
||||
#include "lyra/arguments.hpp"
|
||||
#include "lyra/detail/deprecated_parser_customization.hpp"
|
||||
#include "lyra/detail/from_string.hpp"
|
||||
#include "lyra/detail/print.hpp"
|
||||
#include "lyra/exe_name.hpp"
|
||||
#include "lyra/group.hpp"
|
||||
#include "lyra/option_style.hpp"
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace lyra {
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_cli]
|
||||
= `lyra::cli`
|
||||
|
||||
A Combined parser made up of any two or more other parsers. Creating and using
|
||||
one of these as a basis one can incrementally compose other parsers into this
|
||||
one. For example:
|
||||
|
||||
[source]
|
||||
----
|
||||
auto cli = lyra::cli();
|
||||
std::string what;
|
||||
float when = 0;
|
||||
std::string where;
|
||||
cli |= lyra::opt(what, "what")["--make-it-so"]("Make it so.").required();
|
||||
cli |= lyra::opt(when. "when")["--time"]("When to do <what>.").optional();
|
||||
cli.add_argument(lyra::opt(where, "where").name("--where")
|
||||
.help("There you are.").optional());
|
||||
----
|
||||
|
||||
*/ // end::reference[]
|
||||
class cli : protected arguments
|
||||
{
|
||||
public:
|
||||
cli() = default;
|
||||
|
||||
// Copy construction, needs to copy the exe name and the composed parsers.
|
||||
cli(const cli & other);
|
||||
|
||||
// Compose the `exe_name` parser.
|
||||
cli & add_argument(exe_name const & exe_name);
|
||||
cli & operator|=(exe_name const & exe_name);
|
||||
|
||||
// Compose a regular parser.
|
||||
cli & add_argument(parser const & p);
|
||||
cli & operator|=(parser const & p);
|
||||
|
||||
// Compose a group, by adding it as a single argument.
|
||||
cli & add_argument(group const & p);
|
||||
cli & operator|=(group const & p);
|
||||
|
||||
// Compose the parsers from another `cli`.
|
||||
cli & add_argument(cli const & other);
|
||||
cli & operator|=(cli const & other);
|
||||
|
||||
// Concat composition.
|
||||
template <typename T>
|
||||
cli operator|(T const & other) const;
|
||||
|
||||
// Result reference wrapper to fetch and convert argument.
|
||||
struct value_result
|
||||
{
|
||||
public:
|
||||
explicit value_result(const parser * p)
|
||||
: parser_ref(p)
|
||||
{}
|
||||
|
||||
template <typename T,
|
||||
typename std::enable_if<detail::is_convertible_from_string<
|
||||
typename detail::remove_cvref<T>::type>::value>::
|
||||
type * = nullptr>
|
||||
operator T() const
|
||||
{
|
||||
typename detail::remove_cvref<T>::type converted_value {};
|
||||
if (parser_ref)
|
||||
detail::from_string<std::string,
|
||||
typename detail::remove_cvref<T>::type>(
|
||||
parser_ref->get_value(0), converted_value);
|
||||
return converted_value;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
operator std::vector<T>() const
|
||||
{
|
||||
std::vector<T> converted_value;
|
||||
if (parser_ref)
|
||||
{
|
||||
for (size_t i = 0; i < parser_ref->get_value_count(); ++i)
|
||||
{
|
||||
T v;
|
||||
if (detail::from_string(parser_ref->get_value(i), v))
|
||||
converted_value.push_back(v);
|
||||
}
|
||||
}
|
||||
return converted_value;
|
||||
}
|
||||
|
||||
operator std::string() const
|
||||
{
|
||||
if (parser_ref) return parser_ref->get_value(0);
|
||||
return "";
|
||||
}
|
||||
|
||||
private:
|
||||
const parser * parser_ref = nullptr;
|
||||
};
|
||||
|
||||
value_result operator[](const std::string & n);
|
||||
|
||||
cli & style(const option_style & style);
|
||||
cli & style(option_style && style);
|
||||
|
||||
// Stream out generates the help output.
|
||||
friend std::ostream & operator<<(std::ostream & os, cli const & parser)
|
||||
{
|
||||
return os << static_cast<const arguments &>(parser);
|
||||
}
|
||||
|
||||
// Parse from arguments.
|
||||
parse_result parse(args const & args) const
|
||||
{
|
||||
if (opt_style)
|
||||
return parse(args, *opt_style);
|
||||
else
|
||||
return parse(args, option_style::posix());
|
||||
}
|
||||
parse_result parse(args const & args, const option_style & style) const;
|
||||
|
||||
// Backward compatability parse() that takes `parser_customization` and
|
||||
// converts to `option_style`.
|
||||
[[deprecated]] parse_result parse(
|
||||
args const & args, const parser_customization & customize) const
|
||||
{
|
||||
return this->parse(args,
|
||||
option_style(customize.token_delimiters(),
|
||||
customize.option_prefix(), 2, customize.option_prefix(), 1));
|
||||
}
|
||||
|
||||
// Internal..
|
||||
|
||||
using arguments::parse;
|
||||
using arguments::get_named;
|
||||
|
||||
virtual std::unique_ptr<parser> clone() const override
|
||||
{
|
||||
return std::unique_ptr<parser>(new cli(*this));
|
||||
}
|
||||
|
||||
protected:
|
||||
mutable exe_name m_exeName;
|
||||
|
||||
virtual std::string get_usage_text(
|
||||
const option_style & style) const override
|
||||
{
|
||||
if (!m_exeName.name().empty())
|
||||
return m_exeName.name() + " " + arguments::get_usage_text(style);
|
||||
else
|
||||
// We use an empty exe name as an indicator to remove USAGE text.
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_cli_ctor]
|
||||
== Construction
|
||||
|
||||
end::reference[] */
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_cli_ctor_default]
|
||||
=== Default
|
||||
|
||||
[source]
|
||||
----
|
||||
cli() = default;
|
||||
----
|
||||
|
||||
Default constructing a `cli` is the starting point to adding arguments
|
||||
and options for parsing a command line.
|
||||
|
||||
end::reference[] */
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_cli_ctor_copy]
|
||||
=== Copy
|
||||
|
||||
[source]
|
||||
----
|
||||
cli::cli(const cli& other);
|
||||
----
|
||||
|
||||
end::reference[] */
|
||||
inline cli::cli(const cli & other)
|
||||
: arguments(other)
|
||||
, m_exeName(other.m_exeName)
|
||||
{}
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_cli_specification]
|
||||
== Specification
|
||||
|
||||
end::reference[] */
|
||||
|
||||
// ==
|
||||
|
||||
/* tag::reference[]
|
||||
[#lyra_cli_add_argument]
|
||||
=== `lyra::cli::add_argument`
|
||||
|
||||
[source]
|
||||
----
|
||||
cli& cli::add_argument(exe_name const& exe_name);
|
||||
cli& cli::operator|=(exe_name const& exe_name);
|
||||
cli& cli::add_argument(parser const& p);
|
||||
cli& cli::operator|=(parser const& p);
|
||||
cli& cli::add_argument(group const& p);
|
||||
cli& cli::operator|=(group const& p);
|
||||
cli& cli::add_argument(cli const& other);
|
||||
cli& cli::operator|=(cli const& other);
|
||||
----
|
||||
|
||||
Adds the given argument parser to the considered arguments for this
|
||||
`cli`. Depending on the parser given it will be: recorded as the exe
|
||||
name (for `exe_name` parser), directly added as an argument (for
|
||||
`parser`), or add the parsers from another `cli` to this one.
|
||||
|
||||
end::reference[] */
|
||||
inline cli & cli::add_argument(exe_name const & exe_name)
|
||||
{
|
||||
m_exeName = exe_name;
|
||||
return *this;
|
||||
}
|
||||
inline cli & cli::operator|=(exe_name const & exe_name)
|
||||
{
|
||||
return this->add_argument(exe_name);
|
||||
}
|
||||
inline cli & cli::add_argument(parser const & p)
|
||||
{
|
||||
arguments::add_argument(p);
|
||||
return *this;
|
||||
}
|
||||
inline cli & cli::operator|=(parser const & p)
|
||||
{
|
||||
arguments::add_argument(p);
|
||||
return *this;
|
||||
}
|
||||
inline cli & cli::add_argument(group const & other)
|
||||
{
|
||||
arguments::add_argument(static_cast<parser const &>(other));
|
||||
return *this;
|
||||
}
|
||||
inline cli & cli::operator|=(group const & other)
|
||||
{
|
||||
return this->add_argument(other);
|
||||
}
|
||||
inline cli & cli::add_argument(cli const & other)
|
||||
{
|
||||
arguments::add_argument(static_cast<arguments const &>(other));
|
||||
return *this;
|
||||
}
|
||||
inline cli & cli::operator|=(cli const & other)
|
||||
{
|
||||
return this->add_argument(other);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline cli cli::operator|(T const & other) const
|
||||
{
|
||||
return cli(*this).add_argument(other);
|
||||
}
|
||||
|
||||
template <typename DerivedT, typename T>
|
||||
cli operator|(composable_parser<DerivedT> const & thing, T const & other)
|
||||
{
|
||||
return cli() | static_cast<DerivedT const &>(thing) | other;
|
||||
}
|
||||
|
||||
/* tag::reference[]
|
||||
[#lyra_cli_array_ref]
|
||||
=== `lyra::cli::operator[]`
|
||||
|
||||
[source]
|
||||
----
|
||||
cli::value_result cli::operator[](const std::string & n)
|
||||
----
|
||||
|
||||
Finds the given argument by either option name or hint name and returns a
|
||||
convertible reference to the value, either the one provided by the user or the
|
||||
default.
|
||||
|
||||
end::reference[] */
|
||||
inline cli::value_result cli::operator[](const std::string & n)
|
||||
{
|
||||
return value_result(this->get_named(n));
|
||||
}
|
||||
|
||||
/* tag::reference[]
|
||||
[#lyra_cli_parse]
|
||||
=== `lyra::cli::parse`
|
||||
|
||||
[source]
|
||||
----
|
||||
parse_result cli::parse(
|
||||
args const& args, const option_style& customize) const;
|
||||
----
|
||||
|
||||
Parses given arguments `args` and optional option style.
|
||||
The result indicates success or failure, and if failure what kind of failure
|
||||
it was. The state of variables bound to options is unspecified and any bound
|
||||
callbacks may have been called.
|
||||
|
||||
end::reference[] */
|
||||
inline parse_result cli::parse(
|
||||
args const & args, const option_style & style) const
|
||||
{
|
||||
LYRA_PRINT_SCOPE("cli::parse");
|
||||
m_exeName.set(args.exe_name());
|
||||
detail::token_iterator args_tokens(args, style);
|
||||
parse_result p_result = parse(args_tokens, style);
|
||||
if (p_result
|
||||
&& (p_result.value().type() == parser_result_type::no_match
|
||||
|| p_result.value().type() == parser_result_type::matched))
|
||||
{
|
||||
if (p_result.value().have_tokens())
|
||||
{
|
||||
return parse_result::error(p_result.value(),
|
||||
"Unrecognized token: "
|
||||
+ p_result.value().remainingTokens().argument().name);
|
||||
}
|
||||
}
|
||||
return p_result;
|
||||
}
|
||||
|
||||
/* tag::reference[]
|
||||
[#lyra_cli_style]
|
||||
=== `lyra::cli::style`
|
||||
|
||||
[source]
|
||||
----
|
||||
lyra::cli & lyra::cli::style(const lyra::option_style & style)
|
||||
lyra::cli & lyra::cli::style(lyra::option_style && style)
|
||||
----
|
||||
|
||||
Specifies the <<lyra_option_style>> to accept for this instance.
|
||||
|
||||
end::reference[] */
|
||||
inline cli & cli::style(const option_style & style)
|
||||
{
|
||||
opt_style = std::make_shared<option_style>(style);
|
||||
return *this;
|
||||
}
|
||||
inline cli & cli::style(option_style && style)
|
||||
{
|
||||
opt_style = std::make_shared<option_style>(std::move(style));
|
||||
return *this;
|
||||
}
|
||||
|
||||
} // namespace lyra
|
||||
|
||||
#endif
|
@ -1,17 +0,0 @@
|
||||
// Copyright 2018-2022 René Ferdinand Rivera Morell
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef LYRA_CLI_PARSER_HPP
|
||||
#define LYRA_CLI_PARSER_HPP
|
||||
|
||||
#include "lyra/cli.hpp"
|
||||
|
||||
namespace lyra {
|
||||
|
||||
using cli_parser = cli;
|
||||
|
||||
} // namespace lyra
|
||||
|
||||
#endif
|
@ -1,166 +0,0 @@
|
||||
// Copyright 2020-2022 René Ferdinand Rivera Morell
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef LYRA_COMMAND_HPP
|
||||
#define LYRA_COMMAND_HPP
|
||||
|
||||
#include "lyra/group.hpp"
|
||||
#include "lyra/literal.hpp"
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
namespace lyra {
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_command]
|
||||
= `lyra::command`
|
||||
|
||||
A parser that encapsulates the pattern of parsing sub-commands. It provides a
|
||||
quick wrapper for the equivalent arrangement of `group` and `literal` parsers.
|
||||
For example:
|
||||
|
||||
[source]
|
||||
----
|
||||
lyra::command c = lyra::command("sub");
|
||||
----
|
||||
|
||||
Is equivalent to:
|
||||
|
||||
[source]
|
||||
----
|
||||
lyra::command c = lyra::group()
|
||||
.sequential()
|
||||
.add_argument(literal("sub"))
|
||||
.add_argument(group());
|
||||
lyra::group & g = c.get<lyra::group>(1);
|
||||
----
|
||||
|
||||
I.e. it's conposed of a `literal` followed by the rest of the command arguments.
|
||||
|
||||
Is-a <<lyra_group>>.
|
||||
|
||||
*/ // end::reference[]
|
||||
class command : public group
|
||||
{
|
||||
public:
|
||||
// Construction, with and without, callback.
|
||||
explicit command(const std::string & n);
|
||||
command(
|
||||
const std::string & n, const std::function<void(const group &)> & f);
|
||||
|
||||
// Help description.
|
||||
command & help(const std::string & text);
|
||||
command & operator()(std::string const & description);
|
||||
|
||||
// Add arguments.
|
||||
template <typename P>
|
||||
command & add_argument(P const & p);
|
||||
template <typename P>
|
||||
command & operator|=(P const & p);
|
||||
|
||||
// Internal.
|
||||
virtual std::unique_ptr<parser> clone() const override
|
||||
{
|
||||
return make_clone<command>(this);
|
||||
}
|
||||
};
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_command_ctor]
|
||||
== Construction
|
||||
|
||||
[source]
|
||||
----
|
||||
command::command(const std::string & n);
|
||||
command::command(
|
||||
const std::string & n, const std::function<void(const group &)>& f);
|
||||
----
|
||||
|
||||
To construct an `command` we need a name (`n`) that matches, and triggers, that
|
||||
command.
|
||||
|
||||
|
||||
end::reference[] */
|
||||
inline command::command(const std::string & n)
|
||||
{
|
||||
this->sequential()
|
||||
.add_argument(literal(n))
|
||||
.add_argument(group().required());
|
||||
}
|
||||
inline command::command(
|
||||
const std::string & n, const std::function<void(const group &)> & f)
|
||||
: group(f)
|
||||
{
|
||||
this->sequential()
|
||||
.add_argument(literal(n))
|
||||
.add_argument(group().required());
|
||||
}
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_command_specification]
|
||||
== Specification
|
||||
|
||||
end::reference[] */
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_command_help]
|
||||
=== `lyra:command::help`
|
||||
|
||||
[source]
|
||||
----
|
||||
command & command::help(const std::string& text)
|
||||
command & command::operator()(std::string const& description)
|
||||
----
|
||||
|
||||
Specify a help description for the command. This sets the help for the
|
||||
underlying literal of the command.
|
||||
|
||||
end::reference[] */
|
||||
inline command & command::help(const std::string & text)
|
||||
{
|
||||
this->get<literal>(0).help(text);
|
||||
return *this;
|
||||
}
|
||||
inline command & command::operator()(std::string const & description)
|
||||
{
|
||||
return this->help(description);
|
||||
}
|
||||
|
||||
/* tag::reference[]
|
||||
[#lyra_command_add_argument]
|
||||
=== `lyra::command::add_argument`
|
||||
|
||||
[source]
|
||||
----
|
||||
template <typename P>
|
||||
command & command::add_argument(P const & p);
|
||||
template <typename P>
|
||||
command & command::operator|=(P const & p);
|
||||
----
|
||||
|
||||
Adds the given argument parser to the considered arguments for this `comand`.
|
||||
The argument is added to the sub-group argument instead of this one. Hence it
|
||||
has the effect of adding arguments *after* the command name.
|
||||
|
||||
end::reference[] */
|
||||
template <typename P>
|
||||
command & command::add_argument(P const & p)
|
||||
{
|
||||
this->get<group>(1).add_argument(p);
|
||||
return *this;
|
||||
}
|
||||
template <typename P>
|
||||
command & command::operator|=(P const & p)
|
||||
{
|
||||
return this->add_argument(p);
|
||||
}
|
||||
|
||||
} // namespace lyra
|
||||
|
||||
#endif
|
@ -1,193 +0,0 @@
|
||||
// Copyright 2018-2022 René Ferdinand Rivera Morell
|
||||
// Copyright 2017 Two Blue Cubes Ltd. All rights reserved.
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef LYRA_DETAIL_BOUND_HPP
|
||||
#define LYRA_DETAIL_BOUND_HPP
|
||||
|
||||
#include "lyra/detail/from_string.hpp"
|
||||
#include "lyra/detail/invoke_lambda.hpp"
|
||||
#include "lyra/detail/parse.hpp"
|
||||
#include "lyra/detail/unary_lambda_traits.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace lyra { namespace detail {
|
||||
|
||||
struct NonCopyable
|
||||
{
|
||||
NonCopyable() = default;
|
||||
NonCopyable(NonCopyable const &) = delete;
|
||||
NonCopyable(NonCopyable &&) = delete;
|
||||
NonCopyable & operator=(NonCopyable const &) = delete;
|
||||
NonCopyable & operator=(NonCopyable &&) = delete;
|
||||
};
|
||||
|
||||
struct BoundRef : NonCopyable
|
||||
{
|
||||
virtual ~BoundRef() = default;
|
||||
virtual auto isContainer() const -> bool { return false; }
|
||||
virtual auto isFlag() const -> bool { return false; }
|
||||
|
||||
virtual size_t get_value_count() const { return 0; }
|
||||
virtual std::string get_value(size_t) const { return ""; }
|
||||
};
|
||||
|
||||
struct BoundValueRefBase : BoundRef
|
||||
{
|
||||
virtual auto setValue(std::string const & arg) -> parser_result = 0;
|
||||
};
|
||||
|
||||
struct BoundFlagRefBase : BoundRef
|
||||
{
|
||||
virtual auto setFlag(bool flag) -> parser_result = 0;
|
||||
virtual auto isFlag() const -> bool { return true; }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct BoundValueRef : BoundValueRefBase
|
||||
{
|
||||
T & m_ref;
|
||||
|
||||
explicit BoundValueRef(T & ref)
|
||||
: m_ref(ref)
|
||||
{}
|
||||
|
||||
auto setValue(std::string const & arg) -> parser_result override
|
||||
{
|
||||
return parse_string(arg, m_ref);
|
||||
}
|
||||
|
||||
virtual size_t get_value_count() const override { return 1; }
|
||||
virtual std::string get_value(size_t i) const override
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
std::string result;
|
||||
detail::to_string(m_ref, result);
|
||||
return result;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct BoundValueRef<std::vector<T>> : BoundValueRefBase
|
||||
{
|
||||
std::vector<T> & m_ref;
|
||||
|
||||
explicit BoundValueRef(std::vector<T> & ref)
|
||||
: m_ref(ref)
|
||||
{}
|
||||
|
||||
auto isContainer() const -> bool override { return true; }
|
||||
|
||||
auto setValue(std::string const & arg) -> parser_result override
|
||||
{
|
||||
T temp;
|
||||
auto str_result = parse_string(arg, temp);
|
||||
if (str_result) m_ref.push_back(temp);
|
||||
return str_result;
|
||||
}
|
||||
|
||||
virtual size_t get_value_count() const override { return m_ref.size(); }
|
||||
virtual std::string get_value(size_t i) const override
|
||||
{
|
||||
if (i < m_ref.size())
|
||||
{
|
||||
std::string str_result;
|
||||
detail::to_string(m_ref[i], str_result);
|
||||
return str_result;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
struct BoundFlagRef : BoundFlagRefBase
|
||||
{
|
||||
bool & m_ref;
|
||||
|
||||
explicit BoundFlagRef(bool & ref)
|
||||
: m_ref(ref)
|
||||
{}
|
||||
|
||||
auto setFlag(bool flag) -> parser_result override
|
||||
{
|
||||
m_ref = flag;
|
||||
return parser_result::ok(parser_result_type::matched);
|
||||
}
|
||||
|
||||
virtual size_t get_value_count() const override { return 1; }
|
||||
virtual std::string get_value(size_t i) const override
|
||||
{
|
||||
if (i == 0) return m_ref ? "true" : "false";
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
template <typename L>
|
||||
struct BoundLambda : BoundValueRefBase
|
||||
{
|
||||
L m_lambda;
|
||||
|
||||
static_assert(unary_lambda_traits<L>::isValid,
|
||||
"Supplied lambda must take exactly one argument");
|
||||
explicit BoundLambda(L const & lambda)
|
||||
: m_lambda(lambda)
|
||||
{}
|
||||
|
||||
auto setValue(std::string const & arg) -> parser_result override
|
||||
{
|
||||
return invokeLambda<typename unary_lambda_traits<L>::ArgType>(
|
||||
m_lambda, arg);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename L>
|
||||
struct BoundFlagLambda : BoundFlagRefBase
|
||||
{
|
||||
L m_lambda;
|
||||
|
||||
static_assert(unary_lambda_traits<L>::isValid,
|
||||
"Supplied lambda must take exactly one argument");
|
||||
static_assert(
|
||||
std::is_same<typename unary_lambda_traits<L>::ArgType, bool>::value,
|
||||
"flags must be boolean");
|
||||
|
||||
explicit BoundFlagLambda(L const & lambda)
|
||||
: m_lambda(lambda)
|
||||
{}
|
||||
|
||||
auto setFlag(bool flag) -> parser_result override
|
||||
{
|
||||
return LambdaInvoker<
|
||||
typename unary_lambda_traits<L>::ReturnType>::invoke(m_lambda,
|
||||
flag);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct BoundVal : BoundValueRef<T>
|
||||
{
|
||||
T value;
|
||||
|
||||
BoundVal(T && v)
|
||||
: BoundValueRef<T>(value)
|
||||
, value(v)
|
||||
{}
|
||||
|
||||
BoundVal(BoundVal && other)
|
||||
: BoundValueRef<T>(value)
|
||||
, value(std::move(other.value))
|
||||
{}
|
||||
|
||||
std::shared_ptr<BoundRef> move_to_shared()
|
||||
{
|
||||
return std::shared_ptr<BoundRef>(new BoundVal<T>(std::move(*this)));
|
||||
}
|
||||
};
|
||||
|
||||
}} // namespace lyra::detail
|
||||
|
||||
#endif
|
@ -1,149 +0,0 @@
|
||||
// Copyright 2018-2022 René Ferdinand Rivera Morell
|
||||
// Copyright 2017 Two Blue Cubes Ltd. All rights reserved.
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef LYRA_DETAIL_CHOICES_HPP
|
||||
#define LYRA_DETAIL_CHOICES_HPP
|
||||
|
||||
#include "lyra/detail/from_string.hpp"
|
||||
#include "lyra/detail/result.hpp"
|
||||
#include "lyra/detail/unary_lambda_traits.hpp"
|
||||
#include "lyra/parser_result.hpp"
|
||||
#include <algorithm>
|
||||
#include <initializer_list>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
namespace lyra { namespace detail {
|
||||
|
||||
/*
|
||||
Type erased base for set of choices. I.e. it's an "interface".
|
||||
*/
|
||||
struct choices_base
|
||||
{
|
||||
virtual ~choices_base() = default;
|
||||
virtual parser_result contains_value(std::string const & val) const = 0;
|
||||
};
|
||||
|
||||
/*
|
||||
Stores a set of choice values and provides checking if a given parsed
|
||||
string value is one of the choices.
|
||||
*/
|
||||
template <typename T>
|
||||
struct choices_set : choices_base
|
||||
{
|
||||
// The allowed values.
|
||||
std::vector<T> values;
|
||||
|
||||
template <typename... Vals>
|
||||
explicit choices_set(Vals... vals)
|
||||
: choices_set({ vals... })
|
||||
{}
|
||||
|
||||
explicit choices_set(const std::vector<T> & vals)
|
||||
: values(vals)
|
||||
{}
|
||||
|
||||
// Checks if the given string val exists in the set of
|
||||
// values. Returns a parsing error if the value is not present.
|
||||
parser_result contains_value(std::string const & val) const override
|
||||
{
|
||||
T value;
|
||||
auto parse = parse_string(val, value);
|
||||
if (!parse)
|
||||
{
|
||||
return parser_result::error(
|
||||
parser_result_type::no_match, parse.message());
|
||||
}
|
||||
bool result = std::count(values.begin(), values.end(), value) > 0;
|
||||
if (result)
|
||||
{
|
||||
return parser_result::ok(parser_result_type::matched);
|
||||
}
|
||||
// We consider not finding a choice a parse error.
|
||||
return parser_result::error(parser_result_type::no_match,
|
||||
"Value '" + val
|
||||
+ "' not expected. Allowed values are: " + this->to_string());
|
||||
}
|
||||
|
||||
// Returns a comma separated list of the allowed values.
|
||||
std::string to_string() const
|
||||
{
|
||||
std::string result;
|
||||
for (const T & val : values)
|
||||
{
|
||||
if (!result.empty()) result += ", ";
|
||||
std::string val_string;
|
||||
if (detail::to_string(val, val_string))
|
||||
{
|
||||
result += val_string;
|
||||
}
|
||||
else
|
||||
{
|
||||
result += "<value error>";
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected:
|
||||
explicit choices_set(std::initializer_list<T> const & vals)
|
||||
: values(vals)
|
||||
{}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct choices_set<const char *> : choices_set<std::string>
|
||||
{
|
||||
template <typename... Vals>
|
||||
explicit choices_set(Vals... vals)
|
||||
: choices_set<std::string>(vals...)
|
||||
{}
|
||||
};
|
||||
|
||||
/*
|
||||
Calls a designated function to check if the choice is valid.
|
||||
*/
|
||||
template <typename Lambda>
|
||||
struct choices_check : choices_base
|
||||
{
|
||||
static_assert(unary_lambda_traits<Lambda>::isValid,
|
||||
"Supplied lambda must take exactly one argument");
|
||||
static_assert(std::is_same<bool,
|
||||
typename unary_lambda_traits<Lambda>::ReturnType>::value,
|
||||
"Supplied lambda must return bool");
|
||||
|
||||
Lambda checker;
|
||||
using value_type = typename unary_lambda_traits<Lambda>::ArgType;
|
||||
|
||||
explicit choices_check(Lambda const & checker_function)
|
||||
: checker(checker_function)
|
||||
{}
|
||||
|
||||
// Checks if the given string val exists in the set of
|
||||
// values. Returns a parsing error if the value is not present.
|
||||
parser_result contains_value(std::string const & val) const override
|
||||
{
|
||||
value_type value;
|
||||
auto parse = parse_string(val, value);
|
||||
if (!parse)
|
||||
{
|
||||
return parser_result::error(
|
||||
parser_result_type::no_match, parse.message());
|
||||
}
|
||||
if (checker(value))
|
||||
{
|
||||
return parser_result::ok(parser_result_type::matched);
|
||||
}
|
||||
// We consider not finding a choice a parse error.
|
||||
return parser_result::error(
|
||||
parser_result_type::no_match, "Value '" + val + "' not expected.");
|
||||
}
|
||||
};
|
||||
|
||||
}} // namespace lyra::detail
|
||||
|
||||
#endif
|
@ -1,63 +0,0 @@
|
||||
// Copyright 2018-2022 René Ferdinand Rivera Morell
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef LYRA_DETAIL_DEPRECATED_PARSER_CUSTOMIZATION_HPP
|
||||
#define LYRA_DETAIL_DEPRECATED_PARSER_CUSTOMIZATION_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace lyra {
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_parser_customization]
|
||||
= `lyra::parser_customization`
|
||||
|
||||
Customization interface for parsing of options.
|
||||
|
||||
[source]
|
||||
----
|
||||
virtual std::string token_delimiters() const = 0;
|
||||
----
|
||||
|
||||
Specifies the characters to use for splitting a cli argument into the option
|
||||
and its value (if any).
|
||||
|
||||
[source]
|
||||
----
|
||||
virtual std::string option_prefix() const = 0;
|
||||
----
|
||||
|
||||
Specifies the characters to use as possible prefix, either single or double,
|
||||
for all options.
|
||||
|
||||
end::reference[] */
|
||||
struct parser_customization
|
||||
{
|
||||
virtual std::string token_delimiters() const = 0;
|
||||
virtual std::string option_prefix() const = 0;
|
||||
};
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_default_parser_customization]
|
||||
= `lyra::default_parser_customization`
|
||||
|
||||
Is-a `lyra::parser_customization` that defines token delimiters as space (" ")
|
||||
or equal (`=`). And specifies the option prefix character as dash (`-`)
|
||||
resulting in long options with `--` and short options with `-`.
|
||||
|
||||
This customization is used as the default if none is given.
|
||||
|
||||
end::reference[] */
|
||||
struct default_parser_customization : parser_customization
|
||||
{
|
||||
std::string token_delimiters() const override { return " ="; }
|
||||
std::string option_prefix() const override { return "-"; }
|
||||
};
|
||||
|
||||
} // namespace lyra
|
||||
|
||||
#endif
|
@ -1,196 +0,0 @@
|
||||
// Copyright 2018-2022 René Ferdinand Rivera Morell
|
||||
// Copyright 2017 Two Blue Cubes Ltd. All rights reserved.
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef LYRA_DETAIL_FROM_STRING_HPP
|
||||
#define LYRA_DETAIL_FROM_STRING_HPP
|
||||
|
||||
#include "lyra/detail/trait_utils.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
#ifndef LYRA_CONFIG_OPTIONAL_TYPE
|
||||
# ifdef __has_include
|
||||
# if __has_include(<optional>) && __cplusplus >= 201703L
|
||||
# include <optional>
|
||||
# define LYRA_CONFIG_OPTIONAL_TYPE std::optional
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
namespace lyra { namespace detail {
|
||||
|
||||
template <typename T>
|
||||
bool to_string(const T & source, std::string & target)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << source;
|
||||
ss >> target;
|
||||
return !ss.fail();
|
||||
}
|
||||
|
||||
inline bool to_string(const std::string & source, std::string & target)
|
||||
{
|
||||
target = source;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool to_string(const char * source, std::string & target)
|
||||
{
|
||||
target = source;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool to_string(bool source, std::string & target)
|
||||
{
|
||||
target = source ? "true" : "false";
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef LYRA_CONFIG_OPTIONAL_TYPE
|
||||
template <typename T>
|
||||
inline bool to_string(
|
||||
LYRA_CONFIG_OPTIONAL_TYPE<T> & source, std::string & target)
|
||||
{
|
||||
if (source)
|
||||
return to_string(*source, target);
|
||||
else
|
||||
target = "<nullopt>";
|
||||
return true;
|
||||
}
|
||||
#endif // LYRA_CONFIG_OPTIONAL_TYPE
|
||||
|
||||
template <typename, typename = void>
|
||||
struct is_convertible_from_string : std::false_type
|
||||
{};
|
||||
|
||||
template <typename T>
|
||||
struct is_convertible_from_string<T,
|
||||
typename std::enable_if<std::is_arithmetic<T>::value>::type>
|
||||
: std::true_type
|
||||
{};
|
||||
|
||||
// Validates format of given value strings before conversion. This default
|
||||
// template return true always.
|
||||
template <typename, typename = void>
|
||||
struct validate_from_string
|
||||
{
|
||||
static bool validate(const std::string &) { return true; }
|
||||
};
|
||||
|
||||
// Validates that a to be parsed unsigned integer only contains number
|
||||
// digits.
|
||||
template <typename T>
|
||||
struct validate_from_string<T,
|
||||
typename std::enable_if<
|
||||
std::is_unsigned<typename detail::remove_cvref<T>::type>::value>::type>
|
||||
{
|
||||
static bool validate(const std::string & s)
|
||||
{
|
||||
return s.find_first_not_of("0123456789") == std::string::npos;
|
||||
}
|
||||
};
|
||||
|
||||
// Validates that a to be parsed signed integer only contains a sign and
|
||||
// number digits.
|
||||
template <typename T>
|
||||
struct validate_from_string<T,
|
||||
typename std::enable_if<
|
||||
std::is_integral<typename detail::remove_cvref<T>::type>::value
|
||||
&& std::is_signed<typename detail::remove_cvref<T>::type>::value>::type>
|
||||
{
|
||||
static bool validate(const std::string & s)
|
||||
{
|
||||
return s.find_first_not_of("-0123456789") == std::string::npos;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename S, typename T>
|
||||
inline bool from_string(S const & source, T & target)
|
||||
{
|
||||
std::stringstream ss;
|
||||
// Feed what we want to convert into the stream so that we can convert it
|
||||
// on extraction to the target type.
|
||||
ss << source;
|
||||
// Check that the source string data is valid. This check depends on the
|
||||
// target type.
|
||||
if (!validate_from_string<T>::validate(ss.str())) return false;
|
||||
T temp {};
|
||||
ss >> temp;
|
||||
if (!ss.fail() && ss.eof())
|
||||
{
|
||||
target = temp;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename S, typename... C>
|
||||
inline bool from_string(S const & source, std::basic_string<C...> & target)
|
||||
{
|
||||
to_string(source, target);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct is_convertible_from_string<T,
|
||||
typename std::enable_if<std::is_same<T, bool>::value>::type>
|
||||
: std::true_type
|
||||
{};
|
||||
|
||||
template <typename S>
|
||||
inline bool from_string(S const & source, bool & target)
|
||||
{
|
||||
std::string srcLC;
|
||||
to_string(source, srcLC);
|
||||
std::transform(srcLC.begin(), srcLC.end(), srcLC.begin(),
|
||||
[](char c) { return static_cast<char>(::tolower(c)); });
|
||||
if (srcLC == "y" || srcLC == "1" || srcLC == "true" || srcLC == "yes"
|
||||
|| srcLC == "on")
|
||||
target = true;
|
||||
else if (srcLC == "n" || srcLC == "0" || srcLC == "false" || srcLC == "no"
|
||||
|| srcLC == "off")
|
||||
target = false;
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef LYRA_CONFIG_OPTIONAL_TYPE
|
||||
template <typename T>
|
||||
struct is_convertible_from_string<T,
|
||||
typename std::enable_if<
|
||||
is_specialization_of<T, LYRA_CONFIG_OPTIONAL_TYPE>::value>::type>
|
||||
: std::true_type
|
||||
{};
|
||||
|
||||
template <typename S, typename T>
|
||||
inline bool from_string(S const & source, LYRA_CONFIG_OPTIONAL_TYPE<T> & target)
|
||||
{
|
||||
std::string srcLC;
|
||||
to_string(source, srcLC);
|
||||
std::transform(srcLC.begin(), srcLC.end(), srcLC.begin(),
|
||||
[](char c) { return static_cast<char>(::tolower(c)); });
|
||||
if (srcLC == "<nullopt>")
|
||||
{
|
||||
target.reset();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
T temp;
|
||||
auto str_result = from_string(source, temp);
|
||||
if (str_result) target = std::move(temp);
|
||||
return str_result;
|
||||
}
|
||||
}
|
||||
#endif // LYRA_CONFIG_OPTIONAL_TYPE
|
||||
|
||||
}} // namespace lyra::detail
|
||||
|
||||
#endif
|
@ -1,51 +0,0 @@
|
||||
// Copyright 2018-2022 René Ferdinand Rivera Morell
|
||||
// Copyright 2017 Two Blue Cubes Ltd. All rights reserved.
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef LYRA_DETAIL_INVOKE_LAMBDA_HPP
|
||||
#define LYRA_DETAIL_INVOKE_LAMBDA_HPP
|
||||
|
||||
#include "lyra/detail/parse.hpp"
|
||||
#include "lyra/detail/result.hpp"
|
||||
#include "lyra/detail/unary_lambda_traits.hpp"
|
||||
#include "lyra/parser_result.hpp"
|
||||
|
||||
namespace lyra { namespace detail {
|
||||
|
||||
template <typename ReturnType>
|
||||
struct LambdaInvoker
|
||||
{
|
||||
template <typename L, typename ArgType>
|
||||
static parser_result invoke(L const & lambda, ArgType const & arg)
|
||||
{
|
||||
return lambda(arg);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct LambdaInvoker<void>
|
||||
{
|
||||
template <typename L, typename ArgType>
|
||||
static parser_result invoke(L const & lambda, ArgType const & arg)
|
||||
{
|
||||
lambda(arg);
|
||||
return parser_result::ok(parser_result_type::matched);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ArgType, typename L>
|
||||
inline parser_result invokeLambda(L const & lambda, std::string const & arg)
|
||||
{
|
||||
ArgType temp {};
|
||||
auto p_result = parse_string(arg, temp);
|
||||
return !p_result
|
||||
? p_result
|
||||
: LambdaInvoker<typename unary_lambda_traits<L>::ReturnType>::invoke(
|
||||
lambda, temp);
|
||||
}
|
||||
|
||||
}} // namespace lyra::detail
|
||||
|
||||
#endif
|
@ -1,29 +0,0 @@
|
||||
// Copyright 2018-2022 René Ferdinand Rivera Morell
|
||||
// Copyright 2017 Two Blue Cubes Ltd. All rights reserved.
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef LYRA_DETAIL_PARSE_HPP
|
||||
#define LYRA_DETAIL_PARSE_HPP
|
||||
|
||||
#include "lyra/detail/from_string.hpp"
|
||||
#include "lyra/parser_result.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace lyra { namespace detail {
|
||||
|
||||
template <typename S, typename T>
|
||||
parser_result parse_string(S const & source, T & target)
|
||||
{
|
||||
if (from_string(source, target))
|
||||
return parser_result::ok(parser_result_type::matched);
|
||||
else
|
||||
return parser_result::error(parser_result_type::no_match,
|
||||
"Unable to convert '" + source + "' to destination type");
|
||||
}
|
||||
|
||||
}} // namespace lyra::detail
|
||||
|
||||
#endif
|
@ -1,79 +0,0 @@
|
||||
// Copyright 2021-2022 René Ferdinand Rivera Morell
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef LYRA_DETAIL_PRINT_HPP
|
||||
#define LYRA_DETAIL_PRINT_HPP
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#ifndef LYRA_DEBUG
|
||||
# define LYRA_DEBUG 0
|
||||
#endif
|
||||
|
||||
namespace lyra { namespace detail {
|
||||
|
||||
constexpr bool is_debug = LYRA_DEBUG;
|
||||
|
||||
template <typename T>
|
||||
std::string to_string(T && t)
|
||||
{
|
||||
return std::string(std::move(t));
|
||||
}
|
||||
|
||||
using std::to_string;
|
||||
|
||||
struct print
|
||||
{
|
||||
print(const char * scope_name = nullptr)
|
||||
: scope(scope_name)
|
||||
{
|
||||
if (is_debug) print::depth() += 1;
|
||||
if (scope) debug(scope, "...");
|
||||
}
|
||||
|
||||
~print()
|
||||
{
|
||||
if (scope) debug("...", scope);
|
||||
if (is_debug) print::depth() -= 1;
|
||||
}
|
||||
|
||||
template <typename... A>
|
||||
void debug(A... arg)
|
||||
{
|
||||
if (is_debug)
|
||||
{
|
||||
std::cerr << "[DEBUG]"
|
||||
<< std::string((print::depth() - 1) * 2, ' ');
|
||||
std::string args[] = { to_string(arg)... };
|
||||
for (auto & arg_string : args)
|
||||
{
|
||||
std::cerr << " " << arg_string;
|
||||
}
|
||||
std::cerr << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const char * scope;
|
||||
|
||||
static std::size_t & depth()
|
||||
{
|
||||
static std::size_t d = 0;
|
||||
return d;
|
||||
}
|
||||
};
|
||||
|
||||
}} // namespace lyra::detail
|
||||
|
||||
#if LYRA_DEBUG
|
||||
# define LYRA_PRINT_SCOPE ::lyra::detail::print lyra_print_scope
|
||||
# define LYRA_PRINT_DEBUG lyra_print_scope.debug
|
||||
#else
|
||||
# define LYRA_PRINT_SCOPE(...) while (false)
|
||||
# define LYRA_PRINT_DEBUG(...) while (false)
|
||||
#endif
|
||||
|
||||
#endif
|
@ -1,165 +0,0 @@
|
||||
// Copyright 2018-2022 René Ferdinand Rivera Morell
|
||||
// Copyright 2017 Two Blue Cubes Ltd. All rights reserved.
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef LYRA_DETAIL_RESULT_HPP
|
||||
#define LYRA_DETAIL_RESULT_HPP
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace lyra { namespace detail {
|
||||
|
||||
class result_base
|
||||
{
|
||||
public:
|
||||
result_base const & base() const { return *this; }
|
||||
explicit operator bool() const { return is_ok(); }
|
||||
bool is_ok() const { return kind_ == result_kind::ok; }
|
||||
std::string message() const { return message_; }
|
||||
[[deprecated]] std::string errorMessage() const { return message(); }
|
||||
|
||||
protected:
|
||||
enum class result_kind
|
||||
{
|
||||
ok,
|
||||
error
|
||||
};
|
||||
|
||||
explicit result_base(result_kind kind, const std::string & message = "")
|
||||
: kind_(kind)
|
||||
, message_(message)
|
||||
{}
|
||||
|
||||
explicit result_base(const result_base & other)
|
||||
: kind_(other.kind_)
|
||||
, message_(other.message_)
|
||||
{}
|
||||
|
||||
virtual ~result_base() = default;
|
||||
|
||||
result_base & operator=(const result_base &) = default;
|
||||
|
||||
private:
|
||||
result_kind kind_;
|
||||
std::string message_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class result_value_base : public result_base
|
||||
{
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
value_type const & value() const { return *value_; }
|
||||
bool has_value() const { return bool(value_); }
|
||||
|
||||
protected:
|
||||
std::unique_ptr<value_type> value_;
|
||||
|
||||
explicit result_value_base(
|
||||
result_kind kind, const std::string & message = "")
|
||||
: result_base(kind, message)
|
||||
{}
|
||||
|
||||
explicit result_value_base(result_kind kind,
|
||||
const value_type & val,
|
||||
const std::string & message = "")
|
||||
: result_base(kind, message)
|
||||
{
|
||||
value_.reset(new value_type(val));
|
||||
}
|
||||
|
||||
explicit result_value_base(result_value_base const & other)
|
||||
: result_base(other)
|
||||
{
|
||||
if (other.value_) value_.reset(new value_type(*other.value_));
|
||||
}
|
||||
|
||||
explicit result_value_base(const result_base & other)
|
||||
: result_base(other)
|
||||
{}
|
||||
|
||||
result_value_base & operator=(result_value_base const & other)
|
||||
{
|
||||
result_base::operator=(other);
|
||||
if (other.value_) value_.reset(new T(*other.value_));
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
class result_value_base<void> : public result_base
|
||||
{
|
||||
public:
|
||||
using value_type = void;
|
||||
|
||||
protected:
|
||||
// using result_base::result_base;
|
||||
explicit result_value_base(const result_base & other)
|
||||
: result_base(other)
|
||||
{}
|
||||
explicit result_value_base(
|
||||
result_kind kind, const std::string & message = "")
|
||||
: result_base(kind, message)
|
||||
{}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class basic_result : public result_value_base<T>
|
||||
{
|
||||
public:
|
||||
using value_type = typename result_value_base<T>::value_type;
|
||||
|
||||
explicit basic_result(result_base const & other)
|
||||
: result_value_base<T>(other)
|
||||
{}
|
||||
|
||||
// With-value results..
|
||||
|
||||
static basic_result ok(value_type const & val)
|
||||
{
|
||||
return basic_result(result_base::result_kind::ok, val);
|
||||
}
|
||||
|
||||
static basic_result error(
|
||||
value_type const & val, std::string const & message)
|
||||
{
|
||||
return basic_result(result_base::result_kind::error, val, message);
|
||||
}
|
||||
|
||||
protected:
|
||||
using result_value_base<T>::result_value_base;
|
||||
};
|
||||
|
||||
template <>
|
||||
class basic_result<void> : public result_value_base<void>
|
||||
{
|
||||
public:
|
||||
using value_type = typename result_value_base<void>::value_type;
|
||||
|
||||
explicit basic_result(result_base const & other)
|
||||
: result_value_base<void>(other)
|
||||
{}
|
||||
|
||||
// Value-less results.. (only kind as void is a value-less kind)
|
||||
|
||||
static basic_result ok()
|
||||
{
|
||||
return basic_result(result_base::result_kind::ok);
|
||||
}
|
||||
|
||||
static basic_result error(std::string const & message)
|
||||
{
|
||||
return basic_result(result_base::result_kind::error, message);
|
||||
}
|
||||
|
||||
protected:
|
||||
using result_value_base<void>::result_value_base;
|
||||
};
|
||||
|
||||
}} // namespace lyra::detail
|
||||
|
||||
#endif
|
@ -1,295 +0,0 @@
|
||||
// Copyright 2018-2022 René Ferdinand Rivera Morell
|
||||
// Copyright 2017 Two Blue Cubes Ltd. All rights reserved.
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef LYRA_DETAIL_TOKENS_HPP
|
||||
#define LYRA_DETAIL_TOKENS_HPP
|
||||
|
||||
#include "lyra/option_style.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace lyra { namespace detail {
|
||||
|
||||
// Wraps a token coming from a token stream. These may not directly
|
||||
// correspond to strings as a single string may encode an option + its
|
||||
// argument if the : or = form is used
|
||||
enum class token_type
|
||||
{
|
||||
unknown,
|
||||
option,
|
||||
argument
|
||||
};
|
||||
|
||||
template <typename Char, class Traits = std::char_traits<Char>>
|
||||
class basic_token_name
|
||||
{
|
||||
public:
|
||||
using traits_type = Traits;
|
||||
using value_type = Char;
|
||||
using pointer = value_type *;
|
||||
using const_pointer = const value_type *;
|
||||
using reference = value_type &;
|
||||
using const_reference = const value_type &;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using const_iterator = const_pointer;
|
||||
using iterator = const_iterator;
|
||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||
using reverse_iterator = const_reverse_iterator;
|
||||
using string_type = std::basic_string<value_type, traits_type>;
|
||||
|
||||
basic_token_name() noexcept
|
||||
: str { nullptr }
|
||||
, len { 0 }
|
||||
{}
|
||||
|
||||
basic_token_name(const basic_token_name &) noexcept = default;
|
||||
|
||||
basic_token_name(const_pointer s) noexcept
|
||||
: str { s }
|
||||
, len { traits_type::length(s) }
|
||||
{}
|
||||
|
||||
basic_token_name(const_pointer s, size_type count) noexcept
|
||||
: str { s }
|
||||
, len { count }
|
||||
{}
|
||||
|
||||
basic_token_name & operator=(const basic_token_name &) noexcept = default;
|
||||
|
||||
void swap(basic_token_name & other) noexcept
|
||||
{
|
||||
auto tmp = *this;
|
||||
*this = other;
|
||||
other = tmp;
|
||||
}
|
||||
|
||||
const_iterator begin() const noexcept { return this->str; }
|
||||
const_iterator end() const noexcept { return this->str + this->len; }
|
||||
const_iterator cbegin() const noexcept { return this->str; }
|
||||
const_iterator cend() const noexcept { return this->str + this->len; }
|
||||
|
||||
size_type size() const noexcept { return this->len; }
|
||||
size_type length() const noexcept { return this->len; }
|
||||
bool empty() const noexcept { return this->len == 0; }
|
||||
|
||||
friend string_type to_string(const basic_token_name & t)
|
||||
{
|
||||
return { t.str, t.len };
|
||||
}
|
||||
|
||||
friend string_type operator+(
|
||||
const_pointer lhs, const basic_token_name & rhs)
|
||||
{
|
||||
return lhs + to_string(rhs);
|
||||
}
|
||||
|
||||
private:
|
||||
const_pointer str;
|
||||
size_type len;
|
||||
};
|
||||
|
||||
// using token_name = basic_token_name<std::string::value_type>;
|
||||
using token_name = std::string;
|
||||
|
||||
struct token
|
||||
{
|
||||
token_type type;
|
||||
token_name name;
|
||||
|
||||
token()
|
||||
: type(token_type::unknown)
|
||||
{}
|
||||
token(const token & other) = default;
|
||||
token(token_type t, const token_name & n)
|
||||
: type(t)
|
||||
, name(n)
|
||||
{}
|
||||
|
||||
explicit operator bool() const { return type != token_type::unknown; }
|
||||
};
|
||||
|
||||
// Abstracts iterators into args with option arguments uniformly handled
|
||||
class token_iterator
|
||||
{
|
||||
public:
|
||||
template <typename Span>
|
||||
explicit token_iterator(Span const & args, const option_style & opt_style)
|
||||
: style(opt_style)
|
||||
, args_i(args.begin())
|
||||
, args_e(args.end())
|
||||
, args_i_sub(opt_style.short_option_size)
|
||||
{}
|
||||
|
||||
explicit operator bool() const noexcept { return args_i != args_e; }
|
||||
|
||||
token_iterator & pop(const token & arg_or_opt)
|
||||
{
|
||||
if (arg_or_opt.type == token_type::option && has_short_option_prefix())
|
||||
{
|
||||
// Multiple short options argument (-abc). Advance to the next
|
||||
// short option possible, or the next arg entirely.
|
||||
if (++args_i_sub >= args_i->size())
|
||||
{
|
||||
++args_i;
|
||||
args_i_sub = style.short_option_size;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Regular arg or long option, just advance to the next arg.
|
||||
++args_i;
|
||||
args_i_sub = style.short_option_size;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
token_iterator & pop(const token & /* opt */, const token & /* val */)
|
||||
{
|
||||
if (has_short_option_prefix() && args_i->size() > 2)
|
||||
++args_i;
|
||||
else if (!has_value_delimiter())
|
||||
args_i += 2;
|
||||
else
|
||||
++args_i;
|
||||
args_i_sub = style.short_option_size;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Current arg looks like an option, short or long.
|
||||
bool has_option_prefix() const noexcept
|
||||
{
|
||||
return has_long_option_prefix() || has_short_option_prefix();
|
||||
}
|
||||
|
||||
// Current arg looks like a short option (-o).
|
||||
bool has_short_option_prefix() const noexcept
|
||||
{
|
||||
return (args_i != args_e)
|
||||
&& is_prefixed(
|
||||
style.short_option_prefix, style.short_option_size, *args_i);
|
||||
}
|
||||
|
||||
// Current arg looks like a long option (--option).
|
||||
bool has_long_option_prefix() const noexcept
|
||||
{
|
||||
return (args_i != args_e)
|
||||
&& is_prefixed(
|
||||
style.long_option_prefix, style.long_option_size, *args_i);
|
||||
}
|
||||
|
||||
// Current arg looks like a delimited option+value (--option=x, -o=x)
|
||||
bool has_value_delimiter() const noexcept
|
||||
{
|
||||
return (args_i != args_e)
|
||||
&& (args_i->find_first_of(style.value_delimiters)
|
||||
!= std::string::npos);
|
||||
}
|
||||
|
||||
// Extract the current option token.
|
||||
token option() const
|
||||
{
|
||||
if (has_long_option_prefix())
|
||||
{
|
||||
if (has_value_delimiter())
|
||||
// --option=x
|
||||
return token(token_type::option,
|
||||
args_i->substr(
|
||||
0, args_i->find_first_of(style.value_delimiters)));
|
||||
else
|
||||
// --option
|
||||
return token(token_type::option, *args_i);
|
||||
}
|
||||
else if (has_short_option_prefix())
|
||||
{
|
||||
// -o (or possibly -abco)
|
||||
return { token_type::option,
|
||||
prefix_value(style.short_option_prefix, style.short_option_size)
|
||||
+ (*args_i)[args_i_sub] };
|
||||
}
|
||||
return token();
|
||||
}
|
||||
|
||||
// Extracts the option value if available. This will do any needed
|
||||
// lookahead through the args for the value.
|
||||
token value() const
|
||||
{
|
||||
if (has_option_prefix() && has_value_delimiter())
|
||||
// --option=x, -o=x
|
||||
return token(token_type::argument,
|
||||
args_i->substr(
|
||||
args_i->find_first_of(style.value_delimiters) + 1));
|
||||
else if (has_long_option_prefix())
|
||||
{
|
||||
if (args_i + 1 != args_e)
|
||||
// --option x
|
||||
return token(token_type::argument, *(args_i + 1));
|
||||
}
|
||||
else if (has_short_option_prefix())
|
||||
{
|
||||
if (args_i_sub + 1 < args_i->size())
|
||||
// -ox
|
||||
return token(
|
||||
token_type::argument, args_i->substr(args_i_sub + 1));
|
||||
else if (args_i + 1 != args_e)
|
||||
// -o x
|
||||
return token(token_type::argument, *(args_i + 1));
|
||||
}
|
||||
return token();
|
||||
}
|
||||
|
||||
token argument() const { return token(token_type::argument, *args_i); }
|
||||
|
||||
static bool is_prefixed(
|
||||
const std::string & prefix, std::size_t size, const std::string & s)
|
||||
{
|
||||
if (!prefix.empty() && size > 0 && s.size() > size)
|
||||
{
|
||||
for (auto c : prefix)
|
||||
{
|
||||
// Checks that the option looks like "[<c>]{size}[^<c>]".
|
||||
if (s[size] != c
|
||||
&& s.find_last_not_of(c, size - 1) == std::string::npos)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
const option_style & style;
|
||||
std::vector<std::string>::const_iterator args_i;
|
||||
std::vector<std::string>::const_iterator args_e;
|
||||
std::string::size_type args_i_sub;
|
||||
|
||||
inline bool is_opt_prefix(char c) const noexcept
|
||||
{
|
||||
return is_prefix_char(
|
||||
style.long_option_prefix, style.long_option_size, c)
|
||||
|| is_prefix_char(
|
||||
style.short_option_prefix, style.short_option_size, c);
|
||||
}
|
||||
|
||||
inline bool is_prefix_char(const std::string & prefix,
|
||||
std::size_t size,
|
||||
std::string::value_type c) const noexcept
|
||||
{
|
||||
return !prefix.empty() && size > 0
|
||||
&& prefix.find(c) != std::string::npos;
|
||||
}
|
||||
|
||||
inline std::string prefix_value(
|
||||
const std::string & prefix, std::size_t size) const
|
||||
{
|
||||
return std::string(
|
||||
static_cast<typename std::string::size_type>(size), prefix[0]);
|
||||
}
|
||||
};
|
||||
|
||||
}} // namespace lyra::detail
|
||||
|
||||
#endif
|
@ -1,74 +0,0 @@
|
||||
// Copyright 2020-2022 René Ferdinand Rivera Morell
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef LYRA_DETAIL_TRAIT_UTILS_HPP
|
||||
#define LYRA_DETAIL_TRAIT_UTILS_HPP
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace lyra { namespace detail {
|
||||
|
||||
// Checks that F can be called with arguments of type Args.
|
||||
// Credit for the technique goes to Richard Hodges (in SO post).
|
||||
template <class F, class... Args>
|
||||
struct is_callable
|
||||
{
|
||||
template <class U>
|
||||
static auto test(U * p)
|
||||
-> decltype((*p)(std::declval<Args>()...), void(), std::true_type());
|
||||
|
||||
template <class U>
|
||||
static auto test(...) -> decltype(std::false_type());
|
||||
|
||||
static constexpr bool value = decltype(test<F>(0))::value;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct remove_cvref
|
||||
{
|
||||
typedef
|
||||
typename std::remove_cv<typename std::remove_reference<T>::type>::type
|
||||
type;
|
||||
};
|
||||
|
||||
// Checks that F can be called, with an unspecified set of arguments.
|
||||
//
|
||||
// Currently this only detects function objects, like lambdas.
|
||||
// Where the operator() is not templated.
|
||||
template <class F>
|
||||
struct is_invocable
|
||||
{
|
||||
template <class U>
|
||||
static auto test(U * p)
|
||||
-> decltype((&U::operator()), void(), std::true_type());
|
||||
|
||||
template <class U>
|
||||
static auto test(...) -> decltype(std::false_type());
|
||||
|
||||
static constexpr bool value
|
||||
= decltype(test<typename remove_cvref<F>::type>(0))::value;
|
||||
};
|
||||
|
||||
// C++11 compatible void_t equivalent.
|
||||
template <typename... Ts>
|
||||
struct make_void
|
||||
{
|
||||
typedef void type;
|
||||
};
|
||||
template <typename... Ts>
|
||||
using valid_t = typename make_void<Ts...>::type;
|
||||
|
||||
// Borrowed from https://wg21.link/p2098r1
|
||||
template <class T, template <class...> class Primary>
|
||||
struct is_specialization_of : std::false_type
|
||||
{};
|
||||
template <template <class...> class Primary, class... Args>
|
||||
struct is_specialization_of<Primary<Args...>, Primary> : std::true_type
|
||||
{};
|
||||
|
||||
}} // namespace lyra::detail
|
||||
|
||||
#endif
|
@ -1,37 +0,0 @@
|
||||
// Copyright 2018-2022 René Ferdinand Rivera Morell
|
||||
// Copyright 2017 Two Blue Cubes Ltd. All rights reserved.
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef LYRA_DETAIL_UNARY_LAMBDA_TRAITS_HPP
|
||||
#define LYRA_DETAIL_UNARY_LAMBDA_TRAITS_HPP
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace lyra { namespace detail {
|
||||
|
||||
// Traits for extracting arg and return type of lambdas (for single argument
|
||||
// lambdas)
|
||||
template <typename L>
|
||||
struct unary_lambda_traits : unary_lambda_traits<decltype(&L::operator())>
|
||||
{};
|
||||
|
||||
template <typename ClassT, typename ReturnT, typename... Args>
|
||||
struct unary_lambda_traits<ReturnT (ClassT::*)(Args...) const>
|
||||
{
|
||||
static const bool isValid = false;
|
||||
};
|
||||
|
||||
template <typename ClassT, typename ReturnT, typename ArgT>
|
||||
struct unary_lambda_traits<ReturnT (ClassT::*)(ArgT) const>
|
||||
{
|
||||
static const bool isValid = true;
|
||||
using ArgType = typename std::remove_const<
|
||||
typename std::remove_reference<ArgT>::type>::type;
|
||||
using ReturnType = ReturnT;
|
||||
};
|
||||
|
||||
}} // namespace lyra::detail
|
||||
|
||||
#endif
|
@ -1,155 +0,0 @@
|
||||
// Copyright 2018-2022 René Ferdinand Rivera Morell
|
||||
// Copyright 2017 Two Blue Cubes Ltd. All rights reserved.
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef LYRA_EXE_NAME_HPP
|
||||
#define LYRA_EXE_NAME_HPP
|
||||
|
||||
#include "lyra/detail/bound.hpp"
|
||||
#include "lyra/detail/tokens.hpp"
|
||||
#include "lyra/parser.hpp"
|
||||
#include "lyra/parser_result.hpp"
|
||||
|
||||
namespace lyra {
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_exe_name]
|
||||
= `lyra::exe_name`
|
||||
|
||||
Specifies the name of the executable.
|
||||
|
||||
Is-a <<lyra_composable_parser>>.
|
||||
|
||||
end::reference[] */
|
||||
class exe_name : public composable_parser<exe_name>
|
||||
{
|
||||
public:
|
||||
exe_name()
|
||||
: m_name(std::make_shared<std::string>("<executable>"))
|
||||
{}
|
||||
|
||||
explicit exe_name(std::string & ref);
|
||||
|
||||
template <typename LambdaT>
|
||||
explicit exe_name(LambdaT const & lambda);
|
||||
|
||||
std::string name() const;
|
||||
parser_result set(std::string const & newName);
|
||||
|
||||
// The exe name is not parsed out of the normal tokens, but is handled
|
||||
// specially
|
||||
virtual parse_result parse(detail::token_iterator const & tokens,
|
||||
const option_style &) const override
|
||||
{
|
||||
return parse_result::ok(
|
||||
detail::parse_state(parser_result_type::no_match, tokens));
|
||||
}
|
||||
|
||||
virtual std::unique_ptr<parser> clone() const override
|
||||
{
|
||||
return make_clone<exe_name>(this);
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<std::string> m_name;
|
||||
std::shared_ptr<detail::BoundValueRefBase> m_ref;
|
||||
};
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_exe_name_ctor]
|
||||
== Construction
|
||||
|
||||
end::reference[] */
|
||||
|
||||
/* tag::reference[]
|
||||
[source]
|
||||
----
|
||||
exe_name::exe_name(std::string& ref)
|
||||
----
|
||||
|
||||
Constructs with a target string to receive the name of the executable. When
|
||||
the `cli` is run the target string will contain the exec name.
|
||||
|
||||
end::reference[] */
|
||||
inline exe_name::exe_name(std::string & ref)
|
||||
: exe_name()
|
||||
{
|
||||
m_ref = std::make_shared<detail::BoundValueRef<std::string>>(ref);
|
||||
}
|
||||
|
||||
/* tag::reference[]
|
||||
[source]
|
||||
----
|
||||
template <typename LambdaT>
|
||||
exe_name::exe_name(LambdaT const& lambda)
|
||||
----
|
||||
|
||||
Construct with a callback that is called with the value of the executable name
|
||||
when the `cli` runs.
|
||||
|
||||
end::reference[] */
|
||||
template <typename LambdaT>
|
||||
exe_name::exe_name(LambdaT const & lambda)
|
||||
: exe_name()
|
||||
{
|
||||
m_ref = std::make_shared<detail::BoundLambda<LambdaT>>(lambda);
|
||||
}
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_exe_name_accessors]
|
||||
== Accessors
|
||||
|
||||
end::reference[] */
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_exe_name_name]
|
||||
=== `lyra::exe_name::name`
|
||||
|
||||
[source]
|
||||
----
|
||||
std::string exe_name::name() const
|
||||
----
|
||||
|
||||
Returns the executable name when available. Otherwise it returns a default
|
||||
value.
|
||||
|
||||
end::reference[] */
|
||||
inline std::string exe_name::name() const { return *m_name; }
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_exe_name_set]
|
||||
=== `lyra::exe_name::set`
|
||||
|
||||
[source]
|
||||
----
|
||||
parser_result exe_name::set(std::string const& newName)
|
||||
----
|
||||
|
||||
Sets the executable name with the `newName` value. The new value is reflected
|
||||
in the bound string reference or callback.
|
||||
|
||||
end::reference[] */
|
||||
inline parser_result exe_name::set(std::string const & newName)
|
||||
{
|
||||
auto lastSlash = newName.find_last_of("\\/");
|
||||
auto filename = (lastSlash == std::string::npos)
|
||||
? newName
|
||||
: newName.substr(lastSlash + 1);
|
||||
|
||||
*m_name = filename;
|
||||
if (m_ref)
|
||||
return m_ref->setValue(filename);
|
||||
else
|
||||
return parser_result::ok(parser_result_type::matched);
|
||||
}
|
||||
|
||||
} // namespace lyra
|
||||
|
||||
#endif
|
@ -1,214 +0,0 @@
|
||||
// Copyright 2020-2022 René Ferdinand Rivera Morell
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef LYRA_GROUP_HPP
|
||||
#define LYRA_GROUP_HPP
|
||||
|
||||
#include "lyra/arguments.hpp"
|
||||
#include "lyra/detail/print.hpp"
|
||||
#include <functional>
|
||||
|
||||
namespace lyra {
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_group]
|
||||
= `lyra::group`
|
||||
|
||||
A group of arguments provides for parsing, optionally, a set of arguments
|
||||
together. The group itself is considered successfully parsed only when the
|
||||
arguments in the group are parsed without errors. A common use case for this
|
||||
are sub-commands. This implementation is recursive. And hence allows groups
|
||||
within groups for describing branching argument parsing.
|
||||
|
||||
Is-a <<lyra_arguments>>.
|
||||
|
||||
end::reference[] */
|
||||
class group : public arguments
|
||||
{
|
||||
public:
|
||||
group();
|
||||
group(const group & other);
|
||||
explicit group(const std::function<void(const group &)> & f);
|
||||
|
||||
virtual bool is_group() const override { return true; }
|
||||
|
||||
parse_result parse(detail::token_iterator const & tokens,
|
||||
const option_style & style) const override
|
||||
{
|
||||
LYRA_PRINT_SCOPE("group::parse");
|
||||
LYRA_PRINT_DEBUG("(?)", get_usage_text(style),
|
||||
"?=", tokens ? tokens.argument().name : "");
|
||||
parse_result p_result = arguments::parse(tokens, style);
|
||||
if (p_result && p_result.value().type() != parser_result_type::no_match
|
||||
&& success_signal)
|
||||
{
|
||||
// Trigger any success signal for parsing the argument as the group.
|
||||
// This allows for executing handlers for commands.
|
||||
this->success_signal(*this);
|
||||
}
|
||||
if (!p_result)
|
||||
{
|
||||
LYRA_PRINT_DEBUG("(!)", get_usage_text(style),
|
||||
"!=", tokens ? tokens.argument().name : "");
|
||||
}
|
||||
else
|
||||
{
|
||||
LYRA_PRINT_DEBUG("(=)", get_usage_text(style),
|
||||
"==", tokens ? tokens.argument().name : "", "==>",
|
||||
p_result.value().type());
|
||||
}
|
||||
return p_result;
|
||||
}
|
||||
|
||||
group & optional();
|
||||
group & required(size_t n = 1);
|
||||
group & cardinality(size_t n);
|
||||
group & cardinality(size_t n, size_t m);
|
||||
detail::parser_cardinality cardinality() const override
|
||||
{
|
||||
return m_cardinality;
|
||||
}
|
||||
|
||||
virtual std::unique_ptr<parser> clone() const override
|
||||
{
|
||||
return make_clone<group>(this);
|
||||
}
|
||||
|
||||
private:
|
||||
std::function<void(const group &)> success_signal;
|
||||
detail::parser_cardinality m_cardinality = { 0, 1 };
|
||||
};
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_group_ctor]
|
||||
== Construction
|
||||
|
||||
end::reference[] */
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_group_ctor_default]
|
||||
=== Default
|
||||
|
||||
[source]
|
||||
----
|
||||
group();
|
||||
----
|
||||
|
||||
Default constructing a `group` does not register the success callback.
|
||||
|
||||
end::reference[] */
|
||||
inline group::group()
|
||||
: m_cardinality(0, 1)
|
||||
{}
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_group_ctor_copy]
|
||||
=== Copy
|
||||
|
||||
[source]
|
||||
----
|
||||
group::group(const group & other);
|
||||
----
|
||||
|
||||
end::reference[] */
|
||||
inline group::group(const group & other)
|
||||
: arguments(other)
|
||||
, success_signal(other.success_signal)
|
||||
, m_cardinality(other.m_cardinality)
|
||||
{}
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_group_ctor_success]
|
||||
=== Success Handler
|
||||
|
||||
[source]
|
||||
----
|
||||
group::group(const std::function<void(const group &)> & f)
|
||||
----
|
||||
|
||||
Registers a function to call when the group is successfully parsed. The
|
||||
function is called with the group to facilitate customization.
|
||||
|
||||
end::reference[] */
|
||||
inline group::group(const std::function<void(const group &)> & f)
|
||||
: success_signal(f)
|
||||
{}
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_group_optional]
|
||||
=== `lyra::group::optional`
|
||||
|
||||
[source]
|
||||
----
|
||||
group & group::optional();
|
||||
----
|
||||
|
||||
Indicates that the argument is optional. This is equivalent to specifying
|
||||
`cardinality(0, 1)`.
|
||||
|
||||
end::reference[] */
|
||||
inline group & group::optional()
|
||||
{
|
||||
m_cardinality.optional();
|
||||
return *this;
|
||||
}
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_group_required]
|
||||
=== `lyra::group::required(n)`
|
||||
|
||||
[source]
|
||||
----
|
||||
group & group::required(size_t n);
|
||||
----
|
||||
|
||||
Specifies that the argument needs to given the number of `n` times
|
||||
(defaults to *1*).
|
||||
|
||||
end::reference[] */
|
||||
inline group & group::required(size_t n)
|
||||
{
|
||||
m_cardinality.required(n);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_group_cardinality]
|
||||
=== `lyra::group::cardinality(n)`
|
||||
|
||||
[source]
|
||||
----
|
||||
group & group::cardinality(size_t n);
|
||||
group & group::cardinality(size_t n, size_t m);
|
||||
----
|
||||
|
||||
Specifies the number of times the argument can and needs to appear in the list
|
||||
of arguments. In the first form the argument can appear exactly `n` times. In
|
||||
the second form it specifies that the argument can appear from `n` to `m` times
|
||||
inclusive.
|
||||
|
||||
end::reference[] */
|
||||
inline group & group::cardinality(size_t n)
|
||||
{
|
||||
m_cardinality.counted(n);
|
||||
return *this;
|
||||
}
|
||||
inline group & group::cardinality(size_t n, size_t m)
|
||||
{
|
||||
m_cardinality.bounded(n, m);
|
||||
return *this;
|
||||
}
|
||||
|
||||
} // namespace lyra
|
||||
|
||||
#endif
|
@ -1,78 +0,0 @@
|
||||
// Copyright 2018-2022 René Ferdinand Rivera Morell
|
||||
// Copyright 2017 Two Blue Cubes Ltd. All rights reserved.
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef LYRA_HELP_HPP
|
||||
#define LYRA_HELP_HPP
|
||||
|
||||
#include "lyra/opt.hpp"
|
||||
|
||||
namespace lyra {
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_help]
|
||||
= `lyra::help`
|
||||
|
||||
Utility function that defines a default `--help` option. You can specify a
|
||||
`bool` flag to indicate if the help option was specified and that you could
|
||||
display a help message.
|
||||
|
||||
The option accepts `-?`, `-h`, and `--help` as allowed option names.
|
||||
|
||||
*/ // end::reference[]
|
||||
class help : public opt
|
||||
{
|
||||
public:
|
||||
help(bool & showHelpFlag)
|
||||
: opt([&](bool flag) {
|
||||
showHelpFlag = flag;
|
||||
return parser_result::ok(parser_result_type::short_circuit_all);
|
||||
})
|
||||
{
|
||||
this->description("Display usage information.")
|
||||
.optional()
|
||||
.name("-?")
|
||||
.name("-h")
|
||||
.name("--help");
|
||||
}
|
||||
|
||||
help & description(const std::string & text);
|
||||
|
||||
virtual std::string get_description_text(
|
||||
const option_style &) const override
|
||||
{
|
||||
return description_text;
|
||||
}
|
||||
|
||||
virtual std::unique_ptr<parser> clone() const override
|
||||
{
|
||||
return make_clone<help>(this);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string description_text;
|
||||
};
|
||||
|
||||
/* tag::reference[]
|
||||
[source]
|
||||
----
|
||||
help & help::description(const std::string &text)
|
||||
----
|
||||
|
||||
Sets the given `text` as the general description to show with the help and
|
||||
usage output for CLI parser. This text is displayed between the "Usage"
|
||||
and "Options, arguments" sections.
|
||||
|
||||
end::reference[] */
|
||||
inline help & help::description(const std::string & text)
|
||||
{
|
||||
description_text = text;
|
||||
return *this;
|
||||
}
|
||||
|
||||
} // namespace lyra
|
||||
|
||||
#endif
|
@ -1,149 +0,0 @@
|
||||
// Copyright 2020-2022 René Ferdinand Rivera Morell
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef LYRA_LITERAL_HPP
|
||||
#define LYRA_LITERAL_HPP
|
||||
|
||||
#include "lyra/parser.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace lyra {
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_literal]
|
||||
= `lyra::literal`
|
||||
|
||||
A parser that matches a single fixed value.
|
||||
|
||||
Is-a <<lyra_parser>>.
|
||||
|
||||
end::reference[] */
|
||||
class literal : public parser
|
||||
{
|
||||
public:
|
||||
// Construction.
|
||||
literal(std::string const & n);
|
||||
|
||||
// Help description.
|
||||
literal & help(const std::string & help_description_text);
|
||||
literal & operator()(std::string const & help_description_text);
|
||||
|
||||
// Singular argument allowed and required.
|
||||
virtual detail::parser_cardinality cardinality() const override
|
||||
{
|
||||
return { 1, 1 };
|
||||
}
|
||||
|
||||
// Internal.
|
||||
|
||||
virtual std::string get_usage_text(const option_style &) const override
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
virtual std::string get_description_text(
|
||||
const option_style &) const override
|
||||
{
|
||||
return description;
|
||||
}
|
||||
|
||||
virtual help_text get_help_text(const option_style &) const override
|
||||
{
|
||||
return { { name, description } };
|
||||
}
|
||||
|
||||
using parser::parse;
|
||||
|
||||
virtual parse_result parse(detail::token_iterator const & tokens,
|
||||
const option_style &) const override
|
||||
{
|
||||
auto validationResult = validate();
|
||||
if (!validationResult) return parse_result(validationResult);
|
||||
|
||||
auto const & token = tokens.argument();
|
||||
if (name == token.name)
|
||||
{
|
||||
auto remainingTokens = tokens;
|
||||
remainingTokens.pop(token);
|
||||
return parse_result::ok(detail::parse_state(
|
||||
parser_result_type::matched, remainingTokens));
|
||||
}
|
||||
else
|
||||
{
|
||||
return parse_result(parser_result::error(
|
||||
parser_result_type::no_match, "Expected '" + name + "'."));
|
||||
}
|
||||
}
|
||||
|
||||
virtual std::unique_ptr<parser> clone() const override
|
||||
{
|
||||
return make_clone<literal>(this);
|
||||
}
|
||||
|
||||
protected:
|
||||
std::string name;
|
||||
std::string description;
|
||||
};
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_literal_ctor]
|
||||
== Construction
|
||||
|
||||
end::reference[] */
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
=== Token
|
||||
|
||||
[#lyra_literal_ctor_token]
|
||||
|
||||
[source]
|
||||
----
|
||||
inline literal::literal(std::string const& n)
|
||||
----
|
||||
|
||||
Constructs the literal with the name of the token to match.
|
||||
|
||||
end::reference[] */
|
||||
inline literal::literal(std::string const & n)
|
||||
: name(n)
|
||||
{}
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_literal_specification]
|
||||
== Specification
|
||||
|
||||
end::reference[] */
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_literal_help]
|
||||
=== `lyra:literal::help`
|
||||
|
||||
[source]
|
||||
----
|
||||
literal& literal::help(const std::string& help_description_text)
|
||||
literal& literal::operator()(std::string const& help_description_text)
|
||||
----
|
||||
|
||||
Specify a help description for the literal.
|
||||
|
||||
end::reference[] */
|
||||
inline literal & literal::help(const std::string & help_description_text)
|
||||
{
|
||||
description = help_description_text;
|
||||
return *this;
|
||||
}
|
||||
inline literal & literal::operator()(std::string const & help_description_text)
|
||||
{
|
||||
return this->help(help_description_text);
|
||||
}
|
||||
|
||||
} // namespace lyra
|
||||
|
||||
#endif
|
@ -1,28 +0,0 @@
|
||||
// Copyright 2018-2022 René Ferdinand Rivera Morell
|
||||
// Copyright 2017 Two Blue Cubes Ltd. All rights reserved.
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef LYRA_LYRA_HPP
|
||||
#define LYRA_LYRA_HPP
|
||||
|
||||
#include "lyra/version.hpp"
|
||||
|
||||
#include "lyra/arg.hpp"
|
||||
#include "lyra/arguments.hpp"
|
||||
#include "lyra/cli.hpp"
|
||||
#include "lyra/cli_parser.hpp"
|
||||
#include "lyra/command.hpp"
|
||||
#include "lyra/exe_name.hpp"
|
||||
#include "lyra/group.hpp"
|
||||
#include "lyra/help.hpp"
|
||||
#include "lyra/literal.hpp"
|
||||
#include "lyra/main.hpp"
|
||||
#include "lyra/opt.hpp"
|
||||
#include "lyra/option_style.hpp"
|
||||
#include "lyra/parser.hpp"
|
||||
#include "lyra/parser_result.hpp"
|
||||
#include "lyra/val.hpp"
|
||||
|
||||
#endif // LYRA_HPP_INCLUDED
|
@ -1,244 +0,0 @@
|
||||
// Copyright 2019-2022 René Ferdinand Rivera Morell
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef LYRA_MAIN_HPP
|
||||
#define LYRA_MAIN_HPP
|
||||
|
||||
#include "lyra/arg.hpp"
|
||||
#include "lyra/args.hpp"
|
||||
#include "lyra/cli.hpp"
|
||||
#include "lyra/help.hpp"
|
||||
#include "lyra/opt.hpp"
|
||||
#include "lyra/parser.hpp"
|
||||
#include "lyra/val.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace lyra {
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_main]
|
||||
= `lyra::main`
|
||||
|
||||
Encapsulates the common use case of a main program that has a help option and
|
||||
has a minimal way to specify and parse options. This provides for a way to
|
||||
specify options and arguments in a simple function form. It handles checking for
|
||||
errors and reporting problems.
|
||||
|
||||
*/ // end::reference[]
|
||||
class main final : protected cli
|
||||
{
|
||||
bool show_help = false;
|
||||
|
||||
public:
|
||||
explicit main(const std::string & text = "");
|
||||
|
||||
template <typename T>
|
||||
main & operator()(const T & parser);
|
||||
template <typename T>
|
||||
main & add_argument(const T & parser);
|
||||
template <typename T>
|
||||
main & operator|=(const T & parser);
|
||||
|
||||
template <typename V>
|
||||
main & operator()(
|
||||
std::initializer_list<std::string> arg_names, V && default_value);
|
||||
template <typename V>
|
||||
main & operator()(const std::string & arg_name, V && default_value);
|
||||
|
||||
template <typename L>
|
||||
int operator()(const args & argv, L action);
|
||||
template <typename L>
|
||||
int operator()(int argc, const char ** argv, L action);
|
||||
|
||||
using cli::operator[];
|
||||
|
||||
main & style(const option_style & style)
|
||||
{
|
||||
cli::style(style);
|
||||
return *this;
|
||||
}
|
||||
main & style(option_style && style)
|
||||
{
|
||||
cli::style(style);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_main_ctor]
|
||||
== Construction
|
||||
|
||||
[source]
|
||||
----
|
||||
main::main(const std::string & text);
|
||||
----
|
||||
|
||||
Construct with text for description, which defaults to an empty string. The
|
||||
description is specified for the help option that is added to the command line.
|
||||
|
||||
end::reference[] */
|
||||
inline main::main(const std::string & text)
|
||||
{
|
||||
this->add_argument(help(show_help).description(text));
|
||||
}
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_main_add_argument]
|
||||
== Add Argument
|
||||
|
||||
[source]
|
||||
----
|
||||
template <typename T> main & main::operator()(const T & arg_parser)
|
||||
template <typename T> main & main::add_argument(const T & arg_parser)
|
||||
template <typename T> main & main::operator|=(const T & arg_parser)
|
||||
----
|
||||
|
||||
Adds a parser as an argument to the command line. These forward directly to the
|
||||
`lyra::cli` equivalents. The added parser can be any of the regular Lyra parsers
|
||||
like `lyra::opt` or `lyra::arg`.
|
||||
|
||||
end::reference[] */
|
||||
template <typename T>
|
||||
main & main::operator()(const T & arg_parser)
|
||||
{
|
||||
cli::add_argument(arg_parser);
|
||||
return *this;
|
||||
}
|
||||
template <typename T>
|
||||
main & main::add_argument(const T & arg_parser)
|
||||
{
|
||||
cli::add_argument(arg_parser);
|
||||
return *this;
|
||||
}
|
||||
template <typename T>
|
||||
main & main::operator|=(const T & arg_parser)
|
||||
{
|
||||
cli::operator|=(arg_parser);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_main_simple_args]
|
||||
== Simple Args
|
||||
|
||||
[source]
|
||||
----
|
||||
template <typename V>
|
||||
main & main::operator()(
|
||||
std::initializer_list<std::string> arg_names, V && default_value)
|
||||
template <typename V>
|
||||
main & main::operator()(const std::string & arg_name, V && default_value)
|
||||
----
|
||||
|
||||
Specifies, and adds, a new argument. Depending on the `arg_names` it can be
|
||||
either a `lyra::opt` or `lyra::arg`. The first item in `arg_names` indicates
|
||||
the type of argument created and added:
|
||||
|
||||
Specify either `-<name>` or `--<name>` to add a `lyra::opt`. You can specify as
|
||||
many option names following the first name. A name that doesn't follow the
|
||||
option syntax is considered the as the help text for the option.
|
||||
|
||||
Specify a non `-` prefixed name as the first item to signify a positional
|
||||
`lyra::arg`.
|
||||
|
||||
The single `std::string` call is equivalent to specifying just the one option or
|
||||
argument.
|
||||
|
||||
Example specifications:
|
||||
|
||||
|===
|
||||
| `("-o", 0)` | Short `-o` option as `int` value.
|
||||
| `("--opt", 0)` | Long `--opt` option as `int` value.
|
||||
| `({"-o", "--opt"}, 1.0f)` | Short and long option as `float` value.
|
||||
| `({"-o", "The option."}, 1.0f)` | Short option and help description as `float`
|
||||
value.
|
||||
| `("opt", 2)` | Positional, i.e. `lyra::arg`, argument as `int` value.
|
||||
| `({"opt", "The option."}, 2)` | Positional argument and help description as
|
||||
`int` value.
|
||||
| `("--opt", std::vector<float>())` | Long option with as multiple
|
||||
float values.
|
||||
|===
|
||||
|
||||
end::reference[] */
|
||||
template <typename V>
|
||||
main & main::operator()(
|
||||
std::initializer_list<std::string> arg_names, V && default_value)
|
||||
{
|
||||
auto bound_val = val(std::forward<V>(default_value));
|
||||
if ((*arg_names.begin())[0] == '-')
|
||||
{
|
||||
// An option to add.
|
||||
std::string hint = arg_names.begin()->substr(1);
|
||||
if (hint[0] == '-') hint = hint.substr(1);
|
||||
opt o(std::move(bound_val), hint);
|
||||
for (auto arg_name : arg_names)
|
||||
{
|
||||
if (arg_name[0] == '-')
|
||||
o.name(arg_name);
|
||||
else
|
||||
o.help(arg_name);
|
||||
}
|
||||
cli::add_argument(o);
|
||||
}
|
||||
else
|
||||
{
|
||||
// An argument to add.
|
||||
arg a(std::move(bound_val), *arg_names.begin());
|
||||
a.optional();
|
||||
if (arg_names.size() > 2) a.help(*(arg_names.begin() + 1));
|
||||
cli::add_argument(a);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
template <typename V>
|
||||
main & main::operator()(const std::string & arg_name, V && default_value)
|
||||
{
|
||||
return (*this)({ arg_name }, std::forward<V>(default_value));
|
||||
}
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_main_execute]
|
||||
== Execute
|
||||
|
||||
[source]
|
||||
----
|
||||
template <typename L>
|
||||
int main::operator()(const args & argv, L action)
|
||||
template <typename L>
|
||||
int main::operator()(int argc, const char ** argv, L action)
|
||||
----
|
||||
|
||||
Executes the given action after parsing of the program input arguments. It
|
||||
returns either `0` or `1` if the execution was successful or failed
|
||||
respectively. The `action` is called with the `lyra::main` instance to provide
|
||||
access to the parsed argument values.
|
||||
|
||||
end::reference[] */
|
||||
template <typename L>
|
||||
int main::operator()(const args & argv, L action)
|
||||
{
|
||||
auto cli_result = cli::parse(argv);
|
||||
if (!cli_result) std::cerr << cli_result.message() << "\n\n";
|
||||
if (show_help || !cli_result)
|
||||
std::cout << *this << "\n";
|
||||
else
|
||||
return action(*this);
|
||||
return cli_result ? 0 : 1;
|
||||
}
|
||||
template <typename L>
|
||||
int main::operator()(int argc, const char ** argv, L action)
|
||||
{
|
||||
return (*this)({ argc, argv }, action);
|
||||
}
|
||||
|
||||
} // namespace lyra
|
||||
|
||||
#endif
|
@ -1,372 +0,0 @@
|
||||
// Copyright 2018-2022 René Ferdinand Rivera Morell
|
||||
// Copyright 2017 Two Blue Cubes Ltd. All rights reserved.
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef LYRA_OPT_HPP
|
||||
#define LYRA_OPT_HPP
|
||||
|
||||
#include "lyra/detail/print.hpp"
|
||||
#include "lyra/detail/trait_utils.hpp"
|
||||
#include "lyra/parser.hpp"
|
||||
#include "lyra/val.hpp"
|
||||
#include <memory>
|
||||
|
||||
namespace lyra {
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_opt]
|
||||
= `lyra::opt`
|
||||
|
||||
A parser for one option with multiple possible names. The option value(s) are
|
||||
communicated through a reference to a variable, a container, or a callback.
|
||||
|
||||
Is-a <<lyra_bound_parser>>.
|
||||
|
||||
end::reference[] */
|
||||
class opt : public bound_parser<opt>
|
||||
{
|
||||
public:
|
||||
enum class ctor_lambda_e
|
||||
{
|
||||
val
|
||||
};
|
||||
enum class ctor_ref_e
|
||||
{
|
||||
val
|
||||
};
|
||||
|
||||
// Flag option ctors..
|
||||
|
||||
explicit opt(bool & ref);
|
||||
|
||||
template <typename L>
|
||||
explicit opt(L const & ref,
|
||||
typename std::enable_if<detail::is_invocable<L>::value,
|
||||
ctor_lambda_e>::type
|
||||
= ctor_lambda_e::val);
|
||||
|
||||
// Value option ctors..
|
||||
|
||||
template <typename T>
|
||||
opt(T & ref,
|
||||
std::string const & hint,
|
||||
typename std::enable_if<!detail::is_invocable<T>::value,
|
||||
ctor_ref_e>::type
|
||||
= ctor_ref_e::val);
|
||||
|
||||
template <typename L>
|
||||
opt(L const & ref,
|
||||
std::string const & hint,
|
||||
typename std::enable_if<detail::is_invocable<L>::value,
|
||||
ctor_lambda_e>::type
|
||||
= ctor_lambda_e::val);
|
||||
|
||||
// Bound value ctors..
|
||||
template <typename T>
|
||||
explicit opt(detail::BoundVal<T> && val)
|
||||
: bound_parser(val.move_to_shared())
|
||||
{}
|
||||
template <typename T>
|
||||
explicit opt(detail::BoundVal<T> && val, std::string const & hint)
|
||||
: bound_parser(val.move_to_shared(), hint)
|
||||
{}
|
||||
|
||||
// Option specifications..
|
||||
|
||||
opt & name(const std::string & opt_name);
|
||||
opt & operator[](std::string const & opt_name);
|
||||
|
||||
// Internal..
|
||||
|
||||
virtual std::string get_usage_text(
|
||||
const option_style & style) const override
|
||||
{
|
||||
std::string usage;
|
||||
for (std::size_t o = 0; o < opt_names.size(); ++o)
|
||||
{
|
||||
if (o > 0) usage += "|";
|
||||
usage += format_opt(opt_names[o], style);
|
||||
}
|
||||
if (!m_hint.empty()) usage += " <" + m_hint + ">";
|
||||
return usage;
|
||||
}
|
||||
|
||||
virtual help_text get_help_text(const option_style & style) const override
|
||||
{
|
||||
std::ostringstream oss;
|
||||
bool first = true;
|
||||
for (auto const & opt_name : opt_names)
|
||||
{
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
oss << ", ";
|
||||
oss << format_opt(opt_name, style);
|
||||
}
|
||||
if (!m_hint.empty()) oss << " <" << m_hint << ">";
|
||||
return { { oss.str(), m_description } };
|
||||
}
|
||||
|
||||
virtual bool is_named(const std::string & n) const override
|
||||
{
|
||||
return bound_parser::is_named(n)
|
||||
|| (std::find(opt_names.begin(), opt_names.end(), n)
|
||||
!= opt_names.end());
|
||||
}
|
||||
|
||||
using parser::parse;
|
||||
|
||||
parse_result parse(detail::token_iterator const & tokens,
|
||||
const option_style & style) const override
|
||||
{
|
||||
LYRA_PRINT_SCOPE("opt::parse");
|
||||
auto validationResult = validate();
|
||||
if (!validationResult) return parse_result(validationResult);
|
||||
|
||||
auto remainingTokens = tokens;
|
||||
if (remainingTokens && remainingTokens.has_option_prefix())
|
||||
{
|
||||
auto const & token = remainingTokens.option();
|
||||
if (is_match(token.name, style))
|
||||
{
|
||||
if (m_ref->isFlag())
|
||||
{
|
||||
if (remainingTokens.has_value_delimiter())
|
||||
{
|
||||
return parse_result::error(
|
||||
{ parser_result_type::short_circuit_all,
|
||||
remainingTokens },
|
||||
"Flag option '" + token.name + "' contains value '"
|
||||
+ remainingTokens.value().name + "'.");
|
||||
}
|
||||
remainingTokens.pop(token);
|
||||
auto flagRef
|
||||
= static_cast<detail::BoundFlagRefBase *>(m_ref.get());
|
||||
auto flag_result = flagRef->setFlag(true);
|
||||
if (!flag_result) return parse_result(flag_result);
|
||||
LYRA_PRINT_DEBUG(
|
||||
"(=)", get_usage_text(style), "==", token.name);
|
||||
if (flag_result.value() == parser_result_type::short_circuit_all)
|
||||
return parse_result::ok(detail::parse_state(
|
||||
flag_result.value(), remainingTokens));
|
||||
}
|
||||
else
|
||||
{
|
||||
auto const & argToken = remainingTokens.value();
|
||||
if (argToken.type == detail::token_type::unknown)
|
||||
return parse_result::error(
|
||||
{ parser_result_type::no_match, remainingTokens },
|
||||
"Expected argument following " + token.name);
|
||||
remainingTokens.pop(token, argToken);
|
||||
auto valueRef
|
||||
= static_cast<detail::BoundValueRefBase *>(m_ref.get());
|
||||
if (value_choices)
|
||||
{
|
||||
auto choice_result
|
||||
= value_choices->contains_value(argToken.name);
|
||||
if (!choice_result) return parse_result(choice_result);
|
||||
}
|
||||
auto v_result = valueRef->setValue(argToken.name);
|
||||
if (!v_result)
|
||||
{
|
||||
// Matched the option, but not the value. This is a
|
||||
// hard fail that needs to skip subsequent parsing.
|
||||
return parse_result::error(
|
||||
{ parser_result_type::short_circuit_all,
|
||||
remainingTokens },
|
||||
v_result.message());
|
||||
}
|
||||
LYRA_PRINT_DEBUG("(=)", get_usage_text(style),
|
||||
"==", token.name, argToken.name);
|
||||
if (v_result.value() == parser_result_type::short_circuit_all)
|
||||
return parse_result::ok(detail::parse_state(
|
||||
v_result.value(), remainingTokens));
|
||||
}
|
||||
return parse_result::ok(detail::parse_state(
|
||||
parser_result_type::matched, remainingTokens));
|
||||
}
|
||||
LYRA_PRINT_DEBUG("(!)", get_usage_text(style), "!= ", token.name);
|
||||
}
|
||||
else
|
||||
{
|
||||
LYRA_PRINT_DEBUG("(!)", get_usage_text(style),
|
||||
"!=", remainingTokens.argument().name);
|
||||
}
|
||||
|
||||
return parse_result::ok(
|
||||
detail::parse_state(parser_result_type::no_match, remainingTokens));
|
||||
}
|
||||
|
||||
result validate() const override
|
||||
{
|
||||
if (opt_names.empty())
|
||||
return result::error("No options supplied to opt");
|
||||
if (m_ref->isFlag() && value_choices)
|
||||
return result::error("Flag options cannot contain choices.");
|
||||
for (auto const & name : opt_names)
|
||||
{
|
||||
if (name.empty())
|
||||
return result::error("Option name cannot be empty");
|
||||
if (name[0] != '-')
|
||||
return result::error("Option name must begin with '-'");
|
||||
}
|
||||
return bound_parser::validate();
|
||||
}
|
||||
|
||||
virtual std::unique_ptr<parser> clone() const override
|
||||
{
|
||||
return make_clone<opt>(this);
|
||||
}
|
||||
|
||||
protected:
|
||||
std::vector<std::string> opt_names;
|
||||
|
||||
bool is_match(
|
||||
std::string const & opt_name, const option_style & style) const
|
||||
{
|
||||
auto opt_normalized = normalise_opt(opt_name, style);
|
||||
for (auto const & name : opt_names)
|
||||
{
|
||||
if (normalise_opt(name, style) == opt_normalized) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string normalise_opt(
|
||||
std::string const & opt_name, const option_style & style) const
|
||||
{
|
||||
if (detail::token_iterator::is_prefixed(
|
||||
style.short_option_prefix, style.short_option_size, opt_name))
|
||||
return std::string("-") + opt_name.substr(style.short_option_size);
|
||||
|
||||
if (detail::token_iterator::is_prefixed(
|
||||
style.long_option_prefix, style.long_option_size, opt_name))
|
||||
return std::string("--") + opt_name.substr(style.long_option_size);
|
||||
|
||||
return opt_name;
|
||||
}
|
||||
|
||||
std::string format_opt(
|
||||
std::string const & opt_name, const option_style & style) const
|
||||
{
|
||||
if (opt_name[0] == '-' && opt_name[1] == '-')
|
||||
return style.long_option_string() + opt_name.substr(2);
|
||||
else if (opt_name[0] == '-')
|
||||
return style.short_option_string() + opt_name.substr(1);
|
||||
else
|
||||
return opt_name;
|
||||
}
|
||||
};
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_opt_ctor]
|
||||
== Construction
|
||||
|
||||
end::reference[] */
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_opt_ctor_flags]
|
||||
=== Flags
|
||||
|
||||
[source]
|
||||
----
|
||||
lyra::opt::opt(bool& ref);
|
||||
|
||||
template <typename L>
|
||||
lyra::opt::opt(L const& ref);
|
||||
----
|
||||
|
||||
Constructs a flag option with a target `bool` to indicate if the flag is
|
||||
present. The first form takes a reference to a variable to receive the
|
||||
`bool`. The second takes a callback that is called with `true` when the
|
||||
option is present.
|
||||
|
||||
end::reference[] */
|
||||
inline opt::opt(bool & ref)
|
||||
: bound_parser(std::make_shared<detail::BoundFlagRef>(ref))
|
||||
{}
|
||||
template <typename L>
|
||||
opt::opt(L const & ref,
|
||||
typename std::enable_if<detail::is_invocable<L>::value,
|
||||
opt::ctor_lambda_e>::type)
|
||||
: bound_parser(std::make_shared<detail::BoundFlagLambda<L>>(ref))
|
||||
{}
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_opt_ctor_values]
|
||||
=== Values
|
||||
|
||||
[source]
|
||||
----
|
||||
template <typename T>
|
||||
lyra::opt::opt(T& ref, std::string const& hint);
|
||||
|
||||
template <typename L>
|
||||
lyra::opt::opt(L const& ref, std::string const& hint)
|
||||
----
|
||||
|
||||
Constructs a value option with a target `ref`. The first form takes a reference
|
||||
to a variable to receive the value. The second takes a callback that is called
|
||||
with the value when the option is present.
|
||||
|
||||
end::reference[] */
|
||||
template <typename T>
|
||||
opt::opt(T & ref,
|
||||
std::string const & hint,
|
||||
typename std::enable_if<!detail::is_invocable<T>::value,
|
||||
opt::ctor_ref_e>::type)
|
||||
: bound_parser(ref, hint)
|
||||
{}
|
||||
template <typename L>
|
||||
opt::opt(L const & ref,
|
||||
std::string const & hint,
|
||||
typename std::enable_if<detail::is_invocable<L>::value,
|
||||
opt::ctor_lambda_e>::type)
|
||||
: bound_parser(ref, hint)
|
||||
{}
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_opt_specification]
|
||||
== Specification
|
||||
|
||||
end::reference[] */
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_opt_name]
|
||||
=== `lyra::opt::name`
|
||||
|
||||
[source]
|
||||
----
|
||||
lyra::opt& lyra::opt::name(const std::string &opt_name)
|
||||
lyra::opt& lyra::opt::operator[](const std::string &opt_name)
|
||||
----
|
||||
|
||||
Add a spelling for the option of the form `--<name>` or `-n`.
|
||||
One can add multiple short spellings at once with `-abc`.
|
||||
|
||||
end::reference[] */
|
||||
inline opt & opt::name(const std::string & opt_name)
|
||||
{
|
||||
if (opt_name.size() > 2 && opt_name[0] == '-' && opt_name[1] != '-')
|
||||
for (auto o : opt_name.substr(1))
|
||||
opt_names.push_back(std::string(1, opt_name[0]) + o);
|
||||
else
|
||||
opt_names.push_back(opt_name);
|
||||
return *this;
|
||||
}
|
||||
inline opt & opt::operator[](const std::string & opt_name)
|
||||
{
|
||||
return this->name(opt_name);
|
||||
}
|
||||
|
||||
} // namespace lyra
|
||||
|
||||
#endif
|
@ -1,169 +0,0 @@
|
||||
// Copyright 2021-2022 René Ferdinand Rivera Morell
|
||||
|
||||
#ifndef LYRA_OPTION_STYLE_HPP
|
||||
#define LYRA_OPTION_STYLE_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace lyra {
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_option_style]
|
||||
= `lyra::option_style`
|
||||
|
||||
Specify the syntax style for options to the parser.
|
||||
|
||||
[source]
|
||||
----
|
||||
std::string value_delimiters;
|
||||
std::string long_option_prefix;
|
||||
std::size_t long_option_size = 0;
|
||||
std::string short_option_prefix;
|
||||
std::size_t short_option_size = 0;
|
||||
----
|
||||
|
||||
* `value_delimiters` -- Specifies a set of characters that are accepted as a
|
||||
delimiter/separator between an option name and an option value when a
|
||||
single argument is used for the option+value (i.e. "--option=value").
|
||||
* `long_option_prefix` -- Specifies a set of characters that are accepted as a
|
||||
prefix for long options (i.e. multi-char single option name).
|
||||
* `long_option_size` -- The number of prefix characters that indicates a long
|
||||
option. A value of zero (0) indicates that long options are not accepted.
|
||||
* `short_option_prefix` -- Specifies a set of characters that are accepted as a
|
||||
prefix for short options (i.e. single-char multi-options).
|
||||
* `short_option_size` -- The number of prefix characters that indicates a short
|
||||
option. A value of zero (0) indicates that short options are not accepted.
|
||||
|
||||
end::reference[] */
|
||||
struct option_style
|
||||
{
|
||||
std::string value_delimiters;
|
||||
std::string long_option_prefix;
|
||||
std::size_t long_option_size = 0;
|
||||
std::string short_option_prefix;
|
||||
std::size_t short_option_size = 0;
|
||||
|
||||
// Construction..
|
||||
|
||||
option_style(std::string && value_delimiters_chars,
|
||||
std::string && long_option_prefix_chars = {},
|
||||
std::size_t long_option_prefix_size = 0,
|
||||
std::string && short_option_prefix_chars = {},
|
||||
std::size_t short_option_prefix_size = 0)
|
||||
: value_delimiters(std::move(value_delimiters_chars))
|
||||
, long_option_prefix(std::move(long_option_prefix_chars))
|
||||
, long_option_size(long_option_prefix_size)
|
||||
, short_option_prefix(std::move(short_option_prefix_chars))
|
||||
, short_option_size(short_option_prefix_size)
|
||||
{}
|
||||
|
||||
// Definitions..
|
||||
|
||||
std::string long_option_string() const;
|
||||
std::string short_option_string() const;
|
||||
|
||||
// Styles..
|
||||
|
||||
static const option_style & posix();
|
||||
static const option_style & posix_brief();
|
||||
static const option_style & windows();
|
||||
};
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_option_style_ctor]
|
||||
== Construction
|
||||
|
||||
[source]
|
||||
----
|
||||
lyra::option_style::option_style(
|
||||
std::string && value_delimiters,
|
||||
std::string && long_option_prefix = {},
|
||||
std::size_t long_option_size = 0,
|
||||
std::string && short_option_prefix = {},
|
||||
std::size_t short_option_size = 0)
|
||||
----
|
||||
|
||||
Utility constructor that defines all the settings.
|
||||
|
||||
end::reference[] */
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_option_style_def]
|
||||
== Definitions
|
||||
|
||||
[source]
|
||||
----
|
||||
std::string lyra::option_style::long_option_string() const
|
||||
std::string lyra::option_style::short_option_string() const
|
||||
----
|
||||
|
||||
Gives the default long or short option string, or prefix, for this option
|
||||
style. If the type of option is not available, i.e. size is zero, an empty
|
||||
string is returned.
|
||||
|
||||
end::reference[] */
|
||||
|
||||
inline std::string option_style::long_option_string() const
|
||||
{
|
||||
return long_option_size > 0
|
||||
? std::string(long_option_size, long_option_prefix[0])
|
||||
: "";
|
||||
}
|
||||
|
||||
inline std::string option_style::short_option_string() const
|
||||
{
|
||||
return short_option_size > 0
|
||||
? std::string(short_option_size, short_option_prefix[0])
|
||||
: "";
|
||||
}
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_option_style_styles]
|
||||
== Styles
|
||||
|
||||
[source]
|
||||
----
|
||||
static const option_style & lyra::option_style::posix();
|
||||
static const option_style & lyra::option_style::posix_brief();
|
||||
static const option_style & lyra::option_style::windows();
|
||||
----
|
||||
|
||||
These provide definitions for common syntax of option styles:
|
||||
|
||||
`posix`:: The overall _default_ that is two dashes (`--`) for long option
|
||||
names and one dash (`-`) for short option names. Values for long options
|
||||
use equal (`=`) between the option and value.
|
||||
`posix_brief`:: Variant that only allows for long option names with a single
|
||||
dash (`-`).
|
||||
`windows`:: The common option style on Windows `CMD.EXE` shell. It only allows
|
||||
long name options that start with slash (`/`) where the value is
|
||||
specified after a colon (`:`). Single character flag style options are
|
||||
only available as individual long options, for example `/A`.
|
||||
|
||||
end::reference[] */
|
||||
|
||||
inline const option_style & option_style::posix()
|
||||
{
|
||||
static const option_style style("= ", "-", 2, "-", 1);
|
||||
return style;
|
||||
}
|
||||
|
||||
inline const option_style & option_style::posix_brief()
|
||||
{
|
||||
static const option_style style("= ", "-", 1);
|
||||
return style;
|
||||
}
|
||||
|
||||
inline const option_style & option_style::windows()
|
||||
{
|
||||
static const option_style style(":", "/", 1);
|
||||
return style;
|
||||
}
|
||||
|
||||
} // namespace lyra
|
||||
|
||||
#endif
|
@ -1,647 +0,0 @@
|
||||
// Copyright 2018-2022 René Ferdinand Rivera Morell
|
||||
// Copyright 2021 Max Ferger
|
||||
// Copyright 2017 Two Blue Cubes Ltd. All rights reserved.
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef LYRA_PARSER_HPP
|
||||
#define LYRA_PARSER_HPP
|
||||
|
||||
#include "lyra/args.hpp"
|
||||
#include "lyra/detail/bound.hpp"
|
||||
#include "lyra/detail/choices.hpp"
|
||||
#include "lyra/detail/from_string.hpp"
|
||||
#include "lyra/detail/result.hpp"
|
||||
#include "lyra/detail/tokens.hpp"
|
||||
#include "lyra/detail/trait_utils.hpp"
|
||||
#include "lyra/option_style.hpp"
|
||||
#include "lyra/parser_result.hpp"
|
||||
#include "lyra/val.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
namespace lyra {
|
||||
|
||||
namespace detail {
|
||||
|
||||
class parse_state
|
||||
{
|
||||
public:
|
||||
parse_state(parser_result_type type,
|
||||
token_iterator const & remaining_tokens,
|
||||
size_t parsed_tokens = 0)
|
||||
: result_type(type)
|
||||
, tokens(remaining_tokens)
|
||||
{
|
||||
(void)parsed_tokens;
|
||||
}
|
||||
|
||||
parser_result_type type() const { return result_type; }
|
||||
token_iterator remainingTokens() const { return tokens; }
|
||||
bool have_tokens() const { return bool(tokens); }
|
||||
|
||||
private:
|
||||
parser_result_type result_type;
|
||||
token_iterator tokens;
|
||||
};
|
||||
|
||||
struct parser_cardinality
|
||||
{
|
||||
size_t minimum = 0;
|
||||
size_t maximum = 0;
|
||||
|
||||
parser_cardinality() = default;
|
||||
|
||||
parser_cardinality(size_t a, size_t b)
|
||||
: minimum(a)
|
||||
, maximum(b)
|
||||
{}
|
||||
|
||||
// If zero or more are accepted, it's optional.
|
||||
bool is_optional() const { return (minimum == 0); }
|
||||
|
||||
// Anything that doesn't have an upper bound is considered unbounded.
|
||||
bool is_unbounded() const { return (maximum == 0); }
|
||||
|
||||
bool is_bounded() const { return !is_unbounded(); }
|
||||
|
||||
// If one or more values are expected, it's required.
|
||||
bool is_required() const { return (minimum > 0); }
|
||||
|
||||
void optional()
|
||||
{
|
||||
minimum = 0;
|
||||
maximum = 1;
|
||||
}
|
||||
void required(size_t n = 1)
|
||||
{
|
||||
minimum = n;
|
||||
maximum = n;
|
||||
}
|
||||
void counted(size_t n)
|
||||
{
|
||||
minimum = n;
|
||||
maximum = n;
|
||||
}
|
||||
void bounded(size_t n, size_t m)
|
||||
{
|
||||
minimum = n;
|
||||
maximum = m;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_parser_result]
|
||||
= `lyra::parser_result`
|
||||
|
||||
The result of parsing arguments.
|
||||
|
||||
end::reference[] */
|
||||
class parse_result : public detail::basic_result<detail::parse_state>
|
||||
{
|
||||
public:
|
||||
using base = detail::basic_result<detail::parse_state>;
|
||||
using base::basic_result;
|
||||
using base::error;
|
||||
using base::ok;
|
||||
|
||||
parse_result(const base & other)
|
||||
: base(other)
|
||||
{}
|
||||
};
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_parser]
|
||||
= `lyra::parser`
|
||||
|
||||
Base for all argument parser types.
|
||||
|
||||
end::reference[] */
|
||||
class parser
|
||||
{
|
||||
public:
|
||||
struct help_text_item
|
||||
{
|
||||
std::string option;
|
||||
std::string description;
|
||||
};
|
||||
|
||||
using help_text = std::vector<help_text_item>;
|
||||
|
||||
[[deprecated]] help_text get_help_text() const { return {}; }
|
||||
[[deprecated]] std::string get_usage_text() const { return ""; }
|
||||
[[deprecated]] std::string get_description_text() const { return ""; }
|
||||
|
||||
virtual help_text get_help_text(const option_style &) const { return {}; }
|
||||
virtual std::string get_usage_text(const option_style &) const
|
||||
{
|
||||
return "";
|
||||
}
|
||||
virtual std::string get_description_text(const option_style &) const
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
virtual ~parser() = default;
|
||||
|
||||
virtual detail::parser_cardinality cardinality() const { return { 0, 1 }; }
|
||||
bool is_optional() const { return cardinality().is_optional(); }
|
||||
virtual bool is_group() const { return false; }
|
||||
virtual result validate() const { return result::ok(); }
|
||||
virtual std::unique_ptr<parser> clone() const { return nullptr; }
|
||||
virtual bool is_named(const std::string & n) const
|
||||
{
|
||||
(void)n;
|
||||
return false;
|
||||
}
|
||||
virtual const parser * get_named(const std::string & n) const
|
||||
{
|
||||
if (is_named(n)) return this;
|
||||
return nullptr;
|
||||
}
|
||||
virtual size_t get_value_count() const { return 0; }
|
||||
virtual std::string get_value(size_t i) const
|
||||
{
|
||||
(void)i;
|
||||
return "";
|
||||
}
|
||||
|
||||
virtual parse_result parse(detail::token_iterator const & tokens,
|
||||
const option_style & style) const = 0;
|
||||
|
||||
protected:
|
||||
void print_help_text(std::ostream & os, const option_style & style) const
|
||||
{
|
||||
std::string usage_test = get_usage_text(style);
|
||||
if (!usage_test.empty())
|
||||
os << "USAGE:\n"
|
||||
<< " " << get_usage_text(style) << "\n\n";
|
||||
|
||||
std::string description_test = get_description_text(style);
|
||||
if (!description_test.empty())
|
||||
os << get_description_text(style) << "\n";
|
||||
|
||||
os << "OPTIONS, ARGUMENTS:\n";
|
||||
const std::string::size_type left_col_size = 26 - 3;
|
||||
const std::string left_pad(left_col_size, ' ');
|
||||
for (auto const & cols : get_help_text(style))
|
||||
{
|
||||
if (cols.option.size() > left_pad.size())
|
||||
os << " " << cols.option << "\n " << left_pad << " "
|
||||
<< cols.description << "\n";
|
||||
else
|
||||
os << " " << cols.option
|
||||
<< left_pad.substr(0, left_pad.size() - cols.option.size())
|
||||
<< " " << cols.description << "\n";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_parser_specification]
|
||||
== Specification
|
||||
|
||||
[#lyra_parser_help_text_item]
|
||||
=== `lyra::parser::help_text_item`
|
||||
|
||||
[source]
|
||||
----
|
||||
struct lyra::parser::help_text_item
|
||||
{
|
||||
std::string option;
|
||||
std::string description;
|
||||
};
|
||||
----
|
||||
|
||||
Holds the help information for a single argument option. The `option` member is
|
||||
the long name of the option. And the `description` is the text describing the
|
||||
option. A list of them is returned from the `lyra::parser::get_help_text`
|
||||
method.
|
||||
|
||||
[#lyra_parser_help_text]
|
||||
=== `lyra::parser::help_text`
|
||||
|
||||
[source]
|
||||
----
|
||||
using help_text = std::vector<help_text_item>;
|
||||
----
|
||||
|
||||
The set of help texts for any options in the sub-parsers to this one, if any.
|
||||
|
||||
[#lyra_parser_get_help_text]
|
||||
=== `lyra::parser::get_help_text`
|
||||
|
||||
[source]
|
||||
----
|
||||
virtual help_text get_help_text(const option_style &) const;
|
||||
----
|
||||
|
||||
Collects, and returns, the set of help items for the sub-parser arguments in
|
||||
this parser, if any. The default is to return an empty set. Which is what most
|
||||
parsers will return. Parsers like `arguments`, `group`, and `cli` will return a
|
||||
set for the arguments defined. This is called to print out the help text from
|
||||
the stream operator.
|
||||
|
||||
[#lyra_parser_get_usage_text]
|
||||
=== `lyra::parser::get_usage_text`
|
||||
|
||||
[source]
|
||||
----
|
||||
virtual std::string get_usage_text(const option_style &) const;
|
||||
----
|
||||
|
||||
Returns the formatted `USAGE` text for this parser, and any contained
|
||||
sub-parsers. This is called to print out the help text from the stream operator.
|
||||
|
||||
[#lyra_parser_get_description_text]
|
||||
=== `lyra::parser::get_description_text`
|
||||
|
||||
[source]
|
||||
----
|
||||
virtual std::string get_description_text(const option_style &) const;
|
||||
----
|
||||
|
||||
Returns the description text for this, and any contained sub-parsers. This is
|
||||
called to print out the help text from the stream operator.
|
||||
|
||||
end::reference[] */
|
||||
|
||||
template <typename T, typename U>
|
||||
std::unique_ptr<parser> make_clone(const U * source)
|
||||
{
|
||||
return std::unique_ptr<parser>(new T(*static_cast<const T *>(source)));
|
||||
}
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_composable_parser]
|
||||
= `lyra::composable_parser`
|
||||
|
||||
A parser that can be composed with other parsers using `operator|`. Composing
|
||||
two `composable_parser` instances generates a `cli` parser.
|
||||
|
||||
end::reference[] */
|
||||
template <typename Derived>
|
||||
class composable_parser : public parser
|
||||
{};
|
||||
|
||||
// Common code and state for args and Opts
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_bound_parser]
|
||||
= `lyra::bound_parser`
|
||||
|
||||
Parser that binds a variable reference or callback to the value of an argument.
|
||||
|
||||
end::reference[] */
|
||||
template <typename Derived>
|
||||
class bound_parser : public composable_parser<Derived>
|
||||
{
|
||||
protected:
|
||||
std::shared_ptr<detail::BoundRef> m_ref;
|
||||
std::string m_hint;
|
||||
std::string m_description;
|
||||
detail::parser_cardinality m_cardinality;
|
||||
std::shared_ptr<detail::choices_base> value_choices;
|
||||
|
||||
explicit bound_parser(std::shared_ptr<detail::BoundRef> const & ref)
|
||||
: m_ref(ref)
|
||||
{
|
||||
if (m_ref->isContainer())
|
||||
m_cardinality = { 0, 0 };
|
||||
else
|
||||
m_cardinality = { 0, 1 };
|
||||
}
|
||||
bound_parser(
|
||||
std::shared_ptr<detail::BoundRef> const & ref, std::string const & hint)
|
||||
: m_ref(ref)
|
||||
, m_hint(hint)
|
||||
{
|
||||
if (m_ref->isContainer())
|
||||
m_cardinality = { 0, 0 };
|
||||
else
|
||||
m_cardinality = { 0, 1 };
|
||||
}
|
||||
|
||||
public:
|
||||
enum class ctor_lambda_e
|
||||
{
|
||||
val
|
||||
};
|
||||
|
||||
template <typename Reference>
|
||||
bound_parser(Reference & ref, std::string const & hint);
|
||||
|
||||
template <typename Lambda>
|
||||
bound_parser(Lambda const & ref,
|
||||
std::string const & hint,
|
||||
typename std::enable_if<detail::is_invocable<Lambda>::value,
|
||||
ctor_lambda_e>::type
|
||||
= ctor_lambda_e::val);
|
||||
|
||||
template <typename T>
|
||||
explicit bound_parser(detail::BoundVal<T> && val)
|
||||
: bound_parser(val.move_to_shared())
|
||||
{}
|
||||
template <typename T>
|
||||
bound_parser(detail::BoundVal<T> && val, std::string const & hint)
|
||||
: bound_parser(val.move_to_shared(), hint)
|
||||
{}
|
||||
|
||||
Derived & help(const std::string & text);
|
||||
Derived & operator()(std::string const & description);
|
||||
Derived & optional();
|
||||
Derived & required(size_t n = 1);
|
||||
Derived & cardinality(size_t n);
|
||||
Derived & cardinality(size_t n, size_t m);
|
||||
detail::parser_cardinality cardinality() const override
|
||||
{
|
||||
return m_cardinality;
|
||||
}
|
||||
std::string hint() const;
|
||||
Derived & hint(std::string const & hint);
|
||||
|
||||
template <typename T,
|
||||
typename... Rest,
|
||||
typename std::enable_if<!detail::is_invocable<T>::value, int>::type = 0>
|
||||
Derived & choices(T val0, Rest... rest);
|
||||
template <typename Lambda,
|
||||
typename std::enable_if<detail::is_invocable<Lambda>::value, int>::type
|
||||
= 1>
|
||||
Derived & choices(Lambda const & check_choice);
|
||||
template <typename T, std::size_t N>
|
||||
Derived & choices(const T (&choice_values)[N]);
|
||||
|
||||
virtual std::unique_ptr<parser> clone() const override
|
||||
{
|
||||
return make_clone<Derived>(this);
|
||||
}
|
||||
|
||||
virtual bool is_named(const std::string & n) const override
|
||||
{
|
||||
return n == m_hint;
|
||||
}
|
||||
virtual size_t get_value_count() const override
|
||||
{
|
||||
return m_ref->get_value_count();
|
||||
}
|
||||
virtual std::string get_value(size_t i) const override
|
||||
{
|
||||
return m_ref->get_value(i);
|
||||
}
|
||||
};
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_bound_parser_ctor]
|
||||
== Construction
|
||||
|
||||
end::reference[] */
|
||||
|
||||
/* tag::reference[]
|
||||
[source]
|
||||
----
|
||||
template <typename Derived>
|
||||
template <typename Reference>
|
||||
bound_parser<Derived>::bound_parser(Reference& ref, std::string const& hint);
|
||||
|
||||
template <typename Derived>
|
||||
template <typename Lambda>
|
||||
bound_parser<Derived>::bound_parser(Lambda const& ref, std::string const& hint);
|
||||
----
|
||||
|
||||
Constructs a value option with a target typed variable or callback. These are
|
||||
options that take a value as in `--opt=value`. In the first form the given
|
||||
`ref` receives the value of the option after parsing. The second form the
|
||||
callback is called during the parse with the given value. Both take a
|
||||
`hint` that is used in the help text. When the option can be specified
|
||||
multiple times the callback will be called consecutively for each option value
|
||||
given. And if a container is given as a reference on the first form it will
|
||||
contain all the specified values.
|
||||
|
||||
end::reference[] */
|
||||
template <typename Derived>
|
||||
template <typename Reference>
|
||||
bound_parser<Derived>::bound_parser(Reference & ref, std::string const & hint)
|
||||
: bound_parser(
|
||||
std::make_shared<detail::BoundValueRef<Reference>>(ref), hint)
|
||||
{}
|
||||
|
||||
template <typename Derived>
|
||||
template <typename Lambda>
|
||||
bound_parser<Derived>::bound_parser(Lambda const & ref,
|
||||
std::string const & hint,
|
||||
typename std::enable_if<detail::is_invocable<Lambda>::value,
|
||||
ctor_lambda_e>::type)
|
||||
: bound_parser(std::make_shared<detail::BoundLambda<Lambda>>(ref), hint)
|
||||
{}
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_bound_parser_specification]
|
||||
== Specification
|
||||
|
||||
end::reference[] */
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_bound_parser_help]
|
||||
=== `lyra::bound_parser::help`, `lyra::bound_parser::operator(help)`
|
||||
|
||||
[source]
|
||||
----
|
||||
template <typename Derived>
|
||||
Derived& bound_parser<Derived>::help(std::string const& help_description_text);
|
||||
template <typename Derived>
|
||||
Derived& bound_parser<Derived>::operator()(std::string const& help_description_text);
|
||||
----
|
||||
|
||||
Defines the help description of an argument.
|
||||
|
||||
end::reference[] */
|
||||
template <typename Derived>
|
||||
Derived & bound_parser<Derived>::help(const std::string & help_description_text)
|
||||
{
|
||||
m_description = help_description_text;
|
||||
return static_cast<Derived &>(*this);
|
||||
}
|
||||
template <typename Derived>
|
||||
Derived & bound_parser<Derived>::operator()(std::string const & help_description_text)
|
||||
{
|
||||
return this->help(help_description_text);
|
||||
}
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_bound_parser_optional]
|
||||
=== `lyra::bound_parser::optional`
|
||||
|
||||
[source]
|
||||
----
|
||||
template <typename Derived>
|
||||
Derived& bound_parser<Derived>::optional();
|
||||
----
|
||||
|
||||
Indicates that the argument is optional. This is equivalent to specifying
|
||||
`cardinality(0, 1)`.
|
||||
|
||||
end::reference[] */
|
||||
template <typename Derived>
|
||||
Derived & bound_parser<Derived>::optional()
|
||||
{
|
||||
return this->cardinality(0, 1);
|
||||
}
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_bound_parser_required]
|
||||
=== `lyra::bound_parser::required(n)`
|
||||
|
||||
[source]
|
||||
----
|
||||
template <typename Derived>
|
||||
Derived& bound_parser<Derived>::required(size_t n);
|
||||
----
|
||||
|
||||
Specifies that the argument needs to given the number of `n` times
|
||||
(defaults to *1*).
|
||||
|
||||
end::reference[] */
|
||||
template <typename Derived>
|
||||
Derived & bound_parser<Derived>::required(size_t n)
|
||||
{
|
||||
if (m_ref->isContainer())
|
||||
return this->cardinality(1, 0);
|
||||
else
|
||||
return this->cardinality(n);
|
||||
}
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_bound_parser_cardinality]
|
||||
=== `lyra::bound_parser::cardinality(n, m)`
|
||||
|
||||
[source]
|
||||
----
|
||||
template <typename Derived>
|
||||
Derived& bound_parser<Derived>::cardinality(size_t n);
|
||||
|
||||
template <typename Derived>
|
||||
Derived& bound_parser<Derived>::cardinality(size_t n, size_t m);
|
||||
----
|
||||
|
||||
Specifies the number of times the argument can and needs to appear in the list
|
||||
of arguments. In the first form the argument can appear exactly `n` times. In
|
||||
the second form it specifies that the argument can appear from `n` to `m` times
|
||||
inclusive.
|
||||
|
||||
end::reference[] */
|
||||
template <typename Derived>
|
||||
Derived & bound_parser<Derived>::cardinality(size_t n)
|
||||
{
|
||||
m_cardinality = { n, n };
|
||||
return static_cast<Derived &>(*this);
|
||||
}
|
||||
|
||||
template <typename Derived>
|
||||
Derived & bound_parser<Derived>::cardinality(size_t n, size_t m)
|
||||
{
|
||||
m_cardinality = { n, m };
|
||||
return static_cast<Derived &>(*this);
|
||||
}
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_bound_parser_choices]
|
||||
=== `lyra::bound_parser::choices`
|
||||
|
||||
[source]
|
||||
----
|
||||
template <typename Derived>
|
||||
template <typename T, typename... Rest>
|
||||
lyra::opt& lyra::bound_parser<Derived>::choices(T val0, Rest... rest)
|
||||
|
||||
template <typename Derived>
|
||||
template <typename Lambda>
|
||||
lyra::opt& lyra::bound_parser<Derived>::choices(Lambda const &check_choice)
|
||||
----
|
||||
|
||||
Limit the allowed values of an argument. In the first form the value is
|
||||
limited to the ones listed in the call (two or more values). In the second
|
||||
form the `check_choice` function is called with the parsed value and returns
|
||||
`true` if it's an allowed value.
|
||||
|
||||
end::reference[] */
|
||||
template <typename Derived>
|
||||
template <typename T,
|
||||
typename... Rest,
|
||||
typename std::enable_if<!detail::is_invocable<T>::value, int>::type>
|
||||
Derived & bound_parser<Derived>::choices(T val0, Rest... rest)
|
||||
{
|
||||
value_choices = std::make_shared<detail::choices_set<T>>(val0, rest...);
|
||||
return static_cast<Derived &>(*this);
|
||||
}
|
||||
|
||||
template <typename Derived>
|
||||
template <typename Lambda,
|
||||
typename std::enable_if<detail::is_invocable<Lambda>::value, int>::type>
|
||||
Derived & bound_parser<Derived>::choices(Lambda const & check_choice)
|
||||
{
|
||||
value_choices
|
||||
= std::make_shared<detail::choices_check<Lambda>>(check_choice);
|
||||
return static_cast<Derived &>(*this);
|
||||
}
|
||||
|
||||
template <typename Derived>
|
||||
template <typename T, std::size_t N>
|
||||
Derived & bound_parser<Derived>::choices(const T (&choice_values)[N])
|
||||
{
|
||||
value_choices = std::make_shared<detail::choices_set<T>>(
|
||||
std::vector<T> { choice_values, choice_values + N });
|
||||
return static_cast<Derived &>(*this);
|
||||
}
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_bound_parser_hint]
|
||||
=== `lyra::bound_parser::hint`
|
||||
|
||||
[source]
|
||||
----
|
||||
template <typename Derived>
|
||||
std::string lyra::bound_parser<Derived>::hint() const
|
||||
|
||||
template <typename Derived>
|
||||
Derived & lyra::bound_parser<Derived>::hint(std::string const & hint)
|
||||
----
|
||||
|
||||
Selectors to read and write the hint of a variable-bound parser.
|
||||
|
||||
The hint should not be modified anymore, once the parser is applied to arguments
|
||||
or used in a `lyra::composable_parser`.
|
||||
|
||||
end::reference[] */
|
||||
template <typename Derived>
|
||||
std::string bound_parser<Derived>::hint() const
|
||||
{
|
||||
return m_hint;
|
||||
}
|
||||
|
||||
template <typename Derived>
|
||||
Derived & bound_parser<Derived>::hint(std::string const & hint)
|
||||
{
|
||||
m_hint = hint;
|
||||
return static_cast<Derived &>(*this);
|
||||
}
|
||||
|
||||
} // namespace lyra
|
||||
|
||||
#endif
|
@ -1,41 +0,0 @@
|
||||
// Copyright 2018-2022 René Ferdinand Rivera Morell
|
||||
// Copyright 2017 Two Blue Cubes Ltd. All rights reserved.
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef LYRA_PARSER_RESULT_HPP
|
||||
#define LYRA_PARSER_RESULT_HPP
|
||||
|
||||
#include "lyra/detail/result.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace lyra {
|
||||
|
||||
// enum of result types from a parse
|
||||
enum class parser_result_type
|
||||
{
|
||||
matched,
|
||||
no_match,
|
||||
short_circuit_all
|
||||
};
|
||||
|
||||
inline std::string to_string(parser_result_type v)
|
||||
{
|
||||
switch (v)
|
||||
{
|
||||
case parser_result_type::matched: return "matched";
|
||||
case parser_result_type::no_match: return "no_match";
|
||||
case parser_result_type::short_circuit_all: return "short_circuit_all";
|
||||
}
|
||||
return "?";
|
||||
}
|
||||
|
||||
using result = detail::basic_result<void>;
|
||||
|
||||
// Result type for parser operation
|
||||
using parser_result = detail::basic_result<parser_result_type>;
|
||||
|
||||
} // namespace lyra
|
||||
|
||||
#endif
|
@ -1,44 +0,0 @@
|
||||
// Copyright 2020-2022 René Ferdinand Rivera Morell
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef LYRA_VAL_HPP
|
||||
#define LYRA_VAL_HPP
|
||||
|
||||
#include "lyra/detail/bound.hpp"
|
||||
#include <memory>
|
||||
|
||||
namespace lyra {
|
||||
|
||||
/* tag::reference[]
|
||||
|
||||
[#lyra_val]
|
||||
= `lyra::val`
|
||||
|
||||
[source]
|
||||
----
|
||||
auto val(T && v);
|
||||
auto val(const char * v);
|
||||
----
|
||||
|
||||
Makes a bound self-contained value of the type of the given r-value. The created
|
||||
bound values can be used in place of the value references for arguments. And can
|
||||
be retrieved with the
|
||||
<<lyra_cli_array_ref>> call.
|
||||
|
||||
*/ // end::reference[]
|
||||
template <typename T>
|
||||
detail::BoundVal<T> val(T && v)
|
||||
{
|
||||
return detail::BoundVal<T>(std::forward<T>(v));
|
||||
}
|
||||
|
||||
inline detail::BoundVal<std::string> val(const char * v)
|
||||
{
|
||||
return detail::BoundVal<std::string>(v);
|
||||
}
|
||||
|
||||
} // namespace lyra
|
||||
|
||||
#endif
|
@ -1,17 +0,0 @@
|
||||
// Copyright 2019-2022 René Ferdinand Rivera Morell
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef LYRA_VERSION_HPP
|
||||
#define LYRA_VERSION_HPP
|
||||
|
||||
#define LYRA_VERSION_MAJOR 1
|
||||
#define LYRA_VERSION_MINOR 6
|
||||
#define LYRA_VERSION_PATCH 1
|
||||
|
||||
#define LYRA_VERSION \
|
||||
(((LYRA_VERSION_MAJOR)*10000000) + ((LYRA_VERSION_MINOR)*100000) \
|
||||
+ (LYRA_VERSION_PATCH))
|
||||
|
||||
#endif // LYRA_VERSION_HPP
|
@ -102,7 +102,7 @@ target_include_directories(
|
||||
3party/bnflite
|
||||
3party/eventbus/include
|
||||
3party/inja
|
||||
3party/lyra
|
||||
3party/argagg/include
|
||||
3party/mongoose
|
||||
3party/nlohmann
|
||||
3party/nonstd
|
||||
|
Loading…
x
Reference in New Issue
Block a user