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

Multiple long args #887

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/CommandLine/Core/InstanceBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ static class InstanceBuilder
var missingValueErrors = from token in errorsPartition
select
new MissingValueOptionError(
optionSpecs.Single(o => token.Text.MatchName(o.ShortName, o.LongName, nameComparer))
optionSpecs.Single(o => token.Text.MatchName(o.ShortName, o.LongNames, nameComparer))
.FromOptionSpecification());

var specPropsWithValue =
Expand Down
7 changes: 4 additions & 3 deletions src/CommandLine/Core/NameExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
// Copyright 2005-2015 Giacomo Stelluti Scala & Contributors. All rights reserved. See License.md in the project root for license information.

using System;
using System.Linq;

namespace CommandLine.Core
{
static class NameExtensions
{
public static bool MatchName(this string value, string shortName, string longName, StringComparer comparer)
public static bool MatchName(this string value, string shortName, string[] longNames, StringComparer comparer)
{
return value.Length == 1
? comparer.Equals(value, shortName)
: comparer.Equals(value, longName);
: longNames.Any(longName => comparer.Equals(value, longName));
}

public static NameInfo FromOptionSpecification(this OptionSpecification specification)
{
return new NameInfo(
specification.ShortName,
specification.LongName);
specification.LongNames);
}

public static NameInfo FromSpecification(this Specification specification)
Expand Down
4 changes: 2 additions & 2 deletions src/CommandLine/Core/NameLookup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ static class NameLookup
{
public static NameLookupResult Contains(string name, IEnumerable<OptionSpecification> specifications, StringComparer comparer)
{
var option = specifications.FirstOrDefault(a => name.MatchName(a.ShortName, a.LongName, comparer));
var option = specifications.FirstOrDefault(a => name.MatchName(a.ShortName, a.LongNames, comparer));
if (option == null) return NameLookupResult.NoOptionFound;
return option.ConversionType == typeof(bool) || (option.ConversionType == typeof(int) && option.FlagCounter)
? NameLookupResult.BooleanOptionFound
Expand All @@ -29,7 +29,7 @@ public static NameLookupResult Contains(string name, IEnumerable<OptionSpecifica
StringComparer comparer)
{
return specifications.SingleOrDefault(
a => name.MatchName(a.ShortName, a.LongName, comparer) && a.Separator != '\0')
a => name.MatchName(a.ShortName, a.LongNames, comparer) && a.Separator != '\0')
.ToMaybe()
.MapValueOrDefault(spec => Maybe.Just(spec.Separator), Maybe.Nothing<char>());
}
Expand Down
2 changes: 1 addition & 1 deletion src/CommandLine/Core/OptionMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ static class OptionMapper
pt =>
{
var matched = options.Where(s =>
s.Key.MatchName(((OptionSpecification)pt.Specification).ShortName, ((OptionSpecification)pt.Specification).LongName, comparer)).ToMaybe();
s.Key.MatchName(((OptionSpecification)pt.Specification).ShortName, ((OptionSpecification)pt.Specification).LongNames, comparer)).ToMaybe();
if (matched.IsJust())
{
var matches = matched.GetValueOrDefault(Enumerable.Empty<KeyValuePair<string, IEnumerable<string>>>());
Expand Down
30 changes: 25 additions & 5 deletions src/CommandLine/Core/OptionSpecification.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace CommandLine.Core
sealed class OptionSpecification : Specification
{
private readonly string shortName;
private readonly string longName;
private readonly string[] longNames;
private readonly char separator;
private readonly string setName;
private readonly string group;
Expand All @@ -23,7 +23,21 @@ sealed class OptionSpecification : Specification
required, min, max, defaultValue, helpText, metaValue, enumValues, conversionType, conversionType == typeof(int) && flagCounter ? TargetType.Switch : targetType, hidden)
{
this.shortName = shortName;
this.longName = longName;
this.longNames = new [] { longName };
this.separator = separator;
this.setName = setName;
this.group = group;
this.flagCounter = flagCounter;
}

public OptionSpecification(string shortName, string[] longNames, 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, string group, bool flagCounter = false, bool hidden = false)
: base(SpecificationType.Option,
required, min, max, defaultValue, helpText, metaValue, enumValues, conversionType, conversionType == typeof(int) && flagCounter ? TargetType.Switch : targetType, hidden)
{
this.shortName = shortName;
this.longNames = longNames;
this.separator = separator;
this.setName = setName;
this.group = group;
Expand All @@ -34,7 +48,7 @@ public static OptionSpecification FromAttribute(OptionAttribute attribute, Type
{
return new OptionSpecification(
attribute.ShortName,
attribute.LongName,
attribute.LongNames,
attribute.Required,
attribute.SetName,
attribute.Min == -1 ? Maybe.Nothing<int>() : Maybe.Just(attribute.Min),
Expand All @@ -57,14 +71,20 @@ public static OptionSpecification NewSwitch(string shortName, string longName, b
'\0', Maybe.Nothing<object>(), helpText, metaValue, Enumerable.Empty<string>(), typeof(bool), TargetType.Switch, string.Empty, false, hidden);
}

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

public string ShortName
{
get { return shortName; }
}

public string LongName
public string[] LongNames
{
get { return longName; }
get { return longNames; }
}

public char Separator
Expand Down
2 changes: 1 addition & 1 deletion src/CommandLine/Core/Specification.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ public static Specification FromProperty(PropertyInfo property)
var spec = OptionSpecification.FromAttribute(oa.Single(), property.PropertyType,
ReflectionHelper.GetNamesOfEnum(property.PropertyType));

if (spec.ShortName.Length == 0 && spec.LongName.Length == 0)
if (spec.ShortName.Length == 0 && spec.LongNames.Length == 0)
{
return spec.WithLongName(property.Name.ToLowerInvariant());
}
Expand Down
4 changes: 2 additions & 2 deletions src/CommandLine/Core/SpecificationExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public static OptionSpecification WithLongName(this OptionSpecification specific
{
return new OptionSpecification(
specification.ShortName,
newLongName,
new [] { newLongName },
specification.Required,
specification.SetName,
specification.Min,
Expand All @@ -41,7 +41,7 @@ public static OptionSpecification WithLongName(this OptionSpecification specific

public static string UniqueName(this OptionSpecification specification)
{
return specification.ShortName.Length > 0 ? specification.ShortName : specification.LongName;
return specification.ShortName.Length > 0 ? specification.ShortName : specification.LongNames[0];
}

public static IEnumerable<Specification> ThrowingValidate(this IEnumerable<Specification> specifications, IEnumerable<Tuple<Func<Specification, bool>, string>> guardsLookup)
Expand Down
7 changes: 6 additions & 1 deletion src/CommandLine/Core/SpecificationGuards.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

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

namespace CommandLine.Core
Expand Down Expand Up @@ -30,7 +31,11 @@ static class SpecificationGuards

private static Func<Specification, bool> GuardAgainstOneCharLongName()
{
return spec => spec.IsOption() && ((OptionSpecification)spec).LongName.Length == 1;
return spec =>
{
var optionSpecification = spec as OptionSpecification;
return spec.IsOption() && (optionSpecification?.LongNames.Any(x => x.Length == 1) ?? false);
};
}

private static Func<Specification, bool> GuardAgainstSequenceWithZeroRange()
Expand Down
15 changes: 7 additions & 8 deletions src/CommandLine/Core/SpecificationPropertyRules.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ where sp.Specification.IsOption()
if (options.Any())
{
return from o in options
select new GroupOptionAmbiguityError(new NameInfo(o.ShortName, o.LongName));
select new GroupOptionAmbiguityError(new NameInfo(o.ShortName, o.LongNames));
}

return Enumerable.Empty<Error>();
Expand Down Expand Up @@ -79,7 +79,7 @@ select new

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

return Enumerable.Empty<Error>();
Expand Down Expand Up @@ -199,20 +199,19 @@ where t.IsName()
join o in specs on t.Text equals o.ShortName into to
from o in to.DefaultIfEmpty()
where o != null
select new { o.ShortName, o.LongName };
select new { o.ShortName, o.LongNames };
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 };
from o in specs
where o.LongNames.Contains(t.Text)
select new { o.ShortName, o.LongNames };
var groups = from x in shortOptions.Concat(longOptions)
group x by x into g
let count = g.Count()
select new { Value = g.Key, Count = count };
var errors = from y in groups
where y.Count > 1
select new RepeatedOptionError(new NameInfo(y.Value.ShortName, y.Value.LongName));
select new RepeatedOptionError(new NameInfo(y.Value.ShortName, y.Value.LongNames));
return errors;
};
}
Expand Down
4 changes: 2 additions & 2 deletions src/CommandLine/Core/TypeLookup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ static class TypeLookup
StringComparer comparer)
{
var info =
specifications.SingleOrDefault(a => name.MatchName(a.ShortName, a.LongName, comparer))
specifications.SingleOrDefault(a => name.MatchName(a.ShortName, a.LongNames, comparer))
.ToMaybe()
.Map(
first =>
Expand All @@ -31,4 +31,4 @@ static class TypeLookup

}
}
}
}
48 changes: 36 additions & 12 deletions src/CommandLine/NameInfo.cs
Original file line number Diff line number Diff line change
@@ -1,7 +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 CommandLine.Core;
using System.Linq;

namespace CommandLine
{
Expand All @@ -14,16 +14,40 @@ public sealed class NameInfo : IEquatable<NameInfo>
/// Represents an empty name information. Used when <see cref="CommandLine.Error"/> are tied to values,
/// rather than options.
/// </summary>
public static readonly NameInfo EmptyName = new NameInfo(string.Empty, string.Empty);
private readonly string longName;
public static readonly NameInfo EmptyName = new NameInfo(string.Empty, new string[0]);
private readonly string[] longNames;
private readonly string shortName;

internal NameInfo(string shortName)
{
if (shortName == null) throw new ArgumentNullException("shortName");

this.longNames = new string[0];
this.shortName = shortName;
}

internal NameInfo(string shortName, string longName)
{
if (shortName == null) throw new ArgumentNullException("shortName");
if (longName == null) throw new ArgumentNullException("longName");
if (longName == string.Empty)
{
this.longNames = new string[0];
}
else
{
this.longNames = new [] { longName };
}

this.longName = longName;
this.shortName = shortName;
}

internal NameInfo(string shortName, string[] longNames)
{
if (shortName == null) throw new ArgumentNullException("shortName");
if (longNames == null) throw new ArgumentNullException("longNames");
if (longNames.Any(x => x == null)) throw new ArgumentNullException("longNames");
this.longNames = longNames;
this.shortName = shortName;
}

Expand All @@ -38,9 +62,9 @@ public string ShortName
/// <summary>
/// Gets the long name of the name information.
/// </summary>
public string LongName
public string[] LongNames
{
get { return longName; }
get { return longNames; }
}

/// <summary>
Expand All @@ -50,11 +74,11 @@ public string NameText
{
get
{
return ShortName.Length > 0 && LongName.Length > 0
? ShortName + ", " + LongName
return ShortName.Length > 0 && LongNames.Length > 0
? ShortName + ", " + string.Join(", ", LongNames)
: ShortName.Length > 0
? ShortName
: LongName;
: string.Join(", ", LongNames);
}
}

Expand All @@ -80,7 +104,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 { ShortName, LongName }.GetHashCode();
return CSharpx.EnumerableExtensions.Prepend(LongNames, ShortName).ToArray().GetHashCode();
}

/// <summary>
Expand All @@ -95,7 +119,7 @@ public bool Equals(NameInfo other)
return false;
}

return ShortName.Equals(other.ShortName) && LongName.Equals(other.LongName);
return ShortName.Equals(other.ShortName) && LongNames.SequenceEqual(other.LongNames);
}
}
}
}