From 3f1e73eb759601604c511e620ac715fb3cce0f3b Mon Sep 17 00:00:00 2001 From: Pavel Belikov Date: Sat, 18 Nov 2017 12:14:36 +0300 Subject: add Commands examples --- README.md | 179 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 171 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index c89804a..2d9a037 100644 --- a/README.md +++ b/README.md @@ -40,10 +40,10 @@ describe the usage, as it's built to push the boundaries. It: -* lets you handle flags, flag+value, and positional arguments simply and +* Lets you handle flags, flag+value, and positional arguments simply and elegently, with the full help of static typechecking. -* allows you to use your own types in a pretty simple way. -* lets you use count flags, and lists of all argument-accepting types. +* Allows you to use your own types in a pretty simple way. +* Lets you use count flags, and lists of all argument-accepting types. * Allows full validation of groups of required arguments, though output isn't pretty when something fails group validation. User validation functions are accepted. Groups are fully nestable. @@ -56,12 +56,11 @@ It: * Lets you decide not to allow separate-argument value flags or joined ones (like disallowing `--foo bar`, requiring `--foo=bar`, or the inverse, or the same for short options). -* Allows you to create subparsers somewhat like argparse, through the use of - kick-out arguments (check the gitlike.cxx example program for a simple sample - of this) -* Allow one value flag to take a specific number of values (like `--foo first +* Allows you to create subparsers, reusing arguments for multiple commands and + refactoring your command logic to function or lambda +* Allows one value flag to take a specific number of values (like `--foo first second`, where --foo slurps both arguments). -* Allow you to have value flags only optionally accept values +* Allows you to have value flags only optionally accept values # What does it not do? @@ -377,6 +376,170 @@ Argument 'numbers' received invalid value type 'a' ... ``` +## Commands + +```cpp +#include +#include +int main(int argc, char **argv) +{ + args::ArgumentParser p("git-like parser"); + args::Group commands(p, "commands"); + args::Command add(commands, "add", "add file contents to the index"); + args::Command commit(commands, "commit", "record changes to the repository"); + args::Group arguments(p, "arguments", args::Group::Validators::DontCare, args::Options::Global); + args::ValueFlag gitdir(arguments, "path", "", {"git-dir"}); + args::HelpFlag h(arguments, "help", "help", {'h', "help"}); + args::PositionalList pathsList(arguments, "paths", "files to commit"); + + try + { + p.ParseCLI(argc, argv); + if (add) + { + std::cout << "Add"; + } + else + { + std::cout << "Commit"; + } + + for (auto &&path : pathsList) + { + std::cout << ' ' << path; + } + + std::cout << std::endl; + } + catch (args::Help) + { + std::cout << p; + } + catch (args::Error& e) + { + std::cerr << e.what() << std::endl << p; + return 1; + } + return 0; +} +``` + +```shell +% ./test -h + ./test COMMAND [paths...] {OPTIONS} + + git-like parser + + OPTIONS: + + commands + add add file contents to the index + commit record changes to the repository + arguments + --git-dir=[path] + -h, --help help + paths... files + "--" can be used to terminate flag options and force all following + arguments to be treated as positional options + +% ./test add 1 2 +Add 1 2 +``` + +## Refactoring commands + +```cpp +#include +#include "args.hxx" + +args::Group arguments("arguments"); +args::ValueFlag gitdir(arguments, "path", "", {"git-dir"}); +args::HelpFlag h(arguments, "help", "help", {'h', "help"}); +args::PositionalList pathsList(arguments, "paths", "files to commit"); + +void CommitCommand(args::Subparser &parser) +{ + args::ValueFlag message(parser, "MESSAGE", "commit message", {'m'}); + parser.Parse(); + + std::cout << "Commit"; + + for (auto &&path : pathsList) + { + std::cout << ' ' << path; + } + + std::cout << std::endl; + + if (message) + { + std::cout << "message: " << args::get(message) << std::endl; + } +} + +int main(int argc, const char **argv) +{ + args::ArgumentParser p("git-like parser"); + args::Group commands(p, "commands"); + args::Command add(commands, "add", "add file contents to the index", [&](args::Subparser &parser) + { + parser.Parse(); + std::cout << "Add"; + + for (auto &&path : pathsList) + { + std::cout << ' ' << path; + } + + std::cout << std::endl; + }); + + args::Command commit(commands, "commit", "record changes to the repository", &CommitCommand); + args::GlobalOptions globals(p, arguments); + + try + { + p.ParseCLI(argc, argv); + } + catch (args::Help) + { + std::cout << p; + } + catch (args::Error& e) + { + std::cerr << e.what() << std::endl << p; + return 1; + } + return 0; +} +``` + +```shell +% ./test -h + ./test COMMAND [paths...] {OPTIONS} + + git-like parser + + OPTIONS: + + commands + add add file contents to the index + commit record changes to the repository + arguments + --git-dir=[path] + -h, --help help + paths... files + "--" can be used to terminate flag options and force all following + arguments to be treated as positional options + +% ./test add 1 2 +Add 1 2 + +% ./test commit -m "my commit message" 1 2 +Commit 1 2 +message: my commit message +``` + # Custom type parsers (here we use std::tuple) ```cpp -- cgit v1.2.1 From 18d238f156b846e77f5a73302179abcaa4f08dcb Mon Sep 17 00:00:00 2001 From: Pavel Belikov Date: Sat, 18 Nov 2017 12:21:52 +0300 Subject: Update README.md --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2d9a037..27b9e85 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,11 @@ #### Note that this library is essentially in maintenance mode. I haven't had the time to work on it or give it the love that it deserves. I'm not adding new features, but I will fix bugs. I will also very gladly accept pull requests. -[![build status](https://travis-ci.org/Taywee/args.svg?branch=master)](https://travis-ci.org/Taywee/args) -[![Build status](https://ci.appveyor.com/api/projects/status/nlnlmpttdjlndyc2?svg=true)](https://ci.appveyor.com/project/Taywee/args) +[![Cpp Standard](https://img.shields.io/badge/C%2B%2B-11-blue.svg)](https://img.shields.io/badge/C%2B%2B-11-blue.svg) +[![Travis status](https://travis-ci.org/Taywee/args.svg?branch=master)](https://travis-ci.org/Taywee/args) +[![AppVeyor status](https://ci.appveyor.com/api/projects/status/nlnlmpttdjlndyc2?svg=true)](https://ci.appveyor.com/project/Taywee/args) [![Coverage Status](https://coveralls.io/repos/github/Taywee/args/badge.svg?branch=master)](https://coveralls.io/github/Taywee/args?branch=master) +[![Read the Docs](https://img.shields.io/readthedocs/pip.svg)](https://taywee.github.io/args) A simple, small, flexible, single-header C++11 argument parsing library, in fewer than 2K lines of code. -- cgit v1.2.1 From 7edf12130c15bc780b3a89a3ccf340b2baf3b7e7 Mon Sep 17 00:00:00 2001 From: Pavel Belikov Date: Sat, 18 Nov 2017 12:26:51 +0300 Subject: Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 27b9e85..4e369d2 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ #### Note that this library is essentially in maintenance mode. I haven't had the time to work on it or give it the love that it deserves. I'm not adding new features, but I will fix bugs. I will also very gladly accept pull requests. -[![Cpp Standard](https://img.shields.io/badge/C%2B%2B-11-blue.svg)](https://img.shields.io/badge/C%2B%2B-11-blue.svg) +[![Cpp Standard](https://img.shields.io/badge/C%2B%2B-11-blue.svg)](https://en.wikipedia.org/wiki/C%2B%2B11) [![Travis status](https://travis-ci.org/Taywee/args.svg?branch=master)](https://travis-ci.org/Taywee/args) [![AppVeyor status](https://ci.appveyor.com/api/projects/status/nlnlmpttdjlndyc2?svg=true)](https://ci.appveyor.com/project/Taywee/args) [![Coverage Status](https://coveralls.io/repos/github/Taywee/args/badge.svg?branch=master)](https://coveralls.io/github/Taywee/args?branch=master) -- cgit v1.2.1 From 52a0f5ace435414e953b31d03166a2c725aab4ad Mon Sep 17 00:00:00 2001 From: Pavel Belikov Date: Sat, 18 Nov 2017 12:34:14 +0300 Subject: Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4e369d2..f3a35d8 100644 --- a/README.md +++ b/README.md @@ -58,8 +58,8 @@ It: * Lets you decide not to allow separate-argument value flags or joined ones (like disallowing `--foo bar`, requiring `--foo=bar`, or the inverse, or the same for short options). -* Allows you to create subparsers, reusing arguments for multiple commands and - refactoring your command logic to function or lambda +* Allows you to create subparsers, to reuse arguments for multiple commands and + to refactor your command's logic to a function or lambda * Allows one value flag to take a specific number of values (like `--foo first second`, where --foo slurps both arguments). * Allows you to have value flags only optionally accept values -- cgit v1.2.1 From 0996557a530692d276fd247995523344bfae084a Mon Sep 17 00:00:00 2001 From: Pavel Belikov Date: Sat, 18 Nov 2017 15:54:08 +0300 Subject: add ActionFlag --- args.hxx | 36 ++++++++++++++++++++++++++++++++++++ test.cxx | 19 +++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/args.hxx b/args.hxx index 7af24c4..7a4ccf7 100644 --- a/args.hxx +++ b/args.hxx @@ -2577,6 +2577,42 @@ namespace args } }; + /** A flag class that calls a function when it's matched + */ + class ActionFlag : public FlagBase + { + private: + std::function &)> action; + Nargs nargs; + + public: + ActionFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Nargs nargs_, std::function &)> action_, Options options_ = {}): + FlagBase(name_, help_, std::move(matcher_), options_), action(std::move(action_)), nargs(nargs_) + { + group_.Add(*this); + } + + ActionFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, std::function action_, Options options_ = {}): + FlagBase(name_, help_, std::move(matcher_), options_), nargs(1) + { + group_.Add(*this); + action = [action_](const std::vector &a) { return action_(a.at(0)); }; + } + + ActionFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, std::function action_, Options options_ = {}): + FlagBase(name_, help_, std::move(matcher_), options_), nargs(0) + { + group_.Add(*this); + action = [action_](const std::vector &) { return action_(); }; + } + + virtual Nargs NumberOfArguments() const noexcept override + { return nargs; } + + virtual void ParseValue(const std::vector &value) override + { action(value); } + }; + /** A default Reader class for argument classes * * Simply uses a std::istringstream to read into the destination type, and diff --git a/test.cxx b/test.cxx index 1189f97..5f97c87 100644 --- a/test.cxx +++ b/test.cxx @@ -1066,6 +1066,25 @@ TEST_CASE("HelpParams work as expected", "[args]") } +TEST_CASE("ActionFlag works as expected", "[args]") +{ + args::ArgumentParser p("parser"); + std::string s; + + args::ActionFlag action0(p, "name", "description", {'x'}, [&]() { s = "flag"; }); + args::ActionFlag action1(p, "name", "description", {'y'}, [&](const std::string &arg) { s = arg; }); + args::ActionFlag actionN(p, "name", "description", {'z'}, 2, [&](const std::vector &arg) { s = arg[0] + arg[1]; }); + + p.ParseArgs(std::vector{"-x"}); + REQUIRE(s == "flag"); + + p.ParseArgs(std::vector{"-y", "a"}); + REQUIRE(s == "a"); + + p.ParseArgs(std::vector{"-z", "a", "b"}); + REQUIRE(s == "ab"); +} + #undef ARGS_HXX #define ARGS_TESTNAMESPACE #define ARGS_NOEXCEPT -- cgit v1.2.1 From ec3c42cdd95eba66d374994c7153e954c05b8f06 Mon Sep 17 00:00:00 2001 From: Pavel Belikov Date: Sat, 18 Nov 2017 15:57:51 +0300 Subject: add test for throwing exception from ActionFlag --- test.cxx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test.cxx b/test.cxx index 5f97c87..44cde49 100644 --- a/test.cxx +++ b/test.cxx @@ -1074,6 +1074,7 @@ TEST_CASE("ActionFlag works as expected", "[args]") args::ActionFlag action0(p, "name", "description", {'x'}, [&]() { s = "flag"; }); args::ActionFlag action1(p, "name", "description", {'y'}, [&](const std::string &arg) { s = arg; }); args::ActionFlag actionN(p, "name", "description", {'z'}, 2, [&](const std::vector &arg) { s = arg[0] + arg[1]; }); + args::ActionFlag actionThrow(p, "name", "description", {'v'}, [&]() { throw std::runtime_error(""); }); p.ParseArgs(std::vector{"-x"}); REQUIRE(s == "flag"); @@ -1083,6 +1084,8 @@ TEST_CASE("ActionFlag works as expected", "[args]") p.ParseArgs(std::vector{"-z", "a", "b"}); REQUIRE(s == "ab"); + + REQUIRE_THROWS_AS(p.ParseArgs(std::vector{"-v"}), std::runtime_error); } #undef ARGS_HXX -- cgit v1.2.1