From 235ffd0863b191b238439fc0345ce9d714cf0fc9 Mon Sep 17 00:00:00 2001 From: Andrey Zherikov Date: Fri, 1 Nov 2024 17:15:02 -0400 Subject: [PATCH 1/2] Make Param.name immutable --- source/argparse/param.d | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source/argparse/param.d b/source/argparse/param.d index ae1e512..0197587 100644 --- a/source/argparse/param.d +++ b/source/argparse/param.d @@ -7,7 +7,7 @@ import argparse.config; struct Param(VALUE_TYPE) { const(Config)* config; - string name; + immutable string name; static if(!is(VALUE_TYPE == void)) VALUE_TYPE value; @@ -21,5 +21,4 @@ unittest { RawParam p1; auto p2 = p1; - p1 = p2; } From adb60c271228a3a467d5c063b752f7fd34de8c80 Mon Sep 17 00:00:00 2001 From: Andrey Zherikov Date: Fri, 1 Nov 2024 17:25:28 -0400 Subject: [PATCH 2/2] Use RawParam in parsing --- source/argparse/internal/arguments.d | 69 +++++++++++++++----------- source/argparse/internal/argumentuda.d | 8 +-- source/argparse/internal/command.d | 37 +++++++------- source/argparse/internal/help.d | 5 +- source/argparse/internal/parser.d | 44 ++++++++-------- 5 files changed, 90 insertions(+), 73 deletions(-) diff --git a/source/argparse/internal/arguments.d b/source/argparse/internal/arguments.d index b845a41..5faff2f 100644 --- a/source/argparse/internal/arguments.d +++ b/source/argparse/internal/arguments.d @@ -3,6 +3,7 @@ module argparse.internal.arguments; import argparse.internal.lazystring; import argparse.config; +import argparse.param; import argparse.result; import std.typecons: Nullable; @@ -36,23 +37,23 @@ package(argparse) struct ArgumentInfo Nullable!size_t minValuesCount; Nullable!size_t maxValuesCount; - auto checkValuesCount(const Config config, string argName, size_t count) const + auto checkValuesCount(const ref RawParam param) const { immutable min = minValuesCount.get; immutable max = maxValuesCount.get; // override for boolean flags - if(isBooleanFlag && count == 1) + if(isBooleanFlag && param.value.length == 1) return Result.Success; - if(min == max && count != min) - return Result.Error("Argument '",config.styling.argumentName(argName),"': expected ",min,min == 1 ? " value" : " values"); + if(min == max && param.value.length != min) + return Result.Error("Argument '",param.config.styling.argumentName(param.name),"': expected ",min,min == 1 ? " value" : " values"); - if(count < min) - return Result.Error("Argument '",config.styling.argumentName(argName),"': expected at least ",min,min == 1 ? " value" : " values"); + if(param.value.length < min) + return Result.Error("Argument '",param.config.styling.argumentName(param.name),"': expected at least ",min,min == 1 ? " value" : " values"); - if(count > max) - return Result.Error("Argument '",config.styling.argumentName(argName),"': expected at most ",max,max == 1 ? " value" : " values"); + if(param.value.length > max) + return Result.Error("Argument '",param.config.styling.argumentName(param.name),"': expected at most ",max,max == 1 ? " value" : " values"); return Result.Success; } @@ -70,28 +71,36 @@ unittest return info; } - assert(info(2,4).checkValuesCount(Config.init, "", 1).isError("expected at least 2 values")); - assert(info(2,4).checkValuesCount(Config.init, "", 2)); - assert(info(2,4).checkValuesCount(Config.init, "", 3)); - assert(info(2,4).checkValuesCount(Config.init, "", 4)); - assert(info(2,4).checkValuesCount(Config.init, "", 5).isError("expected at most 4 values")); - - assert(info(2,2).checkValuesCount(Config.init, "", 1).isError("expected 2 values")); - assert(info(2,2).checkValuesCount(Config.init, "", 2)); - assert(info(2,2).checkValuesCount(Config.init, "", 3).isError("expected 2 values")); - - assert(info(1,1).checkValuesCount(Config.init, "", 0).isError("expected 1 value")); - assert(info(1,1).checkValuesCount(Config.init, "", 1)); - assert(info(1,1).checkValuesCount(Config.init, "", 2).isError("expected 1 value")); - - assert(info(0,1).checkValuesCount(Config.init, "", 0)); - assert(info(0,1).checkValuesCount(Config.init, "", 1)); - assert(info(0,1).checkValuesCount(Config.init, "", 2).isError("expected at most 1 value")); - - assert(info(1,2).checkValuesCount(Config.init, "", 0).isError("expected at least 1 value")); - assert(info(1,2).checkValuesCount(Config.init, "", 1)); - assert(info(1,2).checkValuesCount(Config.init, "", 2)); - assert(info(1,2).checkValuesCount(Config.init, "", 3).isError("expected at most 2 values")); + Config config; + auto p0 = RawParam(&config, "", []); + auto p1 = RawParam(&config, "", ["",]); + auto p2 = RawParam(&config, "", ["","",]); + auto p3 = RawParam(&config, "", ["","","",]); + auto p4 = RawParam(&config, "", ["","","","",]); + auto p5 = RawParam(&config, "", ["","","","","",]); + + assert(info(2,4).checkValuesCount(p1).isError("expected at least 2 values")); + assert(info(2,4).checkValuesCount(p2)); + assert(info(2,4).checkValuesCount(p3)); + assert(info(2,4).checkValuesCount(p4)); + assert(info(2,4).checkValuesCount(p5).isError("expected at most 4 values")); + + assert(info(2,2).checkValuesCount(p1).isError("expected 2 values")); + assert(info(2,2).checkValuesCount(p2)); + assert(info(2,2).checkValuesCount(p3).isError("expected 2 values")); + + assert(info(1,1).checkValuesCount(p0).isError("expected 1 value")); + assert(info(1,1).checkValuesCount(p1)); + assert(info(1,1).checkValuesCount(p2).isError("expected 1 value")); + + assert(info(0,1).checkValuesCount(p0)); + assert(info(0,1).checkValuesCount(p1)); + assert(info(0,1).checkValuesCount(p2).isError("expected at most 1 value")); + + assert(info(1,2).checkValuesCount(p0).isError("expected at least 1 value")); + assert(info(1,2).checkValuesCount(p1)); + assert(info(1,2).checkValuesCount(p2)); + assert(info(1,2).checkValuesCount(p3).isError("expected at most 2 values")); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/source/argparse/internal/argumentuda.d b/source/argparse/internal/argumentuda.d index 470e3aa..c2caad8 100644 --- a/source/argparse/internal/argumentuda.d +++ b/source/argparse/internal/argumentuda.d @@ -18,19 +18,19 @@ package(argparse) struct ArgumentUDA(ValueParser) package(argparse) ValueParser valueParser; - package Result parse(COMMAND_STACK, RECEIVER)(const Config config, const COMMAND_STACK cmdStack, ref RECEIVER receiver, string argName, string[] rawValues) + package Result parse(COMMAND_STACK, RECEIVER)(const COMMAND_STACK cmdStack, ref RECEIVER receiver, RawParam param) { try { - auto res = info.checkValuesCount(config, argName, rawValues.length); + auto res = info.checkValuesCount(param); if(!res) return res; - return valueParser.parseParameter(receiver, RawParam(&config, argName, rawValues)); + return valueParser.parseParameter(receiver, param); } catch(Exception e) { - return Result.Error("Argument '", config.styling.argumentName(argName), ": ", e.msg); + return Result.Error("Argument '", param.config.styling.argumentName(param.name), ": ", e.msg); } } diff --git a/source/argparse/internal/command.d b/source/argparse/internal/command.d index cfef64d..6700689 100644 --- a/source/argparse/internal/command.d +++ b/source/argparse/internal/command.d @@ -41,23 +41,24 @@ package struct SubCommands /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -private Result ArgumentParsingFunction(Config config, alias uda, RECEIVER)(const Command[] cmdStack, - ref RECEIVER receiver, - string argName, - string[] rawValues) +private Result ArgumentParsingFunction(alias uda, RECEIVER)(const Command[] cmdStack, + ref RECEIVER receiver, + ref RawParam param) { static if(uda.info.memberSymbol) auto target = &__traits(getMember, receiver, uda.info.memberSymbol); else auto target = null; - return uda.parse(config, cmdStack, target, argName, rawValues); + return uda.parse(cmdStack, target, param); } -private Result ArgumentCompleteFunction(Config config, alias uda, RECEIVER)(const Command[] cmdStack, - ref RECEIVER receiver, - string argName, - string[] rawValues) +private alias getArgumentParsingFunction1(alias uda) = + (const Command[] cmdStack, ref RawParam param) => ArgumentParsingFunction!uda(cmdStack, receiver, param); + +private Result ArgumentCompleteFunction(alias uda, RECEIVER)(const Command[] cmdStack, + ref RECEIVER receiver, + ref RawParam param) { return Result.Success; } @@ -70,10 +71,12 @@ unittest auto test(string[] values) { T t; + Config cfg; + auto param = RawParam(&cfg, "a", values); enum uda = getMemberArgumentUDA!(T, "a")(Config.init); - return ArgumentParsingFunction!(Config.init, uda)([], t, "a", values); + return ArgumentParsingFunction!uda([], t, param); } assert(test(["raw-value"])); @@ -88,19 +91,21 @@ unittest } T t; + Config cfg; + auto param = RawParam(&cfg, "func", []); enum uda = getMemberArgumentUDA!(T, "func")(Config.init); - auto res = ArgumentParsingFunction!(Config.init, uda)([], t, "func", []); + auto res = ArgumentParsingFunction!uda([], t, param); - assert(res.isError(Config.init.styling.argumentName("func")~": My Message.")); + assert(res.isError("func",": My Message.")); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// package struct Command { - alias Parse = Result delegate(const Command[] cmdStack, string argName, string[] argValue); + alias Parse = Result delegate(const Command[] cmdStack,ref RawParam param); struct Argument { @@ -351,14 +356,12 @@ package(argparse) Command createCommand(Config config, COMMAND_TYPE)(ref COMMAND res.restrictions.add!(COMMAND_TYPE, [typeTraits.argumentInfos])(config); enum getArgumentParsingFunction(alias uda) = - (const Command[] cmdStack, string argName, string[] argValue) - => ArgumentParsingFunction!(config, uda)(cmdStack, receiver, argName, argValue); + (const Command[] cmdStack, ref RawParam param) => ArgumentParsingFunction!uda(cmdStack, receiver, param); res.parseFuncs = [staticMap!(getArgumentParsingFunction, typeTraits.argumentUDAs)]; enum getArgumentCompleteFunction(alias uda) = - (const Command[] cmdStack, string argName, string[] argValue) - => ArgumentCompleteFunction!(config, uda)(cmdStack, receiver, argName, argValue); + (const Command[] cmdStack, ref RawParam param) => ArgumentCompleteFunction!uda(cmdStack, receiver, param); res.completeFuncs = [staticMap!(getArgumentCompleteFunction, typeTraits.argumentUDAs)]; diff --git a/source/argparse/internal/help.d b/source/argparse/internal/help.d index 95bdfc5..b0a28a9 100644 --- a/source/argparse/internal/help.d +++ b/source/argparse/internal/help.d @@ -2,6 +2,7 @@ module argparse.internal.help; import argparse.ansi; import argparse.config; +import argparse.param; import argparse.result; import argparse.api.ansi: ansiStylingArgument; import argparse.internal.lazystring; @@ -154,7 +155,7 @@ package struct HelpArgumentUDA return info; }(); - Result parse(COMMAND_STACK, RECEIVER)(const Config config, const COMMAND_STACK cmdStack, ref RECEIVER, string argName, string[] rawValues) + Result parse(COMMAND_STACK, RECEIVER)(const COMMAND_STACK cmdStack, ref RECEIVER receiver, RawParam param) { import std.stdio: stdout; import std.algorithm: map; @@ -165,7 +166,7 @@ package struct HelpArgumentUDA auto args = cmdStack.map!((ref _) => &_.arguments).array; auto output = stdout.lockingTextWriter(); - printHelp(_ => output.put(_), config, cmdStack[$-1], args, progName); + printHelp(_ => output.put(_), *param.config, cmdStack[$-1], args, progName); return Result.Error(0, ""); // hack to force-exit (to be removed) } diff --git a/source/argparse/internal/parser.d b/source/argparse/internal/parser.d index e017baf..c1075bd 100644 --- a/source/argparse/internal/parser.d +++ b/source/argparse/internal/parser.d @@ -4,6 +4,7 @@ import std.typecons: Nullable, nullable; import std.sumtype: SumType; import argparse.config; +import argparse.param; import argparse.result; import argparse.api.ansi: ansiStylingArgument; import argparse.internal.arguments: ArgumentInfo; @@ -92,13 +93,13 @@ private struct Argument { Result delegate() parse; Result delegate() complete; - this(string name, FindResult r, string[] values) + this(RawParam param, FindResult r) { index = r.arg.index; info = r.arg.info; - parse = () => r.arg.parse(r.cmdStack, name, values); - complete = () => r.arg.complete(r.cmdStack, name, values); + parse = () => r.arg.parse(r.cmdStack, param); + complete = () => r.arg.complete(r.cmdStack, param); } } @@ -108,7 +109,7 @@ private struct SubCommand { private alias Entry = SumType!(Unknown, Argument, SubCommand); -private Entry getNextEntry(bool bundling)(Config config, ref string[] args, +private Entry getNextEntry(bool bundling)(const ref Config config, ref string[] args, FindResult delegate(bool) findPositionalArg, FindResult delegate(string) findNamedArg, Command delegate() delegate(string) findCommand) @@ -133,6 +134,8 @@ private Entry getNextEntry(bool bundling)(Config config, ref string[] args, findCommand(str) is null; // command is not a value }; + auto createParam = (string name, string[] value) => RawParam(&config, name, value); + // Is it named argument (starting with '-' and longer than 1 character)? if(arg0[0] == config.namedArgPrefix && arg0.length > 1) { @@ -160,7 +163,7 @@ private Entry getNextEntry(bool bundling)(Config config, ref string[] args, if(res.arg) { args.popFront; - return Entry(Argument(usedName, res, [value])); + return Entry(Argument(createParam(usedName, [value]), res)); } } else @@ -174,7 +177,7 @@ private Entry getNextEntry(bool bundling)(Config config, ref string[] args, { args.popFront; auto values = consumeValuesFromCLI(args, res.arg.info.minValuesCount.get, res.arg.info.maxValuesCount.get, isArgumentValue); - return Entry(Argument(arg0, res, values)); + return Entry(Argument(createParam(arg0, values), res)); } } @@ -185,7 +188,7 @@ private Entry getNextEntry(bool bundling)(Config config, ref string[] args, if(res.arg && res.arg.info.isBooleanFlag) { args.popFront; - return Entry(Argument(arg0, res, ["false"])); + return Entry(Argument(createParam(arg0, ["false"]), res)); } } } @@ -218,7 +221,7 @@ private Entry getNextEntry(bool bundling)(Config config, ref string[] args, if (res.arg) { args.popFront; - return Entry(Argument(usedName, res, [value])); + return Entry(Argument(createParam(usedName, [value]), res)); } } } @@ -233,7 +236,7 @@ private Entry getNextEntry(bool bundling)(Config config, ref string[] args, { args.popFront; auto values = consumeValuesFromCLI(args, res.arg.info.minValuesCount.get, res.arg.info.maxValuesCount.get, isArgumentValue); - return Entry(Argument(arg0, res, values)); + return Entry(Argument(createParam(arg0, values), res)); } } @@ -249,7 +252,7 @@ private Entry getNextEntry(bool bundling)(Config config, ref string[] args, { auto value = arg0[2..$]; args.popFront; - return Entry(Argument(arg0[0..2], res, [value])); + return Entry(Argument(createParam(arg0[0..2], [value]), res)); } } } @@ -271,7 +274,7 @@ private Entry getNextEntry(bool bundling)(Config config, ref string[] args, args[0] = rest; // Due to bundling argument has no value - return Entry(Argument(arg0[0..2], res, [])); + return Entry(Argument(createParam(arg0[0..2], []), res)); } } } @@ -283,7 +286,7 @@ private Entry getNextEntry(bool bundling)(Config config, ref string[] args, if(res.arg && res.arg.info.required) { auto values = consumeValuesFromCLI(args, res.arg.info.minValuesCount.get, res.arg.info.maxValuesCount.get, isArgumentValue); - return Entry(Argument(res.arg.info.placeholder, res, values)); + return Entry(Argument(createParam(res.arg.info.placeholder, values), res)); } // Is it sub command? @@ -298,7 +301,7 @@ private Entry getNextEntry(bool bundling)(Config config, ref string[] args, if(res.arg) { auto values = consumeValuesFromCLI(args, res.arg.info.minValuesCount.get, res.arg.info.maxValuesCount.get, isArgumentValue); - return Entry(Argument(res.arg.info.placeholder, res, values)); + return Entry(Argument(createParam(res.arg.info.placeholder, values), res)); } // Check for positional argument in sub commands @@ -306,7 +309,7 @@ private Entry getNextEntry(bool bundling)(Config config, ref string[] args, if(res.arg) { auto values = consumeValuesFromCLI(args, res.arg.info.minValuesCount.get, res.arg.info.maxValuesCount.get, isArgumentValue); - return Entry(Argument(res.arg.info.placeholder, res, values)); + return Entry(Argument(createParam(res.arg.info.placeholder, values), res)); } } @@ -314,7 +317,7 @@ private Entry getNextEntry(bool bundling)(Config config, ref string[] args, return Entry(Unknown(arg0)); } -private Entry getNextPositionalArgument(ref string[] args, +private Entry getNextPositionalArgument(const ref Config config, ref string[] args, FindResult delegate(bool) findPositionalArg) { import std.range : popFront; @@ -334,7 +337,7 @@ private Entry getNextPositionalArgument(ref string[] args, if(res.arg) { auto values = consumeValuesFromCLI(args, res.arg.info.minValuesCount.get, res.arg.info.maxValuesCount.get, _ => true); - return Entry(Argument(res.arg.info.placeholder, res, values)); + return Entry(Argument(RawParam(&config, res.arg.info.placeholder, values), res)); } args.popFront; @@ -343,9 +346,10 @@ private Entry getNextPositionalArgument(ref string[] args, unittest { - auto test(string[] args) { return getNextEntry!false(Config.init, args, null, null, null); } + Config config; + auto args = [""]; - assert(test([""]) == Entry(Unknown(""))); + assert(getNextEntry!false(config, args, null, null, null) == Entry(Unknown(""))); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -539,7 +543,7 @@ private struct Parser if(args.length > 1) { auto res = (forcePositionalOnly ? - getNextPositionalArgument(args, &findPositionalArgument) : + getNextPositionalArgument(config, args, &findPositionalArgument) : getNextEntry!bundling( config, args, &findPositionalArgument, @@ -563,7 +567,7 @@ private struct Parser else { auto res = (forcePositionalOnly ? - getNextPositionalArgument(args, &findPositionalArgument) : + getNextPositionalArgument(config, args, &findPositionalArgument) : getNextEntry!bundling( config, args, &findPositionalArgument,