Optional Variable Argument Count?
Closed this issue · 11 comments
Hello Dan,
I've just tried your library and I like it, simple and elegant.
I have a special situation:
I have some executable with various options. I save a shortcut on desktop pointing to the executable and add the desired named arguments at the end of the target field in the shortcut definition. Now if I double click the shortcut it will properly execute with the specified named arguments.
I want to provide the user's the ability to drag and drop files on the shortcut. It works with a positional argument, but the NumArgs must be at least 1, so it's not 'optional'. I tried setting NumArgs=0, but understandably it throws an exception. What would you suggest to work around this?
[PositionalArgument(1, Constraint = NumArgsConstraint.AtLeast, NumArgs = 1)]
public List<string> Files { get; set; }
Thanks!
TB
Good idea, I can see how this feature would be useful. I think I'll allow NumArgs=0 but only for the last positional argument since otherwise it's difficult to figure out which arguments go where. I'll look into relaxing the restriction for named arguments too since they're delimited by the next argument.
On a closer look, it appears that only the last positional argument allows varargs anyway, so this should be pretty easy.
Fixed in f5134a9
I also uploaded version 1.4.6 to NuGet. Set NumArgs=0 and try it out! If you're still having issues feel free to reopen.
Woah that was fast! Thanks a lot. I will try it shortly and let you know how it goes.
Hello Dan,
I've made a lot of tests, generally it works great, but I found something which seem broken.
If I have this Options class:
[NamedArgument('t', Description = "Tracer verbosity.")]
public TraceLevel TraceLevel { get; set; }
[PositionalArgument(0, Constraint = NumArgsConstraint.AtLeast, NumArgs = 0)]
public List<string> Files { get; set; }
And run it like this it works pefectly:
some.exe -t error "c:\file1.txt" "c:\file2.txt"
If I run it like this with volontary error on named parameter:
some.exe -tX error "c:\file1.txt" "c:\file2.txt"
It will pass Parse, TryParse and StrictParse without throwing an exception!
Thanks for looking into this.
Alex
Hmm, I'll have to check out why -tX
doesn't throw an error since X
isn't a valid TraceLevel enum.
No. If I remove the last positional argument and run
some.exe -tX error
It will correctly pop the exception.
That's really odd. I just wrote a test case for it based on your example above and it throws a ParseException. Did I forget something in the test case? Does this test case throw an exception for you?
private class TraceOptions
{
[NamedArgument('t')]
public System.Diagnostics.TraceLevel TraceLevel { get; set; }
[PositionalArgument(0, Constraint = NumArgsConstraint.AtLeast, NumArgs = 0)]
public List<string> Files { get; set; }
}
[TestMethod]
public void Parse_WithInvalidEnumValue_ThrowsException()
{
var obj = CliParser.Parse<TraceOptions>("-tX error c:\\file.txt c:\\file2.txt".Split());
Console.WriteLine(obj.TraceLevel);
}
Hello Dan,
I'm sorry I made so many tests and wrote this afterwards. The problem occurs when the enum is a StaticEnumeration. I wanted to make TraceLevel type safe and not rely on integers, so as suggested in clipr's documentation I created a 'type-safe' enum. Try this version of the test:
private class TraceOptions
{
[NamedArgument('t')]
public TraceLevelOption TraceLevel { get; set; }
[PositionalArgument(0, Constraint = NumArgsConstraint.AtLeast, NumArgs = 0)]
public List<string> Files { get; set; }
}
[TestMethod]
public void Parse_WithInvalidEnumValue_ThrowsException()
{
var obj = CliParser.Parse<TraceOptions>("-tX error c:\\file.txt c:\\file2.txt".Split());
Console.WriteLine(obj.TraceLevel);
}
[StaticEnumeration]
public class TraceLevelOption
{
public static readonly TraceLevelOption Off = new TraceLevelOption(TraceLevel.Off);
public static readonly TraceLevelOption Error = new TraceLevelOption(TraceLevel.Error);
public static readonly TraceLevelOption Warning = new TraceLevelOption(TraceLevel.Warning);
public static readonly TraceLevelOption Info = new TraceLevelOption(TraceLevel.Info);
public static readonly TraceLevelOption Verbose = new TraceLevelOption(TraceLevel.Verbose);
private TraceLevel value;
private TraceLevelOption(TraceLevel value)
{
this.value = value;
}
}
Mmm yeah that was my mistake. Apparently TypeConverters are supposed to throw an exception when parsing fails and all I did was return null. I checked in the fix but haven't built a new NuGet package yet.
I also added your test to my Unit Test project to make sure this doesn't break in the future :)
Thanks for the quickfix!