Skip to content

Commit b482fea

Browse files
Support main callback with ref parameter (#177)
1 parent ee69ce3 commit b482fea

File tree

2 files changed

+66
-13
lines changed

2 files changed

+66
-13
lines changed

source/argparse/api/cli.d

Lines changed: 54 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,8 @@ template CLI(Config config, COMMAND)
121121
return res;
122122
}
123123

124-
static int parseArgs(alias newMain)(string[] args, COMMAND initialValue = COMMAND.init)
125-
if(__traits(compiles, { newMain(COMMAND.init); }))
124+
static int parseArgs(alias newMain)(string[] args, auto ref COMMAND initialValue = COMMAND.init)
125+
if(__traits(compiles, { newMain(initialValue); }))
126126
{
127127
alias value = initialValue;
128128

@@ -139,8 +139,8 @@ template CLI(Config config, COMMAND)
139139
}
140140
}
141141

142-
static int parseArgs(alias newMain)(string[] args, COMMAND initialValue = COMMAND.init)
143-
if(__traits(compiles, { newMain(COMMAND.init, string[].init); }))
142+
static int parseArgs(alias newMain)(string[] args, auto ref COMMAND initialValue = COMMAND.init)
143+
if(__traits(compiles, { newMain(initialValue, string[].init); }))
144144
{
145145
alias value = initialValue;
146146

@@ -205,11 +205,55 @@ alias CLI(COMMANDS...) = CLI!(Config.init, COMMANDS);
205205

206206
unittest
207207
{
208-
struct Args {}
208+
static struct Args {}
209209

210-
mixin CLI!({
211-
Config cfg;
212-
cfg.errorHandler = (string s) {};
213-
return cfg;
214-
}(), Args).main!((_){});
210+
Args initValue;
211+
212+
enum Config cfg = { errorHandler: (string s) {} };
213+
214+
assert(CLI!(cfg, Args).parseArgs!((_ ){})([]) == 0);
215+
assert(CLI!(cfg, Args).parseArgs!((_, string[] unknown){})([]) == 0);
216+
assert(CLI!(cfg, Args).parseArgs!((_ ){ return 123; })([]) == 123);
217+
assert(CLI!(cfg, Args).parseArgs!((_, string[] unknown){ return 123; })([]) == 123);
218+
219+
assert(CLI!(cfg, Args).parseArgs!((_ ){})([], initValue) == 0);
220+
assert(CLI!(cfg, Args).parseArgs!((_, string[] unknown){})([], initValue) == 0);
221+
assert(CLI!(cfg, Args).parseArgs!((_ ){ return 123; })([], initValue) == 123);
222+
assert(CLI!(cfg, Args).parseArgs!((_, string[] unknown){ return 123; })([], initValue) == 123);
223+
224+
// Ensure that CLI.main is compilable
225+
{ mixin CLI!(cfg, Args).main!((_ ){}); }
226+
{ mixin CLI!(cfg, Args).main!((_, string[] unknown){}); }
227+
{ mixin CLI!(cfg, Args).main!((_ ){ return 123; }); }
228+
{ mixin CLI!(cfg, Args).main!((_, string[] unknown){ return 123; }); }
229+
}
230+
231+
// Ensure that CLI works with non-copyable structs
232+
unittest
233+
{
234+
static struct Args {
235+
@disable this(ref Args);
236+
this(int) {}
237+
}
238+
239+
//Args initValue;
240+
auto initValue = Args(0);
241+
242+
enum Config cfg = { errorHandler: (string s) {} };
243+
244+
assert(CLI!(cfg, Args).parseArgs!((ref _ ){})([]) == 0);
245+
assert(CLI!(cfg, Args).parseArgs!((ref _, string[] unknown){})([]) == 0);
246+
assert(CLI!(cfg, Args).parseArgs!((ref _ ){ return 123; })([]) == 123);
247+
assert(CLI!(cfg, Args).parseArgs!((ref _, string[] unknown){ return 123; })([]) == 123);
248+
249+
assert(CLI!(cfg, Args).parseArgs!((ref _ ){})([], initValue) == 0);
250+
assert(CLI!(cfg, Args).parseArgs!((ref _, string[] unknown){})([], initValue) == 0);
251+
assert(CLI!(cfg, Args).parseArgs!((ref _ ){ return 123; })([], initValue) == 123);
252+
assert(CLI!(cfg, Args).parseArgs!((ref _, string[] unknown){ return 123; })([], initValue) == 123);
253+
254+
// Ensure that CLI.main is compilable
255+
{ mixin CLI!(cfg, Args).main!((ref _ ){}); }
256+
{ mixin CLI!(cfg, Args).main!((ref _, string[] unknown){}); }
257+
{ mixin CLI!(cfg, Args).main!((ref _ ){ return 123; }); }
258+
{ mixin CLI!(cfg, Args).main!((ref _, string[] unknown){ return 123; }); }
215259
}

source/argparse/internal/command.d

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -206,20 +206,29 @@ private enum hasNoMembersWithUDA(COMMAND) = getSymbolsByUDA!(COMMAND, ArgumentUD
206206
getSymbolsByUDA!(COMMAND, NamedArgument ).length == 0;
207207

208208
private enum isOpFunction(alias mem) = is(typeof(mem) == function) && __traits(identifier, mem).length > 2 && __traits(identifier, mem)[0..2] == "op";
209+
private enum isConstructor(alias mem) = is(typeof(mem) == function) && __traits(identifier, mem) == "__ctor";
209210

210211
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
211212

212213
private template iterateArguments(TYPE)
213214
{
214215
import std.meta: Filter, anySatisfy;
215216

217+
// In case if TYPE is no members with ArgumentUDA, we filter out "op..." functions and ctors
218+
// Otherwise, we pick members with ArgumentUDA
219+
static if(hasNoMembersWithUDA!TYPE)
220+
enum isValidArgumentMember(alias mem) = !isOpFunction!mem && !isConstructor!mem;
221+
else
222+
enum isValidArgumentMember(alias mem) = anySatisfy!(isArgumentUDA, __traits(getAttributes, mem));
223+
216224
template filter(string sym)
217225
{
218226
alias mem = __traits(getMember, TYPE, sym);
219227

220-
enum filter = !is(mem) && !isSubCommand!(typeof(mem)) && (
221-
anySatisfy!(isArgumentUDA, __traits(getAttributes, mem)) ||
222-
hasNoMembersWithUDA!TYPE && !isOpFunction!mem);
228+
enum filter =
229+
!is(mem) && // not a type -- and --
230+
!isSubCommand!(typeof(mem)) && // not subcommand -- and --
231+
isValidArgumentMember!mem; // is valid argument member
223232
}
224233

225234
alias iterateArguments = Filter!(filter, __traits(allMembers, TYPE));

0 commit comments

Comments
 (0)