Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce option groups #552

Merged
merged 14 commits into from
Dec 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 14 additions & 5 deletions src/CommandLine/Core/OptionSpecification.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2005-2015 Giacomo Stelluti Scala & Contributors. All rights reserved. See License.md in the project root for license information.
// Copyright 2005-2015 Giacomo Stelluti Scala & Contributors. All rights reserved. See License.md in the project root for license information.

using System;
using System.Collections.Generic;
Expand All @@ -13,16 +13,19 @@ sealed class OptionSpecification : Specification
private readonly string longName;
private readonly char separator;
private readonly string setName;
private readonly string group;

public OptionSpecification(string shortName, string longName, bool required, string setName, Maybe<int> min, Maybe<int> max,
char separator, Maybe<object> defaultValue, string helpText, string metaValue, IEnumerable<string> enumValues,
Type conversionType, TargetType targetType, bool hidden = false)
: base(SpecificationType.Option, required, min, max, defaultValue, helpText, metaValue, enumValues, conversionType, targetType, hidden)
Type conversionType, TargetType targetType, string group, bool hidden = false)
: base(SpecificationType.Option,
required, min, max, defaultValue, helpText, metaValue, enumValues, conversionType, targetType, hidden)
{
this.shortName = shortName;
this.longName = longName;
this.separator = separator;
this.setName = setName;
this.group = group;
}

public static OptionSpecification FromAttribute(OptionAttribute attribute, Type conversionType, IEnumerable<string> enumValues)
Expand All @@ -41,13 +44,14 @@ public static OptionSpecification FromAttribute(OptionAttribute attribute, Type
enumValues,
conversionType,
conversionType.ToTargetType(),
attribute.Group,
attribute.Hidden);
}

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

public string ShortName
Expand All @@ -69,5 +73,10 @@ public string SetName
{
get { return setName; }
}

public string Group
{
get { return group; }
}
}
}
}
1 change: 1 addition & 0 deletions src/CommandLine/Core/SpecificationExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public static OptionSpecification WithLongName(this OptionSpecification specific
specification.EnumValues,
specification.ConversionType,
specification.TargetType,
specification.Group,
specification.Hidden);
}

Expand Down
76 changes: 55 additions & 21 deletions src/CommandLine/Core/SpecificationPropertyRules.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
// Copyright 2005-2015 Giacomo Stelluti Scala & Contributors. All rights reserved. See License.md in the project root for license information.
// Copyright 2005-2015 Giacomo Stelluti Scala & Contributors. All rights reserved. See License.md in the project root for license information.

using CSharpx;

using System;
using System.Collections.Generic;
using System.Linq;
using CSharpx;

namespace CommandLine.Core
{
Expand All @@ -16,12 +17,43 @@ public static IEnumerable<Func<IEnumerable<SpecificationProperty>, IEnumerable<E
return new List<Func<IEnumerable<SpecificationProperty>, IEnumerable<Error>>>
{
EnforceMutuallyExclusiveSet(),
EnforceGroup(),
EnforceRequired(),
EnforceRange(),
EnforceSingle(tokens)
};
}

private static Func<IEnumerable<SpecificationProperty>, IEnumerable<Error>> EnforceGroup()
{
return specProps =>
{
var optionsValues =
from sp in specProps
where sp.Specification.IsOption()
let o = (OptionSpecification)sp.Specification
where o.Group.Length > 0
select new
{
Option = o,
Value = sp.Value
};

var groups = from o in optionsValues
group o by o.Option.Group into g
select g;

var errorGroups = groups.Where(gr => gr.All(g => g.Value.IsNothing()));

if (errorGroups.Any())
{
return errorGroups.Select(gr => new MissingGroupOptionError(gr.Key, gr.Select(g => new NameInfo(g.Option.ShortName, g.Option.LongName))));
}

return Enumerable.Empty<Error>();
};
}

private static Func<IEnumerable<SpecificationProperty>, IEnumerable<Error>> EnforceMutuallyExclusiveSet()
{
return specProps =>
Expand Down Expand Up @@ -51,26 +83,27 @@ private static Func<IEnumerable<SpecificationProperty>, IEnumerable<Error>> Enfo
return specProps =>
{
var requiredWithValue = from sp in specProps
where sp.Specification.IsOption()
where sp.Specification.Required
where sp.Value.IsJust()
let o = (OptionSpecification)sp.Specification
where o.SetName.Length > 0
select sp.Specification;
where sp.Specification.IsOption()
where sp.Specification.Required
where sp.Value.IsJust()
let o = (OptionSpecification)sp.Specification
where o.SetName.Length > 0
select sp.Specification;
var setWithRequiredValue = (
from s in requiredWithValue
let o = (OptionSpecification)s
where o.SetName.Length > 0
select o.SetName)
.Distinct();
var requiredWithoutValue = from sp in specProps
where sp.Specification.IsOption()
where sp.Specification.Required
where sp.Value.IsNothing()
let o = (OptionSpecification)sp.Specification
where o.SetName.Length > 0
where setWithRequiredValue.ContainsIfNotEmpty(o.SetName)
select sp.Specification;
where sp.Specification.IsOption()
where sp.Specification.Required
where sp.Value.IsNothing()
let o = (OptionSpecification)sp.Specification
where o.SetName.Length > 0
where o.Group.Length == 0
where setWithRequiredValue.ContainsIfNotEmpty(o.SetName)
select sp.Specification;
var missing =
requiredWithoutValue
.Except(requiredWithValue)
Expand All @@ -81,6 +114,7 @@ where sp.Specification.Required
where sp.Value.IsNothing()
let o = (OptionSpecification)sp.Specification
where o.SetName.Length == 0
where o.Group.Length == 0
select sp.Specification)
.Concat(
from sp in specProps
Expand Down Expand Up @@ -130,11 +164,11 @@ from o in to.DefaultIfEmpty()
where o != null
select new { o.ShortName, o.LongName };
var longOptions = from t in tokens
where t.IsName()
join o in specs on t.Text equals o.LongName into to
from o in to.DefaultIfEmpty()
where o != null
select new { o.ShortName, o.LongName };
where t.IsName()
join o in specs on t.Text equals o.LongName into to
from o in to.DefaultIfEmpty()
where o != null
select new { o.ShortName, o.LongName };
var groups = from x in shortOptions.Concat(longOptions)
group x by x into g
let count = g.Count()
Expand All @@ -155,4 +189,4 @@ private static bool ContainsIfNotEmpty<T>(this IEnumerable<T> sequence, T value)
return true;
}
}
}
}
36 changes: 33 additions & 3 deletions src/CommandLine/Error.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright 2005-2015 Giacomo Stelluti Scala & Contributors. All rights reserved. See License.md in the project root for license information.

using System;
using System.Collections.Generic;

namespace CommandLine
{
Expand Down Expand Up @@ -69,7 +70,11 @@ public enum ErrorType
/// <summary>
/// Value of <see cref="CommandLine.InvalidAttributeConfigurationError"/> type.
/// </summary>
InvalidAttributeConfigurationError
InvalidAttributeConfigurationError,
/// <summary>
/// Value of <see cref="CommandLine.MissingGroupOptionError"/> type.
/// </summary>
MissingGroupOptionError

}

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

/// <summary>
Expand Down Expand Up @@ -289,7 +294,7 @@ public override bool Equals(object obj)
/// <remarks>A hash code for the current <see cref="System.Object"/>.</remarks>
public override int GetHashCode()
{
return new {Tag, StopsProcessing, NameInfo}.GetHashCode();
return new { Tag, StopsProcessing, NameInfo }.GetHashCode();
}

/// <summary>
Expand Down Expand Up @@ -526,4 +531,29 @@ internal InvalidAttributeConfigurationError()
{
}
}

public sealed class MissingGroupOptionError : Error
{
public const string ErrorMessage = "At least one option in a group must have value.";

private readonly string group;
private readonly IEnumerable<NameInfo> names;

internal MissingGroupOptionError(string group, IEnumerable<NameInfo> names)
: base(ErrorType.MissingGroupOptionError)
{
this.group = group;
this.names = names;
}

public string Group
{
get { return group; }
}

public IEnumerable<NameInfo> Names
{
get { return names; }
}
}
}
17 changes: 14 additions & 3 deletions src/CommandLine/OptionAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
// Copyright 2005-2015 Giacomo Stelluti Scala & Contributors. All rights reserved. See License.md in the project root for license information.

using System;
using CommandLine.Infrastructure;

using System;

namespace CommandLine
{
/// <summary>
Expand All @@ -15,6 +16,7 @@ public sealed class OptionAttribute : BaseAttribute
private readonly string shortName;
private string setName;
private char separator;
private string group=string.Empty;

private OptionAttribute(string shortName, string longName) : base()
{
Expand Down Expand Up @@ -100,8 +102,17 @@ public string SetName
/// </summary>
public char Separator
{
get { return separator ; }
get { return separator; }
set { separator = value; }
}

/// <summary>
/// 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.
/// </summary>
public string Group
{
get { return group; }
set { group = value; }
}
}
}
}
Loading