Skip to content

Commit

Permalink
Replace template parameters with function arguments in custom parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
andrey-zherikov committed Oct 22, 2024
1 parent 40ede61 commit d190e2e
Show file tree
Hide file tree
Showing 16 changed files with 1,299 additions and 1,018 deletions.
8 changes: 4 additions & 4 deletions docs/code_snippets/parsing_customization.d
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import argparse;
struct T
{
@(NamedArgument
.PreValidation!((string s) { return s.length > 1 && s[0] == '!'; })
.Parse !((string s) { return s[1]; })
.Validation !((char v) { return v >= '0' && v <= '9'; })
.Action !((ref int a, char v) { a = v - '0'; })
.PreValidation((string s) { return s.length > 1 && s[0] == '!'; })
.Parse ((string s) { return cast(char) s[1]; })
.Validation ((char v) { return v >= '0' && v <= '9'; })
.Action ((ref int a, char v) { a = v - '0'; })
)
int a;
}
Expand Down
2 changes: 1 addition & 1 deletion docs/code_snippets/types_custom.d
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ struct Value
}
struct T
{
@(NamedArgument.Parse!((string s) { return Value(s); }))
@(NamedArgument.Parse((string s) { return Value(s); }))
Value s;
}

Expand Down
30 changes: 15 additions & 15 deletions source/argparse/api/ansi.d
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ module argparse.api.ansi;
import argparse.config;
import argparse.param;
import argparse.api.argument: NamedArgument, Description, NumberOfValues, AllowedValues, Parse, Action, ActionNoValue;
import argparse.internal.parsehelpers: PassThrough;

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Public API for ANSI coloring
Expand All @@ -13,9 +12,9 @@ import argparse.internal.parsehelpers: PassThrough;
.Description("Colorize the output. If value is omitted then 'always' is used.")
.AllowedValues!(["always","auto","never"])
.NumberOfValues(0, 1)
.Parse!(PassThrough)
.Action!(AnsiStylingArgument.action)
.ActionNoValue!(AnsiStylingArgument.action)
.Parse((string _) => _)
.Action(AnsiStylingArgument.action)
.ActionNoValue(AnsiStylingArgument.actionNoValue)
)
private struct AnsiStylingArgument
{
Expand All @@ -26,39 +25,40 @@ private struct AnsiStylingArgument
return isEnabled;
}

private static void action(ref AnsiStylingArgument receiver, RawParam param)
private enum action = (ref AnsiStylingArgument receiver, Param!string param)
{
switch(param.value[0])
switch(param.value)
{
case "auto": isEnabled = param.config.stylingMode == Config.StylingMode.on; return;
case "always": isEnabled = true; return;
case "never": isEnabled = false; return;
default:
}
}
private static void action(ref AnsiStylingArgument receiver, Param!void param)
};

private enum actionNoValue = (ref AnsiStylingArgument receiver, Param!void param)
{
isEnabled = true;
}
};
}

unittest
{
AnsiStylingArgument arg;
AnsiStylingArgument.action(arg, Param!void.init);
AnsiStylingArgument.actionNoValue(arg, Param!void.init);
assert(arg);

AnsiStylingArgument.action(arg, RawParam(null, "", [""]));
AnsiStylingArgument.action(arg, Param!string(null, "", ""));
}

unittest
{
AnsiStylingArgument arg;

AnsiStylingArgument.action(arg, RawParam(null, "", ["always"]));
AnsiStylingArgument.action(arg, Param!string(null, "", "always"));
assert(arg);

AnsiStylingArgument.action(arg, RawParam(null, "", ["never"]));
AnsiStylingArgument.action(arg, Param!string(null, "", "never"));
assert(!arg);
}

Expand All @@ -68,11 +68,11 @@ unittest
AnsiStylingArgument arg;

config.stylingMode = Config.StylingMode.on;
AnsiStylingArgument.action(arg, RawParam(&config, "", ["auto"]));
AnsiStylingArgument.action(arg, Param!string(&config, "", "auto"));
assert(arg);

config.stylingMode = Config.StylingMode.off;
AnsiStylingArgument.action(arg, RawParam(&config, "", ["auto"]));
AnsiStylingArgument.action(arg, Param!string(&config, "", "auto"));
assert(!arg);
}

Expand Down
140 changes: 81 additions & 59 deletions source/argparse/api/argument.d
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ import argparse.param;
import argparse.result;

import argparse.internal.arguments: ArgumentInfo;
import argparse.internal.argumentuda: ArgumentUDA;
import argparse.internal.argumentuda: ArgumentUDA, createArgumentUDA;
import argparse.internal.valueparser: ValueParser;
import argparse.internal.parsehelpers: ValueInList;
import argparse.internal.actionfunc;
import argparse.internal.novalueactionfunc;
import argparse.internal.parsefunc;
import argparse.internal.validationfunc;
import argparse.internal.utils: formatAllowedValues;

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -82,7 +85,7 @@ auto ref MaxNumberOfValues(T)(auto ref ArgumentUDA!T uda, size_t max)

unittest
{
ArgumentUDA!void arg;
ArgumentUDA!(ValueParser!(void, void)) arg;
assert(!arg.info.hideFromHelp);
assert(!arg.info.required);
assert(arg.info.minValuesCount.isNull);
Expand All @@ -109,22 +112,13 @@ unittest
arg = arg.MinNumberOfValues(2).MaxNumberOfValues(3);
assert(arg.info.minValuesCount.get == 2);
assert(arg.info.maxValuesCount.get == 3);

// values shouldn't be changed
arg.addDefaults(ArgumentUDA!void.init);
assert(arg.info.placeholder == "text");
assert(arg.info.description.get == "qwer");
assert(arg.info.hideFromHelp);
assert(!arg.info.required);
assert(arg.info.minValuesCount.get == 2);
assert(arg.info.maxValuesCount.get == 3);
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

auto PositionalArgument(uint position)
{
auto arg = ArgumentUDA!(ValueParser!(void, void, void, void, void, void))(ArgumentInfo.init).Required();
auto arg = ArgumentUDA!(ValueParser!(void, void))(ArgumentInfo.init).Required();
arg.info.position = position;
return arg;
}
Expand All @@ -136,7 +130,7 @@ auto PositionalArgument(uint position, string placeholder)

auto NamedArgument(string[] names...)
{
return ArgumentUDA!(ValueParser!(void, void, void, void, void, void))(ArgumentInfo(names.dup)).Optional();
return ArgumentUDA!(ValueParser!(void, void))(ArgumentInfo(names.dup)).Optional();
}

unittest
Expand Down Expand Up @@ -176,7 +170,7 @@ unittest

auto AllowNoValue(alias valueToUse, T)(ArgumentUDA!T uda)
{
return uda.ActionNoValue!(() => valueToUse);
return ActionNoValue(uda, (ref typeof(valueToUse) _) { _ = valueToUse; });
}

auto RequireNoValue(alias valueToUse, T)(ArgumentUDA!T uda)
Expand All @@ -190,33 +184,37 @@ auto RequireNoValue(alias valueToUse, T)(ArgumentUDA!T uda)
unittest
{
auto uda = NamedArgument.AllowNoValue!({});
assert(is(typeof(uda) : ArgumentUDA!(ValueParser!(void, void, void, void, void, FUNC)), alias FUNC));
assert(!is(FUNC == void));
assert(is(typeof(uda) : ArgumentUDA!(ValueParser!(P, R)), P, R));
assert(uda.info.minValuesCount == 0);
}

unittest
{
auto uda = NamedArgument.RequireNoValue!"value";
assert(is(typeof(uda) : ArgumentUDA!(ValueParser!(void, void, void, void, void, FUNC)), alias FUNC));
assert(!is(FUNC == void));
assert(is(typeof(uda) : ArgumentUDA!(ValueParser!(P, R)), P, R));
assert(uda.info.minValuesCount == 0);
assert(uda.info.maxValuesCount == 0);
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Parsing customization

auto PreValidation(alias func, T)(ArgumentUDA!T uda)
auto PreValidation(T, RETURN, VALUE)(ArgumentUDA!T uda, RETURN function(VALUE value) func)
if((is(VALUE == string) || is(VALUE == string[]) || is(VALUE == RawParam)) &&
(is(RETURN == bool) || is(RETURN == Result)))
{
return ArgumentUDA!(uda.parsingFunc.changePreValidation!func)(uda.tupleof);
auto desc = createArgumentUDA(uda.info, uda.valueParser.changePreValidation(ValidationFunc!(string[])(func)));

return desc;
}

auto Parse(alias func, T)(ArgumentUDA!T uda)
///////////////////////////

private auto ParseImpl(T, RECEIVER)(ArgumentUDA!T uda, ParseFunc!RECEIVER func)
{
auto desc = ArgumentUDA!(uda.parsingFunc.changeParse!func)(uda.tupleof);
auto desc = createArgumentUDA(uda.info, uda.valueParser.changeParse(func));

static if(__traits(compiles, { func(string.init); }))
static if(is(RECEIVER == string))
desc.info.minValuesCount = desc.info.maxValuesCount = 1;
else
{
Expand All @@ -227,82 +225,106 @@ auto Parse(alias func, T)(ArgumentUDA!T uda)
return desc;
}

auto Validation(alias func, T)(ArgumentUDA!T uda)
auto Parse(T, RECEIVER, VALUE)(ArgumentUDA!T uda, RECEIVER function(VALUE value) func)
if((is(VALUE == string) || is(VALUE == string[]) || is(VALUE == RawParam)))
{
return ParseImpl(uda, ParseFunc!RECEIVER(func));
}

auto Parse(T, RETURN, RECEIVER)(ArgumentUDA!T uda, RETURN function(ref RECEIVER receiver, RawParam param) func)
if(is(RETURN == void) || is(RETURN == bool) || is(RETURN == Result))
{
return ParseImpl(uda, ParseFunc!RECEIVER(func));
}

///////////////////////////

auto Validation(T, RETURN, VALUE)(ArgumentUDA!T uda, RETURN function(VALUE value) func)
if(is(RETURN == bool) || is(RETURN == Result))
{
return ArgumentUDA!(uda.parsingFunc.changeValidation!func)(uda.tupleof);
static if(!is(VALUE == Param!TYPE, TYPE))
alias TYPE = VALUE;

auto desc = createArgumentUDA(uda.info, uda.valueParser.changeValidation(ValidationFunc!TYPE(func)));

return desc;
}

auto Action(alias func, T)(ArgumentUDA!T uda)
///////////////////////////

auto Action(T, RETURN, RECEIVER, VALUE)(ArgumentUDA!T uda, RETURN function(ref RECEIVER receiver, VALUE value) func)
if(is(RETURN == void) || is(RETURN == bool) || is(RETURN == Result))
{
return ArgumentUDA!(uda.parsingFunc.changeAction!func)(uda.tupleof);
static if(!is(VALUE == Param!TYPE, TYPE))
alias TYPE = VALUE;

auto desc = createArgumentUDA(uda.info, uda.valueParser.changeAction(ActionFunc!(RECEIVER, TYPE)(func)));

return desc;
}

package auto ActionNoValue(alias func, T)(ArgumentUDA!T uda)
///////////////////////////
private auto ActionNoValueImpl(T, RECEIVER)(ArgumentUDA!T uda, NoValueActionFunc!RECEIVER func)
{
auto desc = ArgumentUDA!(uda.parsingFunc.changeNoValueAction!func)(uda.tupleof);
auto desc = createArgumentUDA(uda.info, uda.valueParser.changeNoValueAction(func));
desc.info.minValuesCount = 0;
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));
}

unittest
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))
{
auto uda = NamedArgument.PreValidation!({});
assert(is(typeof(uda) : ArgumentUDA!(ValueParser!(void, FUNC, void, void, void, void)), alias FUNC));
assert(!is(FUNC == void));
return ActionNoValueImpl(uda, NoValueActionFunc!RECEIVER(func));
}

///////////////////////////

unittest
{
auto uda = NamedArgument.Parse!({});
assert(is(typeof(uda) : ArgumentUDA!(ValueParser!(void, void, FUNC, void, void, void)), alias FUNC));
assert(!is(FUNC == void));
auto uda = NamedArgument.PreValidation((RawParam _) => true);
assert(is(typeof(uda) : ArgumentUDA!(ValueParser!(void, void))));
}

unittest
{
auto uda = NamedArgument.Parse!((string _) => _);
assert(is(typeof(uda) : ArgumentUDA!(ValueParser!(void, void, FUNC, void, void, void)), alias FUNC));
assert(!is(FUNC == void));
auto uda = NamedArgument.Parse((string _) => _);
assert(is(typeof(uda) : ArgumentUDA!(ValueParser!(P, void)), alias P));
assert(uda.info.minValuesCount == 1);
assert(uda.info.maxValuesCount == 1);
}

unittest
{
auto uda = NamedArgument.Parse!((string[] _) => _);
assert(is(typeof(uda) : ArgumentUDA!(ValueParser!(void, void, FUNC, void, void, void)), alias FUNC));
assert(!is(FUNC == void));
auto uda = NamedArgument.Parse((string[] _) => _);
assert(is(typeof(uda) : ArgumentUDA!(ValueParser!(P, void)), alias P));
assert(uda.info.minValuesCount == 0);
assert(uda.info.maxValuesCount == size_t.max);
}

unittest
{
auto uda = NamedArgument.Validation!({});
assert(is(typeof(uda) : ArgumentUDA!(ValueParser!(void, void, void, FUNC, void, void)), alias FUNC));
assert(!is(FUNC == void));
auto uda = NamedArgument.Validation((RawParam _) => true);
assert(is(typeof(uda) : ArgumentUDA!(ValueParser!(P, void)), alias P));
}

unittest
{
auto uda = NamedArgument.Action!({});
assert(is(typeof(uda) : ArgumentUDA!(ValueParser!(void, void, void, void, FUNC, void)), alias FUNC));
assert(!is(FUNC == void));
auto uda = NamedArgument.Action((ref string _1, RawParam _2) {});
assert(is(typeof(uda) : ArgumentUDA!(ValueParser!(P, R)), alias P, alias R));
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


auto AllowedValues(alias values, T)(ArgumentUDA!T uda)
{
import std.array : assocArray;
import std.range : repeat;
import std.traits: KeyType;

enum valuesAA = assocArray(values, false.repeat);

auto desc = uda.Validation!(ValueInList!(values, KeyType!(typeof(valuesAA))));
auto desc = uda.Validation((Param!(typeof(values[0])) _) => ValueInList(values)(_));
if(desc.info.placeholder.length == 0)
desc.info.placeholder = formatAllowedValues(values);

Expand All @@ -318,19 +340,19 @@ unittest

private struct CounterParsingFunction
{
static Result parse(T)(ref T receiver, const ref RawParam param)
static Result parseParameter(T)(T* receiver, RawParam param)
{
assert(param.value.length == 0);

++receiver;
++(*receiver);

return Result.Success;
}
}

auto Counter(T)(ArgumentUDA!T uda)
{
auto desc = ArgumentUDA!CounterParsingFunction(uda.tupleof);
auto desc = ArgumentUDA!CounterParsingFunction(uda.info);
desc.info.minValuesCount = 0;
desc.info.maxValuesCount = 0;
return desc;
Expand Down
Loading

0 comments on commit d190e2e

Please sign in to comment.