From f195278d951fb79aede5a07e7e387237bbc19488 Mon Sep 17 00:00:00 2001 From: Andrey Zherikov Date: Wed, 23 Oct 2024 13:40:17 -0400 Subject: [PATCH] Support main callback with ref parameter --- source/argparse/api/cli.d | 67 +++++++++++++++++++++++++++--- source/argparse/internal/command.d | 3 +- 2 files changed, 64 insertions(+), 6 deletions(-) diff --git a/source/argparse/api/cli.d b/source/argparse/api/cli.d index 21fa580..33e880d 100644 --- a/source/argparse/api/cli.d +++ b/source/argparse/api/cli.d @@ -62,8 +62,8 @@ template CLI(Config config, COMMAND) return res; } - static int parseArgs(alias newMain)(string[] args, COMMAND initialValue = COMMAND.init) - if(__traits(compiles, { newMain(COMMAND.init); })) + static int parseArgs(alias newMain)(string[] args, auto ref COMMAND initialValue = COMMAND.init) + if(__traits(compiles, { newMain(initialValue); })) { alias value = initialValue; @@ -80,8 +80,8 @@ template CLI(Config config, COMMAND) } } - static int parseArgs(alias newMain)(string[] args, COMMAND initialValue = COMMAND.init) - if(__traits(compiles, { newMain(COMMAND.init, string[].init); })) + static int parseArgs(alias newMain)(string[] args, auto ref COMMAND initialValue = COMMAND.init) + if(__traits(compiles, { newMain(initialValue, string[].init); })) { alias value = initialValue; @@ -153,4 +153,61 @@ template CLI(Config config, COMMAND) } } -alias CLI(COMMANDS...) = CLI!(Config.init, COMMANDS); \ No newline at end of file +alias CLI(COMMANDS...) = CLI!(Config.init, COMMANDS); + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +unittest +{ + static struct Args {} + + Args initValue; + + enum cfg = Config.init; + + assert(CLI!(cfg, Args).parseArgs!((_ ){})([]) == 0); + assert(CLI!(cfg, Args).parseArgs!((_, string[] unknown){})([]) == 0); + assert(CLI!(cfg, Args).parseArgs!((_ ){ return 123; })([]) == 123); + assert(CLI!(cfg, Args).parseArgs!((_, string[] unknown){ return 123; })([]) == 123); + + assert(CLI!(cfg, Args).parseArgs!((_ ){})([], initValue) == 0); + assert(CLI!(cfg, Args).parseArgs!((_, string[] unknown){})([], initValue) == 0); + assert(CLI!(cfg, Args).parseArgs!((_ ){ return 123; })([], initValue) == 123); + assert(CLI!(cfg, Args).parseArgs!((_, string[] unknown){ return 123; })([], initValue) == 123); + + // Ensure that CLI.main is compilable + { mixin CLI!(cfg, Args).main!((_ ){}); } + { mixin CLI!(cfg, Args).main!((_, string[] unknown){}); } + { mixin CLI!(cfg, Args).main!((_ ){ return 123; }); } + { mixin CLI!(cfg, Args).main!((_, string[] unknown){ return 123; }); } +} + +// Ensure that CLI works with non-copyable structs +unittest +{ + static struct Args { + @disable this(ref Args); + this(int) {} + } + + //Args initValue; + auto initValue = Args(0); + + enum cfg = Config.init; + + assert(CLI!(cfg, Args).parseArgs!((ref _ ){})([]) == 0); + assert(CLI!(cfg, Args).parseArgs!((ref _, string[] unknown){})([]) == 0); + assert(CLI!(cfg, Args).parseArgs!((ref _ ){ return 123; })([]) == 123); + assert(CLI!(cfg, Args).parseArgs!((ref _, string[] unknown){ return 123; })([]) == 123); + + assert(CLI!(cfg, Args).parseArgs!((ref _ ){})([], initValue) == 0); + assert(CLI!(cfg, Args).parseArgs!((ref _, string[] unknown){})([], initValue) == 0); + assert(CLI!(cfg, Args).parseArgs!((ref _ ){ return 123; })([], initValue) == 123); + assert(CLI!(cfg, Args).parseArgs!((ref _, string[] unknown){ return 123; })([], initValue) == 123); + + // Ensure that CLI.main is compilable + { mixin CLI!(cfg, Args).main!((ref _ ){}); } + { mixin CLI!(cfg, Args).main!((ref _, string[] unknown){}); } + { mixin CLI!(cfg, Args).main!((ref _ ){ return 123; }); } + { mixin CLI!(cfg, Args).main!((ref _, string[] unknown){ return 123; }); } +} diff --git a/source/argparse/internal/command.d b/source/argparse/internal/command.d index 6f47f37..a141895 100644 --- a/source/argparse/internal/command.d +++ b/source/argparse/internal/command.d @@ -107,6 +107,7 @@ private enum hasNoMembersWithUDA(COMMAND) = getSymbolsByUDA!(COMMAND, ArgumentUD getSymbolsByUDA!(COMMAND, SubCommands ).length == 0; private enum isOpFunction(alias mem) = is(typeof(mem) == function) && __traits(identifier, mem).length > 2 && __traits(identifier, mem)[0..2] == "op"; +private enum isConstructor(alias mem) = is(typeof(mem) == function) && __traits(identifier, mem) == "__ctor"; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -122,7 +123,7 @@ private template iterateArguments(TYPE) enum filter = !is(mem) && ( hasUDA!(mem, ArgumentUDA) || hasUDA!(mem, NamedArgument) || - hasNoMembersWithUDA!TYPE && !isOpFunction!mem && !isSumType!(typeof(mem))); + hasNoMembersWithUDA!TYPE && !isOpFunction!mem && !isConstructor!mem && !isSumType!(typeof(mem))); } alias iterateArguments = Filter!(filter, __traits(allMembers, TYPE));