From f09f63ad3303d7eae9a192fa532324b51e316495 Mon Sep 17 00:00:00 2001 From: Andrey Zherikov Date: Fri, 20 Dec 2024 19:33:46 -0500 Subject: [PATCH] Change AllowNoValue to accept run-time parameter --- README.md | 11 +++ docs/code_snippets/arity_no_values.d | 2 +- docs/topics/Arity.md | 4 +- source/argparse/api/ansi.d | 14 ++-- source/argparse/api/argument.d | 19 ++--- source/argparse/internal/novalueactionfunc.d | 88 +++++++------------- source/argparse/internal/valueparser.d | 12 +-- source/argparse/package.d | 4 +- 8 files changed, 66 insertions(+), 88 deletions(-) diff --git a/README.md b/README.md index ce199fe..154a89e 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,17 @@ * `HideFromHelp` is renamed to `Hidden` and now also hides an argument from shell completion. +* `AllowNoValue` now accepts a value as run-time parameter, not as template parameter. + + For example, replace this + ```d + .AllowNoValue!"myvalue" + ``` + with + ```d + .AllowNoValue("myvalue") + ``` + * Dropped support for DMD-2.099. ### Enhancements and bug fixes diff --git a/docs/code_snippets/arity_no_values.d b/docs/code_snippets/arity_no_values.d index 3cb20fc..8c19e84 100644 --- a/docs/code_snippets/arity_no_values.d +++ b/docs/code_snippets/arity_no_values.d @@ -2,7 +2,7 @@ import argparse; struct T { - @(NamedArgument.AllowNoValue !10) int a; + @(NamedArgument.AllowNoValue(10)) int a; @(NamedArgument.RequireNoValue!20) int b; } diff --git a/docs/topics/Arity.md b/docs/topics/Arity.md index 67a5688..fbed502 100644 --- a/docs/topics/Arity.md +++ b/docs/topics/Arity.md @@ -44,9 +44,9 @@ Example: ## Named arguments with no values -Sometimes named arguments are can have no values in command line. Here are two cases that arise in this situation: +Sometimes named arguments can have no values in command line. Here are two cases that arise in this situation: -- Argument should get specific value if there is no value provided in command line. Use `AllowNoValue` in this case. +- If value is optional and argument should get specific value in this case then use `AllowNoValue`. - Argument must not have any values in command line. Use `RequireNoValue` in this case. diff --git a/source/argparse/api/ansi.d b/source/argparse/api/ansi.d index 61e6073..e9cec93 100644 --- a/source/argparse/api/ansi.d +++ b/source/argparse/api/ansi.d @@ -2,6 +2,7 @@ module argparse.api.ansi; import argparse.config; import argparse.param; +import argparse.result; import argparse.api.argument: NamedArgument, Description, NumberOfValues, AllowedValues, Parse, Action, ActionNoValue; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -29,22 +30,25 @@ private struct AnsiStylingArgument { switch(param.value) { - case "auto": isEnabled = param.config.stylingMode == Config.StylingMode.on; return; - case "always": isEnabled = true; return; - case "never": isEnabled = false; return; + case "auto": receiver.isEnabled = param.config.stylingMode == Config.StylingMode.on; return Result.Success; + case "always": receiver.isEnabled = true; return Result.Success; + case "never": receiver.isEnabled = false; return Result.Success; default: } + return Result.Success; }; - private enum actionNoValue = (ref AnsiStylingArgument receiver, Param!void param) + private enum actionNoValue = (ref AnsiStylingArgument receiver, Param!void _) { - isEnabled = true; + receiver.isEnabled = true; + return Result.Success; }; } unittest { AnsiStylingArgument arg; + assert(!arg); AnsiStylingArgument.actionNoValue(arg, Param!void.init); assert(arg); diff --git a/source/argparse/api/argument.d b/source/argparse/api/argument.d index e6507b8..b1d8287 100644 --- a/source/argparse/api/argument.d +++ b/source/argparse/api/argument.d @@ -168,14 +168,14 @@ unittest /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -auto AllowNoValue(alias valueToUse, T)(ArgumentUDA!T uda) +auto AllowNoValue(VALUE, T)(ArgumentUDA!T uda, VALUE valueToUse) { - return ActionNoValue(uda, (ref typeof(valueToUse) _) { _ = valueToUse; }); + return ActionNoValueImpl(uda,SetValue(valueToUse)); } auto RequireNoValue(alias valueToUse, T)(ArgumentUDA!T uda) { - auto desc = uda.AllowNoValue!valueToUse; + auto desc = uda.AllowNoValue(valueToUse); desc.info.minValuesCount = 0; desc.info.maxValuesCount = 0; return desc; @@ -183,8 +183,8 @@ auto RequireNoValue(alias valueToUse, T)(ArgumentUDA!T uda) unittest { - auto uda = NamedArgument.AllowNoValue!({}); - assert(is(typeof(uda) : ArgumentUDA!(ValueParser!(P, R)), P, R)); + auto uda = NamedArgument.AllowNoValue("value"); + assert(is(typeof(uda) : ArgumentUDA!(ValueParser!(void, string)))); assert(uda.info.minValuesCount == 0); } @@ -271,14 +271,7 @@ private auto ActionNoValueImpl(T, RECEIVER)(ArgumentUDA!T uda, NoValueActionFunc return desc; } -package auto ActionNoValue(T, RETURN, RECEIVER)(ArgumentUDA!T uda, RETURN function(ref RECEIVER receiver) func) -if(is(RETURN == void) || is(RETURN == bool) || is(RETURN == Result)) -{ - return ActionNoValueImpl(uda, NoValueActionFunc!RECEIVER(func)); -} - -package auto ActionNoValue(T, RETURN, RECEIVER)(ArgumentUDA!T uda, RETURN function(ref RECEIVER receiver, Param!void param) func) -if(is(RETURN == void) || is(RETURN == bool) || is(RETURN == Result)) +package auto ActionNoValue(T, RECEIVER)(ArgumentUDA!T uda, Result function(ref RECEIVER receiver, Param!void param) func) { return ActionNoValueImpl(uda, NoValueActionFunc!RECEIVER(func)); } diff --git a/source/argparse/internal/novalueactionfunc.d b/source/argparse/internal/novalueactionfunc.d index a709583..c5c099d 100644 --- a/source/argparse/internal/novalueactionfunc.d +++ b/source/argparse/internal/novalueactionfunc.d @@ -5,52 +5,43 @@ import argparse.param; import argparse.result; import argparse.internal.errorhelpers; +import std.meta; import std.traits; import std.sumtype; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -private struct Handler(RECEIVER) +package(argparse) struct NoValueActionFunc(RECEIVER) { - static Result opCall(bool function(ref RECEIVER receiver) func, ref RECEIVER receiver, Param!void param) - { - return func(receiver) ? Result.Success : processingError(param); - } - static Result opCall(void function(ref RECEIVER receiver) func, ref RECEIVER receiver, Param!void param) - { - func(receiver); - return Result.Success; - } - static Result opCall(Result function(ref RECEIVER receiver) func, ref RECEIVER receiver, Param!void param) - { - return func(receiver); - } - static Result opCall(bool function(ref RECEIVER receiver, Param!void param) func, ref RECEIVER receiver, Param!void param) - { - return func(receiver, param) ? Result.Success : processingError(param); - } - static Result opCall(void function(ref RECEIVER receiver, Param!void param) func, ref RECEIVER receiver, Param!void param) + private struct ProcessingError { - func(receiver, param); - return Result.Success; + Result opCall(ref RECEIVER receiver, Param!void param) const + { + return processingError(param); + } } - static Result opCall(Result function(ref RECEIVER receiver, Param!void param) func, ref RECEIVER receiver, Param!void param) + + private struct SetValue { - return func(receiver, param); + RECEIVER value; + + this(RECEIVER v) + { + value = v; + } + + Result opCall(ref RECEIVER receiver, Param!void param) const + { + import std.conv: to; + + receiver = value.to!RECEIVER; + + return Result.Success; + } } -} -// bool action(ref DEST receiver) -// void action(ref DEST receiver) -// Result action(ref DEST receiver) -// bool action(ref DEST receiver, Param!void param) -// void action(ref DEST receiver, Param!void param) -// Result action(ref DEST receiver, Param!void param) -package(argparse) struct NoValueActionFunc(RECEIVER) -{ - alias getFirstParameter(T) = Parameters!T[0]; - alias TYPES = staticMap!(getFirstParameter, typeof(__traits(getOverloads, Handler!RECEIVER, "opCall"))); + alias TYPES = AliasSeq!(ProcessingError, Result function(ref RECEIVER receiver, Param!void param), SetValue); SumType!TYPES F; @@ -60,12 +51,6 @@ package(argparse) struct NoValueActionFunc(RECEIVER) F = func; } - static foreach(T; TYPES) - auto opAssign(T func) - { - F = func; - } - bool opCast(T : bool)() const { return F != typeof(F).init; @@ -73,7 +58,7 @@ package(argparse) struct NoValueActionFunc(RECEIVER) Result opCall(ref RECEIVER receiver, Param!void param) const { - return F.match!(_ => Handler!RECEIVER(_, receiver, param)); + return F.match!(_ => _(receiver, param)); } } @@ -91,27 +76,16 @@ unittest return NoValueActionFunc!T(func)(receiver, Param!void.init); } - // Result action(ref DEST receiver) - assert(test!int((ref int r) { r=7; return Result.Success; }) == 7); - assert(testErr!int((ref int r) => Result.Error("error text")).isError("error text")); - - // bool action(ref DEST receiver) - assert(test!int((ref int p) { p=7; return true; }) == 7); - assert(testErr!int((ref int p) => false).isError("Can't process value")); - - // void action(ref DEST receiver) - assert(test!int((ref int p) { p=7; }) == 7); - // Result action(ref DEST receiver, Param!void param) assert(test!int((ref int r, Param!void p) { r=7; return Result.Success; }) == 7); assert(testErr!int((ref int r, Param!void p) => Result.Error("error text")).isError("error text")); +} - // bool action(ref DEST receiver, Param!void param) - assert(test!int((ref int r, Param!void p) { r=7; return true; }) == 7); - assert(testErr!int((ref int r, Param!void p) => false).isError("Can't process value")); +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // void action(ref DEST receiver, Param!void param) - assert(test!int((ref int r, Param!void p) { r=7; }) == 7); +package(argparse) auto SetValue(VALUE)(VALUE value) +{ + return NoValueActionFunc!VALUE(NoValueActionFunc!VALUE.SetValue(value)); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/source/argparse/internal/valueparser.d b/source/argparse/internal/valueparser.d index 4e5b882..87b40bc 100644 --- a/source/argparse/internal/valueparser.d +++ b/source/argparse/internal/valueparser.d @@ -150,9 +150,6 @@ package(argparse) struct ValueParser(PARSE_TYPE, RECEIVER_TYPE) static if(is(RECEIVER_TYPE == PARSE_TYPE)) res.action = (ref RECEIVER_TYPE receiver, Param!PARSE_TYPE param) { receiver = param.value; }; - if(!res.noValueAction) - res.noValueAction = (ref RECEIVER_TYPE _, param) => processingError(param); - return res; } @@ -172,7 +169,6 @@ package(argparse) struct ValueParser(PARSE_TYPE, RECEIVER_TYPE) assert(parse); assert(validate); assert(action); - assert(noValueAction); if (rawParam.value.length == 0) { @@ -256,7 +252,7 @@ if(!is(T == void)) default: return false; } })) - .changeNoValueAction(NoValueActionFunc!T((ref T receiver) { receiver = true; })); + .changeNoValueAction(SetValue(true)); } else static if(isSomeChar!T) { @@ -299,14 +295,14 @@ if(!is(T == void)) enum TypedValueParser = ValueParser!(T, T).defaults .changeParse(parseValue!T) .changeAction(action) - .changeNoValueAction(NoValueActionFunc!T((ref T receiver) => Result.Success)); + .changeNoValueAction(NoValueActionFunc!T((ref _1, _2) => Result.Success)); } else static if(!isArray!(ForeachType!TElement) || isSomeString!(ForeachType!TElement)) // 2D array { enum TypedValueParser = ValueParser!(TElement, T).defaults .changeParse(parseValue!TElement) .changeAction(Extend!T) - .changeNoValueAction(NoValueActionFunc!T((ref T receiver) { receiver ~= TElement.init; })); + .changeNoValueAction(NoValueActionFunc!T((ref T receiver, _) { receiver ~= TElement.init; return Result.Success; })); } else static assert(false, "Multi-dimentional arrays are not supported: " ~ T.stringof); @@ -341,7 +337,7 @@ if(!is(T == void)) } return Result.Success; })) - .changeNoValueAction(NoValueActionFunc!T((ref T receiver) => Result.Success)); + .changeNoValueAction(NoValueActionFunc!T((ref _1, _2) => Result.Success)); } else static if(is(T == function) || is(T == delegate) || is(typeof(*T) == function) || is(typeof(*T) == delegate)) { diff --git a/source/argparse/package.d b/source/argparse/package.d index 9937d48..36438f3 100644 --- a/source/argparse/package.d +++ b/source/argparse/package.d @@ -144,7 +144,7 @@ unittest int no_b; @(NamedArgument(["b", "boo"]).Description("Flag boo") - .AllowNoValue!55 + .AllowNoValue(55) ) int b; @@ -457,7 +457,7 @@ unittest { struct T { - @(NamedArgument.AllowNoValue !10) int a; + @(NamedArgument.AllowNoValue(10)) int a; @(NamedArgument.RequireNoValue!20) int b; }