From 51dd8de7b12c705d54a6130aa74426e948eb5fd5 Mon Sep 17 00:00:00 2001 From: Andrey Zherikov Date: Fri, 16 Sep 2022 17:58:05 -0400 Subject: [PATCH] Add support of custom types (#77) --- README.md | 27 +++++++++++++--- examples/getting_started/advanced/app.d | 9 +++++- examples/getting_started/basic/app.d | 2 +- source/argparse/internal.d | 11 ++++++- source/argparse/package.d | 42 ++++++++++++++++++++++++- 5 files changed, 83 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index fd153ea..92479a9 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ considered arguments with the same name as the name of member: ```d import argparse; -static struct Basic +struct Basic { // Basic data types are supported: // --name argument @@ -127,7 +127,7 @@ Optional arguments: For more sophisticated CLI usage, `argparse` provides few UDAs: ```d -static struct Advanced +struct Advanced { // Positional arguments are required by default @PositionalArgument(0) @@ -1081,7 +1081,7 @@ If you want to determine whether `--color` argument was specified in command lin data member: ```d -static struct Arguments +struct Arguments { static auto color = ansiStylingArgument; } @@ -1323,7 +1323,7 @@ An argument can be bound to a function with one of the following signatures command line and the set of values specified in command line is provided into parameter. ```d -static struct T +struct T { int a; @@ -1333,6 +1333,25 @@ static struct T assert(CLI!T.parseArgs!((T t) { assert(t == T(4)); })(["-a","-a","-a","-a"]) == 0); ``` +### Custom types + +Any arbitrary type can be used to receive command line argument values. `argparse` supports this use case - you just need +to provide parsing function: + +```d +struct Value +{ + string a; +} +struct T +{ + @(NamedArgument.Parse!((string s) { return Value(s); })) + Value s; +} + +assert(CLI!T.parseArgs!((T t) { assert(t == T(Value("foo"))); return 12345; })(["-s","foo"]) == 12345); +``` + ## Argument parsing customization Some time the functionality provided out of the box is not enough and it needs to be tuned. diff --git a/examples/getting_started/advanced/app.d b/examples/getting_started/advanced/app.d index 782c680..0062db4 100644 --- a/examples/getting_started/advanced/app.d +++ b/examples/getting_started/advanced/app.d @@ -1,7 +1,7 @@ import argparse; import argparse.ansi; -static struct Advanced +struct Advanced { // Positional arguments are required by default @PositionalArgument(0) @@ -22,6 +22,13 @@ static struct Advanced @NamedArgument(["b","banana","ban"]) int banana; + // Custom types can also be used with custon parsing function + struct CustomType { + double d; + } + @(NamedArgument.Parse!((string value) { import std.conv: to; return CustomType(value.to!double); })) + CustomType custom; + @(NamedArgument.Description(green.bold.underline("Colorize")~" the output. If value is omitted then '"~red("always")~"' is used.")) static auto color = ansiStylingArgument; } diff --git a/examples/getting_started/basic/app.d b/examples/getting_started/basic/app.d index c77195d..2509397 100644 --- a/examples/getting_started/basic/app.d +++ b/examples/getting_started/basic/app.d @@ -1,7 +1,7 @@ import argparse; // If struct has no UDA then all members are named arguments -static struct Basic +struct Basic { // Basic data types are supported: // --name argument diff --git a/source/argparse/internal.d b/source/argparse/internal.d index 415b437..2ec4144 100644 --- a/source/argparse/internal.d +++ b/source/argparse/internal.d @@ -1195,7 +1195,16 @@ if(!is(T == void)) ); } else - static assert(false, "Type is not supported: " ~ T.stringof); + { + alias DefaultValueParseFunctions = ValueParseFunctions!( + void, // pre process + void, // pre validate + void, // parse + void, // validate + void, // action + void // no-value action + ); + } } unittest diff --git a/source/argparse/package.d b/source/argparse/package.d index c83490f..55bdea5 100644 --- a/source/argparse/package.d +++ b/source/argparse/package.d @@ -1251,7 +1251,17 @@ public auto PreValidation(alias func, T)(auto ref ArgumentUDA!T uda) public auto Parse(alias func, T)(auto ref ArgumentUDA!T uda) { - return ArgumentUDA!(uda.parsingFunc.changeParse!func)(uda.tupleof); + auto desc = ArgumentUDA!(uda.parsingFunc.changeParse!func)(uda.tupleof); + + static if(__traits(compiles, { func(string.init); })) + desc.info.minValuesCount = desc.info.maxValuesCount = 1; + else + { + desc.info.minValuesCount = 0; + desc.info.maxValuesCount = ulong.max; + } + + return desc; } public auto Validation(alias func, T)(auto ref ArgumentUDA!T uda) @@ -1286,6 +1296,24 @@ unittest assert(!is(FUNC == void)); } +unittest +{ + auto uda = NamedArgument().Parse!((string _) => _); + assert(is(typeof(uda) : ArgumentUDA!(ValueParseFunctions!(void, void, FUNC, void, void, void)), alias FUNC)); + assert(!is(FUNC == void)); + assert(uda.info.minValuesCount == 1); + assert(uda.info.maxValuesCount == 1); +} + +unittest +{ + auto uda = NamedArgument().Parse!((string[] _) => _); + assert(is(typeof(uda) : ArgumentUDA!(ValueParseFunctions!(void, void, FUNC, void, void, void)), alias FUNC)); + assert(!is(FUNC == void)); + assert(uda.info.minValuesCount == 0); + assert(uda.info.maxValuesCount == ulong.max); +} + unittest { auto uda = NamedArgument().Validation!({}); @@ -1577,6 +1605,18 @@ unittest assert(CLI!T.parseArgs!((T t) { assert(t == T(4)); return 12345; })(["-a","-a","-a","-a"]) == 12345); } +unittest +{ + struct Value { string a; } + struct T + { + @(NamedArgument.Parse!((string s) { return Value(s); })) + Value s; + } + + assert(CLI!T.parseArgs!((T t) { assert(t == T(Value("foo"))); return 12345; })(["-s","foo"]) == 12345); +} + auto Command(string[] name...) {