Skip to content

Commit 690136e

Browse files
authored
Add multi-instance option support (#678)
* Add multi-instance option support Fixes #357 * Fix Appveyor build It seems Appveyor's C# compiler is old enough that it doesn't know how to do tuple deconstruction, so we'll avoid doing that. It makes TokenPartitioner uglier, but no getting around that if we're dealing with an old C# compiler.
1 parent e6c670c commit 690136e

22 files changed

+598
-132
lines changed

src/CommandLine/Core/InstanceBuilder.cs

+25-1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,30 @@ public static ParserResult<T> Build<T>(
2424
bool autoVersion,
2525
IEnumerable<ErrorType> nonFatalErrors)
2626
{
27+
return Build(
28+
factory,
29+
tokenizer,
30+
arguments,
31+
nameComparer,
32+
ignoreValueCase,
33+
parsingCulture,
34+
autoHelp,
35+
autoVersion,
36+
false,
37+
nonFatalErrors);
38+
}
39+
40+
public static ParserResult<T> Build<T>(
41+
Maybe<Func<T>> factory,
42+
Func<IEnumerable<string>, IEnumerable<OptionSpecification>, Result<IEnumerable<Token>, Error>> tokenizer,
43+
IEnumerable<string> arguments,
44+
StringComparer nameComparer,
45+
bool ignoreValueCase,
46+
CultureInfo parsingCulture,
47+
bool autoHelp,
48+
bool autoVersion,
49+
bool allowMultiInstance,
50+
IEnumerable<ErrorType> nonFatalErrors) {
2751
var typeInfo = factory.MapValueOrDefault(f => f().GetType(), typeof(T));
2852

2953
var specProps = typeInfo.GetSpecifications(pi => SpecificationProperty.Create(
@@ -95,7 +119,7 @@ public static ParserResult<T> Build<T>(
95119
instance = BuildImmutable(typeInfo, factory, specProps, specPropsWithValue, setPropertyErrors);
96120
}
97121

98-
var validationErrors = specPropsWithValue.Validate(SpecificationPropertyRules.Lookup(tokens));
122+
var validationErrors = specPropsWithValue.Validate(SpecificationPropertyRules.Lookup(tokens, allowMultiInstance));
99123

100124
var allErrors =
101125
tokenizerResult.SuccessMessages()

src/CommandLine/Core/InstanceChooser.cs

+29-2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,31 @@ public static ParserResult<object> Choose(
2222
bool autoHelp,
2323
bool autoVersion,
2424
IEnumerable<ErrorType> nonFatalErrors)
25+
{
26+
return Choose(
27+
tokenizer,
28+
types,
29+
arguments,
30+
nameComparer,
31+
ignoreValueCase,
32+
parsingCulture,
33+
autoHelp,
34+
autoVersion,
35+
false,
36+
nonFatalErrors);
37+
}
38+
39+
public static ParserResult<object> Choose(
40+
Func<IEnumerable<string>, IEnumerable<OptionSpecification>, Result<IEnumerable<Token>, Error>> tokenizer,
41+
IEnumerable<Type> types,
42+
IEnumerable<string> arguments,
43+
StringComparer nameComparer,
44+
bool ignoreValueCase,
45+
CultureInfo parsingCulture,
46+
bool autoHelp,
47+
bool autoVersion,
48+
bool allowMultiInstance,
49+
IEnumerable<ErrorType> nonFatalErrors)
2550
{
2651
var verbs = Verb.SelectFromTypes(types);
2752
var defaultVerbs = verbs.Where(t => t.Item1.IsDefault);
@@ -46,7 +71,7 @@ bool preprocCompare(string command) =>
4671
arguments.Skip(1).FirstOrDefault() ?? string.Empty, nameComparer))
4772
: (autoVersion && preprocCompare("version"))
4873
? MakeNotParsed(types, new VersionRequestedError())
49-
: MatchVerb(tokenizer, verbs, defaultVerb, arguments, nameComparer, ignoreValueCase, parsingCulture, autoHelp, autoVersion, nonFatalErrors);
74+
: MatchVerb(tokenizer, verbs, defaultVerb, arguments, nameComparer, ignoreValueCase, parsingCulture, autoHelp, autoVersion, allowMultiInstance, nonFatalErrors);
5075
}
5176

5277
return arguments.Any()
@@ -92,6 +117,7 @@ private static ParserResult<object> MatchVerb(
92117
CultureInfo parsingCulture,
93118
bool autoHelp,
94119
bool autoVersion,
120+
bool allowMultiInstance,
95121
IEnumerable<ErrorType> nonFatalErrors)
96122
{
97123
string firstArg = arguments.First();
@@ -114,7 +140,8 @@ private static ParserResult<object> MatchVerb(
114140
ignoreValueCase,
115141
parsingCulture,
116142
autoHelp,
117-
autoVersion,
143+
autoVersion,
144+
allowMultiInstance,
118145
nonFatalErrors);
119146
}
120147

src/CommandLine/Core/OptionMapper.cs

+19-14
Original file line numberDiff line numberDiff line change
@@ -22,26 +22,31 @@ public static Result<
2222
.Select(
2323
pt =>
2424
{
25-
var matched = options.FirstOrDefault(s =>
25+
var matched = options.Where(s =>
2626
s.Key.MatchName(((OptionSpecification)pt.Specification).ShortName, ((OptionSpecification)pt.Specification).LongName, comparer)).ToMaybe();
27-
return matched.IsJust()
28-
? (
29-
from sequence in matched
30-
from converted in
31-
converter(
32-
sequence.Value,
33-
pt.Property.PropertyType,
34-
pt.Specification.TargetType != TargetType.Sequence)
35-
select Tuple.Create(
36-
pt.WithValue(Maybe.Just(converted)), Maybe.Nothing<Error>())
37-
)
27+
if (matched.IsJust())
28+
{
29+
var matches = matched.GetValueOrDefault(Enumerable.Empty<KeyValuePair<string, IEnumerable<string>>>());
30+
var values = new HashSet<string>();
31+
foreach (var kvp in matches)
32+
{
33+
foreach (var value in kvp.Value)
34+
{
35+
values.Add(value);
36+
}
37+
}
38+
39+
return converter(values, pt.Property.PropertyType, pt.Specification.TargetType != TargetType.Sequence)
40+
.Select(value => Tuple.Create(pt.WithValue(Maybe.Just(value)), Maybe.Nothing<Error>()))
3841
.GetValueOrDefault(
3942
Tuple.Create<SpecificationProperty, Maybe<Error>>(
4043
pt,
4144
Maybe.Just<Error>(
4245
new BadFormatConversionError(
43-
((OptionSpecification)pt.Specification).FromOptionSpecification()))))
44-
: Tuple.Create(pt, Maybe.Nothing<Error>());
46+
((OptionSpecification)pt.Specification).FromOptionSpecification()))));
47+
}
48+
49+
return Tuple.Create(pt, Maybe.Nothing<Error>());
4550
}
4651
).Memoize();
4752
return Result.Succeed(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright 2005-2015 Giacomo Stelluti Scala & Contributors. All rights reserved. See License.md in the project root for license information.
2+
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
using CSharpx;
7+
8+
namespace CommandLine.Core
9+
{
10+
static class PartitionExtensions
11+
{
12+
public static Tuple<IEnumerable<T>,IEnumerable<T>> PartitionByPredicate<T>(
13+
this IEnumerable<T> items,
14+
Func<T, bool> pred)
15+
{
16+
List<T> yes = new List<T>();
17+
List<T> no = new List<T>();
18+
foreach (T item in items) {
19+
List<T> list = pred(item) ? yes : no;
20+
list.Add(item);
21+
}
22+
return Tuple.Create<IEnumerable<T>,IEnumerable<T>>(yes, no);
23+
}
24+
}
25+
}

src/CommandLine/Core/Scalar.cs

-27
This file was deleted.

src/CommandLine/Core/Sequence.cs

-43
This file was deleted.

src/CommandLine/Core/SpecificationPropertyRules.cs

+15-2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,14 @@ static class SpecificationPropertyRules
1313
public static IEnumerable<Func<IEnumerable<SpecificationProperty>, IEnumerable<Error>>>
1414
Lookup(
1515
IEnumerable<Token> tokens)
16+
{
17+
return Lookup(tokens, false);
18+
}
19+
20+
public static IEnumerable<Func<IEnumerable<SpecificationProperty>, IEnumerable<Error>>>
21+
Lookup(
22+
IEnumerable<Token> tokens,
23+
bool allowMultiInstance)
1624
{
1725
return new List<Func<IEnumerable<SpecificationProperty>, IEnumerable<Error>>>
1826
{
@@ -21,7 +29,7 @@ public static IEnumerable<Func<IEnumerable<SpecificationProperty>, IEnumerable<E
2129
EnforceMutuallyExclusiveSetAndGroupAreNotUsedTogether(),
2230
EnforceRequired(),
2331
EnforceRange(),
24-
EnforceSingle(tokens)
32+
EnforceSingle(tokens, allowMultiInstance)
2533
};
2634
}
2735

@@ -173,10 +181,15 @@ from s in options
173181
};
174182
}
175183

176-
private static Func<IEnumerable<SpecificationProperty>, IEnumerable<Error>> EnforceSingle(IEnumerable<Token> tokens)
184+
private static Func<IEnumerable<SpecificationProperty>, IEnumerable<Error>> EnforceSingle(IEnumerable<Token> tokens, bool allowMultiInstance)
177185
{
178186
return specProps =>
179187
{
188+
if (allowMultiInstance)
189+
{
190+
return Enumerable.Empty<Error>();
191+
}
192+
180193
var specs = from sp in specProps
181194
where sp.Specification.IsOption()
182195
where sp.Value.IsJust()

src/CommandLine/Core/Switch.cs

-21
This file was deleted.

0 commit comments

Comments
 (0)