Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change AllowNoValue to accept run-time parameter #187

Merged
merged 1 commit into from
Dec 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion docs/code_snippets/arity_no_values.d
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import argparse;

struct T
{
@(NamedArgument.AllowNoValue !10) int a;
@(NamedArgument.AllowNoValue(10)) int a;
@(NamedArgument.RequireNoValue!20) int b;
}

Expand Down
4 changes: 2 additions & 2 deletions docs/topics/Arity.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
14 changes: 9 additions & 5 deletions source/argparse/api/ansi.d
Original file line number Diff line number Diff line change
Expand Up @@ -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;

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -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);

Expand Down
19 changes: 6 additions & 13 deletions source/argparse/api/argument.d
Original file line number Diff line number Diff line change
Expand Up @@ -168,23 +168,23 @@ 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;
}

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);
}

Expand Down Expand Up @@ -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));
}
Expand Down
88 changes: 31 additions & 57 deletions source/argparse/internal/novalueactionfunc.d
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -60,20 +51,14 @@ 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;
}

Result opCall(ref RECEIVER receiver, Param!void param) const
{
return F.match!(_ => Handler!RECEIVER(_, receiver, param));
return F.match!(_ => _(receiver, param));
}
}

Expand All @@ -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));
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Expand Down
12 changes: 4 additions & 8 deletions source/argparse/internal/valueparser.d
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand All @@ -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)
{
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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))
{
Expand Down
4 changes: 2 additions & 2 deletions source/argparse/package.d
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ unittest
int no_b;

@(NamedArgument(["b", "boo"]).Description("Flag boo")
.AllowNoValue!55
.AllowNoValue(55)
)
int b;

Expand Down Expand Up @@ -457,7 +457,7 @@ unittest
{
struct T
{
@(NamedArgument.AllowNoValue !10) int a;
@(NamedArgument.AllowNoValue(10)) int a;
@(NamedArgument.RequireNoValue!20) int b;
}

Expand Down
Loading