Skip to content

Commit 3dfd650

Browse files
committed
Merge changes from upstream/master.
2 parents cc801db + 6d20686 commit 3dfd650

14 files changed

+179
-45
lines changed

README.md

+33-3
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
[![Nuget](https://img.shields.io/nuget/dt/commandlineparser.svg)](http://nuget.org/packages/commandlineparser)
33
[![Nuget](https://img.shields.io/nuget/v/commandlineparser.svg)](http://nuget.org/packages/commandlineparser)
44
[![Nuget](https://img.shields.io/nuget/vpre/commandlineparser.svg)](http://nuget.org/packages/commandlineparser)
5+
[![Join the gitter chat!](https://badges.gitter.im/gsscoder/commandline.svg)](https://gitter.im/gsscoder/commandline?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
56

67
Command Line Parser Library 2.0.275.0 beta for CLR.
78
===
9+
810
The Command Line Parser Library offers CLR applications a clean and concise API for manipulating command line arguments and related tasks, such as defining switches, options and verb commands. It allows you to display a help screen with a high degree of customization and a simple way to report syntax errors to the end user.
911

1012
Everything that is boring and repetitive about parsing command line arguments is delegated to the library, letting developers concentrate on core logic. It's written in **C#** and doesn't depend on other packages.
@@ -82,8 +84,7 @@ class Options {
8284

8385
[Value(0, MetaName = "offset",
8486
HelpText = "File offset.")]
85-
public long? Offset { get; set;}
86-
}
87+
public long? Offset { get; set; }
8788
}
8889
```
8990
Consume them:
@@ -146,6 +147,35 @@ int Main(string[] args) {
146147
}
147148
```
148149

150+
**F#:**
151+
```fsharp
152+
open CommandLine
153+
154+
[<Verb("add", HelpText = "Add file contents to the index.")>]
155+
type AddOptions = {
156+
// normal options here
157+
}
158+
[<Verb("commit", HelpText = "Record changes to the repository.")>]
159+
type CommitOptions = {
160+
// normal options here
161+
}
162+
[<Verb("clone", HelpText = "Clone a repository into a new directory.")>]
163+
type CloneOptions = {
164+
// normal options here
165+
}
166+
167+
[<EntryPoint>]
168+
let main args =
169+
let result = Parser.Default.ParseArguments<AddOptions, CommitOptions, CloneOptions> args
170+
match result with
171+
| :? CommandLine.Parsed<obj> as command ->
172+
match command.Value with
173+
| :? AddOptions as opts -> RunAddAndReturnExitCode opts
174+
| :? CommitOptions as opts -> RunCommitAndReturnExitCode opts
175+
| :? CloneOptions as opts -> RunCloneAndReturnExitCode opts
176+
| :? CommandLine.NotParsed<obj> -> 1
177+
```
178+
149179
Acknowledgements:
150180
---
151181
[![Jet Brains ReSharper](/art/resharper-logo.png)](http://www.jetbrains.com/resharper/)
@@ -266,6 +296,6 @@ Latest Changes:
266296
Contact:
267297
---
268298
Giacomo Stelluti Scala
269-
- gsscoder AT gmail DOT com
299+
- gsscoder AT gmail DOT com (_use this for everything that is not available via GitHub features_)
270300
- [Blog](http://gsscoder.blogspot.it)
271301
- [Twitter](http://twitter.com/gsscoder)

src/CommandLine/Core/InstanceBuilder.cs

+4-3
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public static ParserResult<T> Build<T>(
2020
Func<IEnumerable<string>, IEnumerable<OptionSpecification>, Result<IEnumerable<Token>, Error>> tokenizer,
2121
IEnumerable<string> arguments,
2222
StringComparer nameComparer,
23+
bool ignoreValueCase,
2324
CultureInfo parsingCulture,
2425
IEnumerable<ErrorType> nonFatalErrors)
2526
{
@@ -60,14 +61,14 @@ public static ParserResult<T> Build<T>(
6061
OptionMapper.MapValues(
6162
(from pt in specProps where pt.Specification.IsOption() select pt),
6263
optionsPartition,
63-
(vals, type, isScalar) => TypeConverter.ChangeType(vals, type, isScalar, parsingCulture),
64+
(vals, type, isScalar) => TypeConverter.ChangeType(vals, type, isScalar, parsingCulture, ignoreValueCase),
6465
nameComparer);
6566

6667
var valueSpecPropsResult =
6768
ValueMapper.MapValues(
6869
(from pt in specProps where pt.Specification.IsValue() select pt),
6970
valuesPartition,
70-
(vals, type, isScalar) => TypeConverter.ChangeType(vals, type, isScalar, parsingCulture));
71+
(vals, type, isScalar) => TypeConverter.ChangeType(vals, type, isScalar, parsingCulture, ignoreValueCase));
7172

7273
var missingValueErrors = from token in errorsPartition
7374
select
@@ -110,7 +111,7 @@ join sp in specPropsWithValue on prms.Name.ToLower() equals sp.Property.Name.ToL
110111
};
111112

112113
var instance = typeInfo.IsMutable() ? buildMutable() : buildImmutable();
113-
114+
114115
var validationErrors = specPropsWithValue.Validate(SpecificationPropertyRules.Lookup(tokens));
115116

116117
var allErrors =

src/CommandLine/Core/InstanceChooser.cs

+1
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ private static ParserResult<object> MatchVerb(
6060
tokenizer,
6161
arguments.Skip(1),
6262
nameComparer,
63+
false,
6364
parsingCulture,
6465
nonFatalErrors)
6566
: MakeNotParsed(verbs.Select(v => v.Item2), new BadVerbSelectedError(arguments.First()));

src/CommandLine/Core/SpecificationPropertyRules.cs

+13-7
Original file line numberDiff line numberDiff line change
@@ -123,13 +123,19 @@ private static Func<IEnumerable<SpecificationProperty>, IEnumerable<Error>> Enfo
123123
where sp.Specification.IsOption()
124124
where sp.Value.IsJust()
125125
select (OptionSpecification)sp.Specification;
126-
var options = from t in tokens
127-
where t.IsName()
128-
join o in specs on t.Text equals o.UniqueName() into to
129-
from o in to.DefaultIfEmpty()
130-
where o != null
131-
select new { o.ShortName, o.LongName };
132-
var groups = from x in options
126+
var shortOptions = from t in tokens
127+
where t.IsName()
128+
join o in specs on t.Text equals o.ShortName into to
129+
from o in to.DefaultIfEmpty()
130+
where o != null
131+
select new { o.ShortName, o.LongName };
132+
var longOptions = from t in tokens
133+
where t.IsName()
134+
join o in specs on t.Text equals o.LongName into to
135+
from o in to.DefaultIfEmpty()
136+
where o != null
137+
select new { o.ShortName, o.LongName };
138+
var groups = from x in shortOptions.Concat(longOptions)
133139
group x by x into g
134140
let count = g.Count()
135141
select new { Value = g.Key, Count = count };

src/CommandLine/Core/TypeConverter.cs

+11-11
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@ namespace CommandLine.Core
1515
{
1616
static class TypeConverter
1717
{
18-
public static Maybe<object> ChangeType(IEnumerable<string> values, Type conversionType, bool scalar, CultureInfo conversionCulture)
18+
public static Maybe<object> ChangeType(IEnumerable<string> values, Type conversionType, bool scalar, CultureInfo conversionCulture, bool ignoreValueCase)
1919
{
2020
return scalar
21-
? ChangeTypeScalar(values.Single(), conversionType, conversionCulture)
22-
: ChangeTypeSequence(values, conversionType, conversionCulture);
21+
? ChangeTypeScalar(values.Single(), conversionType, conversionCulture, ignoreValueCase)
22+
: ChangeTypeSequence(values, conversionType, conversionCulture, ignoreValueCase);
2323
}
2424

25-
private static Maybe<object> ChangeTypeSequence(IEnumerable<string> values, Type conversionType, CultureInfo conversionCulture)
25+
private static Maybe<object> ChangeTypeSequence(IEnumerable<string> values, Type conversionType, CultureInfo conversionCulture, bool ignoreValueCase)
2626
{
2727
var type =
2828
conversionType.GetGenericArguments()
@@ -32,22 +32,22 @@ private static Maybe<object> ChangeTypeSequence(IEnumerable<string> values, Type
3232
new InvalidOperationException("Non scalar properties should be sequence of type IEnumerable<T>.")
3333
);
3434

35-
var converted = values.Select(value => ChangeTypeScalar(value, type, conversionCulture));
35+
var converted = values.Select(value => ChangeTypeScalar(value, type, conversionCulture, ignoreValueCase));
3636

3737
return converted.Any(a => a.MatchNothing())
3838
? Maybe.Nothing<object>()
3939
: Maybe.Just(converted.Select(c => ((Just<object>)c).Value).ToUntypedArray(type));
4040
}
4141

42-
private static Maybe<object> ChangeTypeScalar(string value, Type conversionType, CultureInfo conversionCulture)
42+
private static Maybe<object> ChangeTypeScalar(string value, Type conversionType, CultureInfo conversionCulture, bool ignoreValueCase)
4343
{
44-
var result = ChangeTypeScalarImpl(value, conversionType, conversionCulture);
44+
var result = ChangeTypeScalarImpl(value, conversionType, conversionCulture, ignoreValueCase);
4545
result.Match((_,__) => { }, e => e.First().RethrowWhenAbsentIn(
4646
new[] { typeof(InvalidCastException), typeof(FormatException), typeof(OverflowException) }));
4747
return result.ToMaybe();
4848
}
4949

50-
private static Result<object, Exception> ChangeTypeScalarImpl(string value, Type conversionType, CultureInfo conversionCulture)
50+
private static Result<object, Exception> ChangeTypeScalarImpl(string value, Type conversionType, CultureInfo conversionCulture, bool ignoreValueCase)
5151
{
5252
Func<object> changeType = () =>
5353
{
@@ -76,7 +76,7 @@ private static Result<object, Exception> ChangeTypeScalarImpl(string value, Type
7676

7777
return value.IsBooleanString()
7878
? value.ToBoolean() : conversionType.GetTypeInfo().IsEnum
79-
? value.ToEnum(conversionType) : safeChangeType();
79+
? value.ToEnum(conversionType, ignoreValueCase) : safeChangeType();
8080
};
8181

8282
Func<object> makeType = () =>
@@ -98,12 +98,12 @@ private static Result<object, Exception> ChangeTypeScalarImpl(string value, Type
9898
: makeType);
9999
}
100100

101-
private static object ToEnum(this string value, Type conversionType)
101+
private static object ToEnum(this string value, Type conversionType, bool ignoreValueCase)
102102
{
103103
object parsedValue;
104104
try
105105
{
106-
parsedValue = Enum.Parse(conversionType, value);
106+
parsedValue = Enum.Parse(conversionType, value, ignoreValueCase);
107107
}
108108
catch (ArgumentException)
109109
{

src/CommandLine/Parser.cs

+2
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ public ParserResult<T> ParseArguments<T>(IEnumerable<string> args)
9797
(arguments, optionSpecs) => Tokenize(arguments, optionSpecs, settings),
9898
args,
9999
settings.NameComparer,
100+
settings.CaseInsensitiveEnumValues,
100101
settings.ParsingCulture,
101102
HandleUnknownArguments(settings.IgnoreUnknownArguments)),
102103
settings);
@@ -125,6 +126,7 @@ public ParserResult<T> ParseArguments<T>(Func<T> factory, IEnumerable<string> ar
125126
(arguments, optionSpecs) => Tokenize(arguments, optionSpecs, settings),
126127
args,
127128
settings.NameComparer,
129+
settings.CaseInsensitiveEnumValues,
128130
settings.ParsingCulture,
129131
HandleUnknownArguments(settings.IgnoreUnknownArguments)),
130132
settings);

src/CommandLine/ParserSettings.cs

+12
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ public class ParserSettings : IDisposable
1515
{
1616
private bool disposed;
1717
private bool caseSensitive;
18+
private bool caseInsensitiveEnumValues;
1819
private TextWriter helpWriter;
1920
private bool ignoreUnknownArguments;
2021
private CultureInfo parsingCulture;
@@ -26,6 +27,7 @@ public class ParserSettings : IDisposable
2627
public ParserSettings()
2728
{
2829
caseSensitive = true;
30+
caseInsensitiveEnumValues = false;
2931
parsingCulture = CultureInfo.InvariantCulture;
3032
}
3133

@@ -48,6 +50,16 @@ public bool CaseSensitive
4850
set { PopsicleSetter.Set(Consumed, ref caseSensitive, value); }
4951
}
5052

53+
/// <summary>
54+
/// Gets or sets a value indicating whether perform case sensitive comparisons of <i>values</i>.
55+
/// Note that case insensitivity only applies to <i>values</i>, not the parameters.
56+
/// </summary>
57+
public bool CaseInsensitiveEnumValues
58+
{
59+
get { return caseInsensitiveEnumValues; }
60+
set { PopsicleSetter.Set(Consumed, ref caseInsensitiveEnumValues, value); }
61+
}
62+
5163
/// <summary>
5264
/// Gets or sets the culture used when parsing arguments to typed properties.
5365
/// </summary>

src/CommandLine/Text/HelpText.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public class HelpText
3535
/// Initializes a new instance of the <see cref="CommandLine.Text.HelpText"/> class.
3636
/// </summary>
3737
public HelpText()
38-
: this(SentenceBuilder.CreateDefault(), string.Empty, string.Empty)
38+
: this(SentenceBuilder.Create(), string.Empty, string.Empty)
3939
{
4040
}
4141

@@ -58,7 +58,7 @@ public HelpText(SentenceBuilder sentenceBuilder)
5858
/// <param name="heading">An heading string or an instance of <see cref="CommandLine.Text.HeadingInfo"/>.</param>
5959
/// <exception cref="System.ArgumentException">Thrown when parameter <paramref name="heading"/> is null or empty string.</exception>
6060
public HelpText(string heading)
61-
: this(SentenceBuilder.CreateDefault(), heading, string.Empty)
61+
: this(SentenceBuilder.Create(), heading, string.Empty)
6262
{
6363
}
6464

@@ -81,7 +81,7 @@ public HelpText(SentenceBuilder sentenceBuilder, string heading)
8181
/// <param name="copyright">A string with copyright or an instance of <see cref="CommandLine.Text.CopyrightInfo"/>.</param>
8282
/// <exception cref="System.ArgumentNullException">Thrown when one or more parameters are null or empty strings.</exception>
8383
public HelpText(string heading, string copyright)
84-
: this(SentenceBuilder.CreateDefault(), heading, copyright)
84+
: this(SentenceBuilder.Create(), heading, copyright)
8585
{
8686
}
8787

src/CommandLine/Text/MultiLineTextAttribute.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ public string Line5
150150
internal HelpText AddToHelpText(HelpText helpText, Func<string, HelpText> func)
151151
{
152152
var strArray = new[] { line1, line2, line3, line4, line5 };
153-
return strArray.Aggregate(helpText, (current, line) => func(line));
153+
return strArray.Take(GetLastLineWithText(strArray)).Aggregate(helpText, (current, line) => func(line));
154154
}
155155

156156
internal HelpText AddToHelpText(HelpText helpText, bool before)

src/CommandLine/Text/SentenceBuilder.cs

+9-5
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,19 @@ namespace CommandLine.Text
1515
public abstract class SentenceBuilder
1616
{
1717
/// <summary>
18-
/// Create the default instance of <see cref="CommandLine.Text.SentenceBuilder"/>,
19-
/// localized in English.
18+
/// Create instance of <see cref="CommandLine.Text.SentenceBuilder"/>,
2019
/// </summary>
21-
/// <returns>The default <see cref="CommandLine.Text.SentenceBuilder"/> instance.</returns>
22-
public static SentenceBuilder CreateDefault()
20+
/// <returns>The <see cref="CommandLine.Text.SentenceBuilder"/> instance.</returns>
21+
public static SentenceBuilder Create()
2322
{
24-
return new DefaultSentenceBuilder();
23+
return Factory();
2524
}
2625

26+
/// <summary>
27+
/// Factory to allow custom SentenceBuilder injection
28+
/// </summary>
29+
public static Func<SentenceBuilder> Factory { get; set; } = () => new DefaultSentenceBuilder();
30+
2731
/// <summary>
2832
/// Gets a delegate that returns the word 'required'.
2933
/// </summary>

tests/CommandLine.Tests/Fakes/Simple_Options.cs

+3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ public class Simple_Options
99
[Option(HelpText = "Define a string value here.")]
1010
public string StringValue { get; set; }
1111

12+
[Option('s', "shortandlong", HelpText = "Example with both short and long name.")]
13+
public string ShortAndLong { get; set; }
14+
1215
[Option('i', Min = 3, Max = 4, HelpText = "Define a int sequence here.")]
1316
public IEnumerable<int> IntSequence { get; set; }
1417

0 commit comments

Comments
 (0)