Skip to content

Commit 1c13768

Browse files
committed
Implemented handling of unkeyed/unprefixed options, aka command line arguments
1 parent c6d6e1c commit 1c13768

20 files changed

+540
-219
lines changed

J4JCommandLine.Tests/RootLevelTests.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ public void Simple_property_array(
8585
if (required)
8686
option.Required();
8787

88+
option.ArgumentCount( 0, 2 );
89+
8890
var cmdLineArgs = new List<string> { $"-{key}" };
8991
cmdLineArgs.AddRange(args);
9092

@@ -119,6 +121,8 @@ public void Simple_property_list(
119121
if (required)
120122
option.Required();
121123

124+
option.ArgumentCount(0, 2);
125+
122126
var cmdLineArgs = new List<string> { $"-{key}" };
123127
cmdLineArgs.AddRange(args);
124128

@@ -134,7 +138,7 @@ public void Simple_property_list(
134138
[Theory]
135139
[InlineData(new string[] { "32" }, 0, Int32.MaxValue, MappingResults.Success)]
136140
[InlineData(new string[] { "32" }, 2, Int32.MaxValue, MappingResults.TooFewParameters)]
137-
[InlineData(new string[] { "32" }, 0, 0, MappingResults.TooManyParameters)]
141+
[InlineData(new string[] { "32" }, 0, 0, MappingResults.Success)]
138142
public void Num_parameters_list(
139143
string[] rawArgs,
140144
int minArgs,
@@ -146,7 +150,7 @@ public void Num_parameters_list(
146150
target.Should().NotBeNull();
147151

148152
var option = target!.Bind(x => x.IntList, "x");
149-
option.Should().BeAssignableTo<TargetedOption>();
153+
option.Should().BeAssignableTo<MappableOption>();
150154

151155
option.ArgumentCount(minArgs, maxArgs);
152156

@@ -174,7 +178,7 @@ public void Num_parameters_list(
174178
[Theory]
175179
[InlineData(new string[] { "32" }, 0, Int32.MaxValue, MappingResults.Success)]
176180
[InlineData(new string[] { "32" }, 2, Int32.MaxValue, MappingResults.TooFewParameters)]
177-
[InlineData(new string[] { "32" }, 0, 0, MappingResults.TooManyParameters)]
181+
[InlineData(new string[] { "32" }, 0, 0, MappingResults.Success)]
178182
public void Num_parameters_array(
179183
string[] rawArgs,
180184
int minArgs,
@@ -186,7 +190,7 @@ public void Num_parameters_array(
186190
target.Should().NotBeNull();
187191

188192
var option = target!.Bind(x => x.IntArray, "x");
189-
option.Should().BeAssignableTo<TargetedOption>();
193+
option.Should().BeAssignableTo<MappableOption>();
190194

191195
option.ArgumentCount(minArgs, maxArgs);
192196

J4JCommandLine.Tests/StaticLevelTests.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ public void Simple_property_array(
8484
if( required )
8585
option.Required();
8686

87+
option.ArgumentCount( desired.Count );
88+
8789
var cmdLineArgs = new List<string> { $"-{key}" };
8890
cmdLineArgs.AddRange( args );
8991

@@ -118,6 +120,8 @@ public void Simple_property_list(
118120
if( required )
119121
option.Required();
120122

123+
option.ArgumentCount(desired.Count);
124+
121125
var cmdLineArgs = new List<string> { $"-{key}" };
122126
cmdLineArgs.AddRange( args );
123127

@@ -133,7 +137,7 @@ public void Simple_property_list(
133137
[ Theory ]
134138
[ InlineData( new string[] { "32" }, 0, Int32.MaxValue, MappingResults.Success ) ]
135139
[ InlineData( new string[] { "32" }, 2, Int32.MaxValue, MappingResults.TooFewParameters ) ]
136-
[ InlineData( new string[] { "32" }, 0, 0, MappingResults.TooManyParameters ) ]
140+
[ InlineData( new string[] { "32" }, 0, 0, MappingResults.Success ) ]
137141
public void Num_parameters_list(
138142
string[] rawArgs,
139143
int minArgs,
@@ -145,7 +149,7 @@ public void Num_parameters_list(
145149
target.Should().NotBeNull();
146150

147151
var option = target!.Bind( x => RootProperties.IntList, "x" );
148-
option.Should().BeAssignableTo<TargetedOption>();
152+
option.Should().BeAssignableTo<MappableOption>();
149153

150154
option.ArgumentCount( minArgs, maxArgs );
151155

@@ -173,7 +177,7 @@ public void Num_parameters_list(
173177
[ Theory ]
174178
[ InlineData( new string[] { "32" }, 0, Int32.MaxValue, MappingResults.Success ) ]
175179
[ InlineData( new string[] { "32" }, 2, Int32.MaxValue, MappingResults.TooFewParameters ) ]
176-
[ InlineData( new string[] { "32" }, 0, 0, MappingResults.TooManyParameters ) ]
180+
[ InlineData( new string[] { "32" }, 0, 0, MappingResults.Success ) ]
177181
public void Num_parameters_array(
178182
string[] rawArgs,
179183
int minArgs,
@@ -185,7 +189,7 @@ public void Num_parameters_array(
185189
target.Should().NotBeNull();
186190

187191
var option = target!.Bind( x => RootProperties.IntArray, "x" );
188-
option.Should().BeAssignableTo<TargetedOption>();
192+
option.Should().BeAssignableTo<MappableOption>();
189193

190194
option.ArgumentCount( minArgs, maxArgs );
191195

J4JCommandLine.Tests/SubLevelTests.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ public void Simple_property_array(
102102
if (required)
103103
option.Required();
104104

105+
option.ArgumentCount( desired.Count );
106+
105107
var cmdLineArgs = new List<string> { $"-{key}" };
106108
cmdLineArgs.AddRange(args);
107109

@@ -137,6 +139,8 @@ public void Simple_property_list(
137139
if (required)
138140
option.Required();
139141

142+
option.ArgumentCount( desired.Count );
143+
140144
var cmdLineArgs = new List<string> { $"-{key}" };
141145
cmdLineArgs.AddRange( args );
142146

@@ -195,7 +199,7 @@ public void Complex_properties_collection(
195199
[Theory]
196200
[InlineData(new string[]{ "32"}, 0, Int32.MaxValue, MappingResults.Success)]
197201
[InlineData(new string[] { "32" }, 2, Int32.MaxValue, MappingResults.TooFewParameters)]
198-
[InlineData(new string[] { "32" }, 0, 0, MappingResults.TooManyParameters)]
202+
[InlineData(new string[] { "32" }, 0, 0, MappingResults.Success)]
199203
public void Num_parameters_list(
200204
string[] rawArgs,
201205
int minArgs,
@@ -207,7 +211,7 @@ public void Num_parameters_list(
207211
target.Should().NotBeNull();
208212

209213
var option = target!.Bind( x => x.SimpleChildProperties.IntList, "x" );
210-
option.Should().BeAssignableTo<TargetedOption>();
214+
option.Should().BeAssignableTo<MappableOption>();
211215

212216
option.ArgumentCount( minArgs, maxArgs );
213217

@@ -235,7 +239,7 @@ public void Num_parameters_list(
235239
[Theory]
236240
[InlineData(new string[] { "32" }, 0, Int32.MaxValue, MappingResults.Success)]
237241
[InlineData(new string[] { "32" }, 2, Int32.MaxValue, MappingResults.TooFewParameters)]
238-
[InlineData(new string[] { "32" }, 0, 0, MappingResults.TooManyParameters)]
242+
[InlineData(new string[] { "32" }, 0, 0, MappingResults.Success)]
239243
public void Num_parameters_array(
240244
string[] rawArgs,
241245
int minArgs,
@@ -247,7 +251,7 @@ public void Num_parameters_array(
247251
target.Should().NotBeNull();
248252

249253
var option = target!.Bind( x => x.SimpleChildProperties.IntArray, "x" );
250-
option.Should().BeAssignableTo<TargetedOption>();
254+
option.Should().BeAssignableTo<MappableOption>();
251255

252256
option.ArgumentCount(minArgs, maxArgs);
253257

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,16 @@
44

55
namespace J4JSoftware.CommandLine
66
{
7-
public class TargetedOption : Option
7+
public class MappableOption : Option
88
{
99
private readonly ITextConverter _converter;
1010

11-
public TargetedOption(
11+
public MappableOption(
1212
OptionCollection options,
13-
ITargetableType targetableType
13+
ITargetableType targetableType,
14+
bool isKeyed
1415
)
15-
: base( OptionType.Mappable, targetableType, options )
16+
: base( isKeyed ? OptionType.Keyed : OptionType.Unkeyed, targetableType, options )
1617
{
1718
_converter = targetableType.Converter!;
1819
}
@@ -38,18 +39,21 @@ public override MappingResults Convert(
3839
break;
3940

4041
case Multiplicity.List:
41-
retVal = ConvertToList( bindingTarget, parseResult, out var listResult );
42+
retVal = ConvertToList( bindingTarget, parseResult, out var listResult);
4243
result = listResult;
44+
4345
break;
4446

4547
case Multiplicity.SimpleValue:
46-
retVal = ConvertToSimpleValue( bindingTarget, parseResult, out var singleResult );
48+
retVal = ConvertToSimpleValue( bindingTarget, parseResult, out var singleResult);
4749
result = singleResult;
50+
4851
break;
4952

5053
default:
5154
result = null;
5255
retVal = MappingResults.UnsupportedMultiplicity;
56+
5357
break;
5458
}
5559

@@ -71,10 +75,12 @@ public override MappingResults Convert(
7175

7276
// checks to see if the provided IParseResult contains an allowable number of parameters and, if so,
7377
// attempts to convert them to a simple value (i.e., a single object, an IValueType, a string)
74-
private MappingResults ConvertToSimpleValue( IBindingTarget bindingTarget, IParseResult parseResult,
78+
private MappingResults ConvertToSimpleValue(
79+
IBindingTarget bindingTarget,
80+
IParseResult parseResult,
7581
out object? result )
7682
{
77-
if( !ValidParameterCount(bindingTarget, parseResult, out var paramResult ) )
83+
if ( !ValidParameterCount(bindingTarget, parseResult, out var paramResult ) )
7884
{
7985
result = null;
8086
return paramResult;
@@ -98,7 +104,10 @@ private MappingResults ConvertToSimpleValue( IBindingTarget bindingTarget, IPars
98104

99105
// checks to see if the provided IParseResult contains an allowable number of parameters and, if so,
100106
// attempts to convert them to an array of simple values (i.e., a single object, an IValueType, a string)
101-
private MappingResults ConvertToArray( IBindingTarget bindingTarget, IParseResult parseResult, out Array? result )
107+
private MappingResults ConvertToArray(
108+
IBindingTarget bindingTarget,
109+
IParseResult parseResult,
110+
out Array? result )
102111
{
103112
if( !ValidParameterCount( bindingTarget, parseResult, out var paramResult ) )
104113
{
@@ -133,9 +142,12 @@ private MappingResults ConvertToArray( IBindingTarget bindingTarget, IParseResul
133142

134143
// checks to see if the provided IParseResult contains an allowable number of parameters and, if so,
135144
// attempts to convert them to a generic list of simple values (i.e., a single object, an IValueType, a string)
136-
private MappingResults ConvertToList( IBindingTarget bindingTarget, IParseResult parseResult, out IList? result )
145+
private MappingResults ConvertToList(
146+
IBindingTarget bindingTarget,
147+
IParseResult parseResult,
148+
out IList? result )
137149
{
138-
if (!ValidParameterCount( bindingTarget, parseResult, out var paramResult))
150+
if (!ValidParameterCount( bindingTarget, parseResult, out var paramResult ))
139151
{
140152
result = null;
141153
return paramResult;

J4JCommandLine/option/Option.cs

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,10 @@ OptionCollection options
4444
public bool IsRequired { get; internal set; }
4545

4646
// the minimum number of parameters to a command line option
47-
public int MinParameters { get; internal set; }
47+
public int MinParameters { get; internal set; } = 1;
4848

4949
// the maximum number of parameters to a command line option
50-
public int MaxParameters { get; internal set; } = int.MaxValue;
50+
public int MaxParameters { get; internal set; } = 1;
5151

5252
// the validator for the Option
5353
public IOptionValidator Validator { get; internal set; }
@@ -84,7 +84,6 @@ public bool Validate( IBindingTarget bindingTarget, string key, object value )
8484
return false;
8585
}
8686

87-
8887
// the method called to convert the parsing results for a particular command
8988
// line key to a option value. Return values other than MappingResults.Success
9089
// indicate one or more problems were encountered in the conversion and validation
@@ -97,7 +96,10 @@ public abstract MappingResults Convert(
9796

9897
// validates whether or not a valid number of parameters are included in the specified
9998
// IParseResult
100-
protected virtual bool ValidParameterCount(IBindingTarget bindingTarget, IParseResult parseResult, out MappingResults result)
99+
protected virtual bool ValidParameterCount(
100+
IBindingTarget bindingTarget,
101+
IParseResult parseResult,
102+
out MappingResults result )
101103
{
102104
if (parseResult.NumParameters < MinParameters)
103105
{
@@ -110,15 +112,20 @@ protected virtual bool ValidParameterCount(IBindingTarget bindingTarget, IParseR
110112
return false;
111113
}
112114

113-
if (parseResult.NumParameters > MaxParameters)
115+
if( parseResult.NumParameters > MaxParameters )
114116
{
115-
bindingTarget.AddError(
116-
parseResult.Key,
117-
$"Expected {MinParameters} parameters, got {parseResult.NumParameters}" );
118-
119-
result = MappingResults.TooManyParameters;
120-
121-
return false;
117+
if( parseResult.IsLastResult )
118+
parseResult.MoveExcessParameters(MaxParameters);
119+
else
120+
{
121+
bindingTarget.AddError(
122+
parseResult.Key,
123+
$"Expected no more than {MaxParameters} parameters, got {parseResult.NumParameters}");
124+
125+
result = MappingResults.TooManyParameters;
126+
127+
return false;
128+
}
122129
}
123130

124131
result = MappingResults.Success;

J4JCommandLine/option/OptionCollection.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,18 @@ public bool Add( Option option )
4545
return false;
4646
}
4747

48+
// only one unkeyed option is allowed because it corresponds to all of the
49+
// un-prefixed/unkeyed parameters
50+
if( option.OptionType == OptionType.Unkeyed )
51+
{
52+
var existing = _options.Where( opt => opt.OptionType == OptionType.Unkeyed )
53+
.Select( ( opt, idx ) => (opt,idx) )
54+
.FirstOrDefault();
55+
56+
if( existing.opt != null )
57+
_options.RemoveAt( existing.idx );
58+
}
59+
4860
_options.Add( option );
4961
_masterText.AddRange( TextUsageType.OptionKey, option.Keys );
5062

0 commit comments

Comments
 (0)