Skip to content

Commit 7e6325d

Browse files
authored
Merge pull request #552 from hadzhiyski/feature/group-options
Introduce option groups
2 parents 02878fb + ff61b69 commit 7e6325d

18 files changed

+546
-267
lines changed

src/CommandLine/Core/OptionSpecification.cs

+14-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2005-2015 Giacomo Stelluti Scala & Contributors. All rights reserved. See License.md in the project root for license information.
1+
// Copyright 2005-2015 Giacomo Stelluti Scala & Contributors. All rights reserved. See License.md in the project root for license information.
22

33
using System;
44
using System.Collections.Generic;
@@ -13,16 +13,19 @@ sealed class OptionSpecification : Specification
1313
private readonly string longName;
1414
private readonly char separator;
1515
private readonly string setName;
16+
private readonly string group;
1617

1718
public OptionSpecification(string shortName, string longName, bool required, string setName, Maybe<int> min, Maybe<int> max,
1819
char separator, Maybe<object> defaultValue, string helpText, string metaValue, IEnumerable<string> enumValues,
19-
Type conversionType, TargetType targetType, bool hidden = false)
20-
: base(SpecificationType.Option, required, min, max, defaultValue, helpText, metaValue, enumValues, conversionType, targetType, hidden)
20+
Type conversionType, TargetType targetType, string group, bool hidden = false)
21+
: base(SpecificationType.Option,
22+
required, min, max, defaultValue, helpText, metaValue, enumValues, conversionType, targetType, hidden)
2123
{
2224
this.shortName = shortName;
2325
this.longName = longName;
2426
this.separator = separator;
2527
this.setName = setName;
28+
this.group = group;
2629
}
2730

2831
public static OptionSpecification FromAttribute(OptionAttribute attribute, Type conversionType, IEnumerable<string> enumValues)
@@ -41,13 +44,14 @@ public static OptionSpecification FromAttribute(OptionAttribute attribute, Type
4144
enumValues,
4245
conversionType,
4346
conversionType.ToTargetType(),
47+
attribute.Group,
4448
attribute.Hidden);
4549
}
4650

4751
public static OptionSpecification NewSwitch(string shortName, string longName, bool required, string helpText, string metaValue, bool hidden = false)
4852
{
4953
return new OptionSpecification(shortName, longName, required, string.Empty, Maybe.Nothing<int>(), Maybe.Nothing<int>(),
50-
'\0', Maybe.Nothing<object>(), helpText, metaValue, Enumerable.Empty<string>(), typeof(bool), TargetType.Switch, hidden);
54+
'\0', Maybe.Nothing<object>(), helpText, metaValue, Enumerable.Empty<string>(), typeof(bool), TargetType.Switch, string.Empty, hidden);
5155
}
5256

5357
public string ShortName
@@ -69,5 +73,10 @@ public string SetName
6973
{
7074
get { return setName; }
7175
}
76+
77+
public string Group
78+
{
79+
get { return group; }
80+
}
7281
}
73-
}
82+
}

src/CommandLine/Core/SpecificationExtensions.cs

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ public static OptionSpecification WithLongName(this OptionSpecification specific
3434
specification.EnumValues,
3535
specification.ConversionType,
3636
specification.TargetType,
37+
specification.Group,
3738
specification.Hidden);
3839
}
3940

src/CommandLine/Core/SpecificationPropertyRules.cs

+55-21
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
// Copyright 2005-2015 Giacomo Stelluti Scala & Contributors. All rights reserved. See License.md in the project root for license information.
1+
// Copyright 2005-2015 Giacomo Stelluti Scala & Contributors. All rights reserved. See License.md in the project root for license information.
2+
3+
using CSharpx;
24

35
using System;
46
using System.Collections.Generic;
57
using System.Linq;
6-
using CSharpx;
78

89
namespace CommandLine.Core
910
{
@@ -16,12 +17,43 @@ public static IEnumerable<Func<IEnumerable<SpecificationProperty>, IEnumerable<E
1617
return new List<Func<IEnumerable<SpecificationProperty>, IEnumerable<Error>>>
1718
{
1819
EnforceMutuallyExclusiveSet(),
20+
EnforceGroup(),
1921
EnforceRequired(),
2022
EnforceRange(),
2123
EnforceSingle(tokens)
2224
};
2325
}
2426

27+
private static Func<IEnumerable<SpecificationProperty>, IEnumerable<Error>> EnforceGroup()
28+
{
29+
return specProps =>
30+
{
31+
var optionsValues =
32+
from sp in specProps
33+
where sp.Specification.IsOption()
34+
let o = (OptionSpecification)sp.Specification
35+
where o.Group.Length > 0
36+
select new
37+
{
38+
Option = o,
39+
Value = sp.Value
40+
};
41+
42+
var groups = from o in optionsValues
43+
group o by o.Option.Group into g
44+
select g;
45+
46+
var errorGroups = groups.Where(gr => gr.All(g => g.Value.IsNothing()));
47+
48+
if (errorGroups.Any())
49+
{
50+
return errorGroups.Select(gr => new MissingGroupOptionError(gr.Key, gr.Select(g => new NameInfo(g.Option.ShortName, g.Option.LongName))));
51+
}
52+
53+
return Enumerable.Empty<Error>();
54+
};
55+
}
56+
2557
private static Func<IEnumerable<SpecificationProperty>, IEnumerable<Error>> EnforceMutuallyExclusiveSet()
2658
{
2759
return specProps =>
@@ -51,26 +83,27 @@ private static Func<IEnumerable<SpecificationProperty>, IEnumerable<Error>> Enfo
5183
return specProps =>
5284
{
5385
var requiredWithValue = from sp in specProps
54-
where sp.Specification.IsOption()
55-
where sp.Specification.Required
56-
where sp.Value.IsJust()
57-
let o = (OptionSpecification)sp.Specification
58-
where o.SetName.Length > 0
59-
select sp.Specification;
86+
where sp.Specification.IsOption()
87+
where sp.Specification.Required
88+
where sp.Value.IsJust()
89+
let o = (OptionSpecification)sp.Specification
90+
where o.SetName.Length > 0
91+
select sp.Specification;
6092
var setWithRequiredValue = (
6193
from s in requiredWithValue
6294
let o = (OptionSpecification)s
6395
where o.SetName.Length > 0
6496
select o.SetName)
6597
.Distinct();
6698
var requiredWithoutValue = from sp in specProps
67-
where sp.Specification.IsOption()
68-
where sp.Specification.Required
69-
where sp.Value.IsNothing()
70-
let o = (OptionSpecification)sp.Specification
71-
where o.SetName.Length > 0
72-
where setWithRequiredValue.ContainsIfNotEmpty(o.SetName)
73-
select sp.Specification;
99+
where sp.Specification.IsOption()
100+
where sp.Specification.Required
101+
where sp.Value.IsNothing()
102+
let o = (OptionSpecification)sp.Specification
103+
where o.SetName.Length > 0
104+
where o.Group.Length == 0
105+
where setWithRequiredValue.ContainsIfNotEmpty(o.SetName)
106+
select sp.Specification;
74107
var missing =
75108
requiredWithoutValue
76109
.Except(requiredWithValue)
@@ -81,6 +114,7 @@ where sp.Specification.Required
81114
where sp.Value.IsNothing()
82115
let o = (OptionSpecification)sp.Specification
83116
where o.SetName.Length == 0
117+
where o.Group.Length == 0
84118
select sp.Specification)
85119
.Concat(
86120
from sp in specProps
@@ -130,11 +164,11 @@ from o in to.DefaultIfEmpty()
130164
where o != null
131165
select new { o.ShortName, o.LongName };
132166
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 };
167+
where t.IsName()
168+
join o in specs on t.Text equals o.LongName into to
169+
from o in to.DefaultIfEmpty()
170+
where o != null
171+
select new { o.ShortName, o.LongName };
138172
var groups = from x in shortOptions.Concat(longOptions)
139173
group x by x into g
140174
let count = g.Count()
@@ -155,4 +189,4 @@ private static bool ContainsIfNotEmpty<T>(this IEnumerable<T> sequence, T value)
155189
return true;
156190
}
157191
}
158-
}
192+
}

src/CommandLine/Error.cs

+33-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright 2005-2015 Giacomo Stelluti Scala & Contributors. All rights reserved. See License.md in the project root for license information.
22

33
using System;
4+
using System.Collections.Generic;
45

56
namespace CommandLine
67
{
@@ -69,7 +70,11 @@ public enum ErrorType
6970
/// <summary>
7071
/// Value of <see cref="CommandLine.InvalidAttributeConfigurationError"/> type.
7172
/// </summary>
72-
InvalidAttributeConfigurationError
73+
InvalidAttributeConfigurationError,
74+
/// <summary>
75+
/// Value of <see cref="CommandLine.MissingGroupOptionError"/> type.
76+
/// </summary>
77+
MissingGroupOptionError
7378

7479
}
7580

@@ -210,7 +215,7 @@ public override bool Equals(object obj)
210215
/// <remarks>A hash code for the current <see cref="System.Object"/>.</remarks>
211216
public override int GetHashCode()
212217
{
213-
return new {Tag, StopsProcessing, Token}.GetHashCode();
218+
return new { Tag, StopsProcessing, Token }.GetHashCode();
214219
}
215220

216221
/// <summary>
@@ -289,7 +294,7 @@ public override bool Equals(object obj)
289294
/// <remarks>A hash code for the current <see cref="System.Object"/>.</remarks>
290295
public override int GetHashCode()
291296
{
292-
return new {Tag, StopsProcessing, NameInfo}.GetHashCode();
297+
return new { Tag, StopsProcessing, NameInfo }.GetHashCode();
293298
}
294299

295300
/// <summary>
@@ -526,4 +531,29 @@ internal InvalidAttributeConfigurationError()
526531
{
527532
}
528533
}
534+
535+
public sealed class MissingGroupOptionError : Error
536+
{
537+
public const string ErrorMessage = "At least one option in a group must have value.";
538+
539+
private readonly string group;
540+
private readonly IEnumerable<NameInfo> names;
541+
542+
internal MissingGroupOptionError(string group, IEnumerable<NameInfo> names)
543+
: base(ErrorType.MissingGroupOptionError)
544+
{
545+
this.group = group;
546+
this.names = names;
547+
}
548+
549+
public string Group
550+
{
551+
get { return group; }
552+
}
553+
554+
public IEnumerable<NameInfo> Names
555+
{
556+
get { return names; }
557+
}
558+
}
529559
}

src/CommandLine/OptionAttribute.cs

+14-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
// Copyright 2005-2015 Giacomo Stelluti Scala & Contributors. All rights reserved. See License.md in the project root for license information.
22

3-
using System;
43
using CommandLine.Infrastructure;
54

5+
using System;
6+
67
namespace CommandLine
78
{
89
/// <summary>
@@ -15,6 +16,7 @@ public sealed class OptionAttribute : BaseAttribute
1516
private readonly string shortName;
1617
private string setName;
1718
private char separator;
19+
private string group=string.Empty;
1820

1921
private OptionAttribute(string shortName, string longName) : base()
2022
{
@@ -100,8 +102,17 @@ public string SetName
100102
/// </summary>
101103
public char Separator
102104
{
103-
get { return separator ; }
105+
get { return separator; }
104106
set { separator = value; }
105107
}
108+
109+
/// <summary>
110+
/// Gets or sets the option group name. When one or more options are grouped, at least one of them should have value. Required rules are ignored.
111+
/// </summary>
112+
public string Group
113+
{
114+
get { return group; }
115+
set { group = value; }
116+
}
106117
}
107-
}
118+
}

0 commit comments

Comments
 (0)