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

Add environment variable support #698

Open
wants to merge 4 commits into
base: master
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/CommandLine/BaseAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public abstract class BaseAttribute : Attribute
private int min;
private int max;
private object @default;
private string env;
private Infrastructure.LocalizableAttributeProperty helpText;
private string metaValue;
private Type resourceType;
Expand Down Expand Up @@ -87,6 +88,18 @@ public object Default
}
}

/// <summary>
/// Gets or sets mapped property env value.
/// </summary>
public string Env
{
get { return env; }
set
{
env = value;
}
}

/// <summary>
/// Gets or sets a short description of this command line option. Usually a sentence summary.
/// </summary>
Expand Down
31 changes: 28 additions & 3 deletions src/CommandLine/Core/InstanceBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ public static ParserResult<T> Build<T>(
T instance;
if(typeInfo.IsMutable() == true)
{
instance = BuildMutable(factory, specPropsWithValue, setPropertyErrors);
instance = BuildMutable(factory, specPropsWithValue, setPropertyErrors, ignoreValueCase, parsingCulture);
}
else
{
Expand Down Expand Up @@ -126,7 +126,12 @@ public static ParserResult<T> Build<T>(
return result;
}

private static T BuildMutable<T>(Maybe<Func<T>> factory, IEnumerable<SpecificationProperty> specPropsWithValue, List<Error> setPropertyErrors )
private static T BuildMutable<T>(
Maybe<Func<T>> factory,
IEnumerable<SpecificationProperty> specPropsWithValue,
List<Error> setPropertyErrors,
bool ignoreValueCase,
CultureInfo parsingCulture)
{
var mutable = factory.MapValueOrDefault(f => f(), () => Activator.CreateInstance<T>());

Expand All @@ -141,7 +146,27 @@ private static T BuildMutable<T>(Maybe<Func<T>> factory, IEnumerable<Specificati
setPropertyErrors.AddRange(
mutable.SetProperties(
specPropsWithValue,
sp => sp.Value.IsNothing() && sp.Specification.DefaultValue.IsJust(),
sp => sp.Value.IsNothing() && sp.Specification.Env.Map(Environment.GetEnvironmentVariable).Map(n => !(n is null)).GetValueOrDefault(false),
sp => sp.Specification.Env
.Map(Environment.GetEnvironmentVariable)
.Bind(v =>
{
var isSequence = sp.Specification.TargetType == TargetType.Sequence;
if (isSequence)
{
throw new Exception($"Sequences not supported for options with \"Env\" as is the case with {sp.Property.Name}");
}
return TypeConverter
.ChangeType(new string[] { v }, sp.Specification.ConversionType, true, parsingCulture, ignoreValueCase);
})
.FromJustOrFail()
)
);

setPropertyErrors.AddRange(
mutable.SetProperties(
specPropsWithValue,
sp => sp.Value.IsNothing() && sp.Specification.DefaultValue.IsJust() && sp.Specification.Env.Map(Environment.GetEnvironmentVariable).Map(n => n is null).GetValueOrDefault(true),
sp => sp.Specification.DefaultValue.FromJustOrFail()
)
);
Expand Down
7 changes: 4 additions & 3 deletions src/CommandLine/Core/OptionSpecification.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ sealed class OptionSpecification : Specification
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,
char separator, Maybe<object> defaultValue, Maybe<string> env, string helpText, string metaValue, IEnumerable<string> enumValues,
Type conversionType, TargetType targetType, string group, bool hidden = false)
: base(SpecificationType.Option,
required, min, max, defaultValue, helpText, metaValue, enumValues, conversionType, targetType, hidden)
required, min, max, defaultValue, env, helpText, metaValue, enumValues, conversionType, targetType, hidden)
{
this.shortName = shortName;
this.longName = longName;
Expand All @@ -39,6 +39,7 @@ public static OptionSpecification FromAttribute(OptionAttribute attribute, Type
attribute.Max == -1 ? Maybe.Nothing<int>() : Maybe.Just(attribute.Max),
attribute.Separator,
attribute.Default.ToMaybe(),
attribute.Env.ToMaybe(),
attribute.HelpText,
attribute.MetaValue,
enumValues,
Expand All @@ -51,7 +52,7 @@ public static OptionSpecification FromAttribute(OptionAttribute attribute, Type
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, string.Empty, hidden);
'\0', Maybe.Nothing<object>(), Maybe.Nothing<string>(), helpText, metaValue, Enumerable.Empty<string>(), typeof(bool), TargetType.Switch, string.Empty, hidden);
}

public string ShortName
Expand Down
16 changes: 12 additions & 4 deletions src/CommandLine/Core/Specification.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ abstract class Specification
private readonly Maybe<int> min;
private readonly Maybe<int> max;
private readonly Maybe<object> defaultValue;
private readonly Maybe<string> env;
private readonly string helpText;
private readonly string metaValue;
private readonly IEnumerable<string> enumValues;
Expand All @@ -38,14 +39,15 @@ abstract class Specification
private readonly TargetType targetType;

protected Specification(SpecificationType tag, bool required, Maybe<int> min, Maybe<int> max,
Maybe<object> defaultValue, string helpText, string metaValue, IEnumerable<string> enumValues,
Maybe<object> defaultValue, Maybe<string> env, string helpText, string metaValue, IEnumerable<string> enumValues,
Type conversionType, TargetType targetType, bool hidden = false)
{
this.tag = tag;
this.required = required;
this.min = min;
this.max = max;
this.defaultValue = defaultValue;
this.env = env;
this.conversionType = conversionType;
this.targetType = targetType;
this.helpText = helpText;
Expand All @@ -54,7 +56,7 @@ protected Specification(SpecificationType tag, bool required, Maybe<int> min, Ma
this.hidden = hidden;
}

public SpecificationType Tag
public SpecificationType Tag
{
get { return tag; }
}
Expand All @@ -79,6 +81,12 @@ public Maybe<object> DefaultValue
get { return defaultValue; }
}

public Maybe<string> Env
{
get { return env; }
}


public string HelpText
{
get { return helpText; }
Expand Down Expand Up @@ -110,13 +118,13 @@ public bool Hidden
}

public static Specification FromProperty(PropertyInfo property)
{
{
var attrs = property.GetCustomAttributes(true);
var oa = attrs.OfType<OptionAttribute>();
if (oa.Count() == 1)
{
var spec = OptionSpecification.FromAttribute(oa.Single(), property.PropertyType,
ReflectionHelper.GetNamesOfEnum(property.PropertyType));
ReflectionHelper.GetNamesOfEnum(property.PropertyType));

if (spec.ShortName.Length == 0 && spec.LongName.Length == 0)
{
Expand Down
1 change: 1 addition & 0 deletions src/CommandLine/Core/SpecificationExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public static OptionSpecification WithLongName(this OptionSpecification specific
specification.Max,
specification.Separator,
specification.DefaultValue,
specification.Env,
specification.HelpText,
specification.MetaValue,
specification.EnumValues,
Expand Down
5 changes: 3 additions & 2 deletions src/CommandLine/Core/ValueSpecification.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ sealed class ValueSpecification : Specification
private readonly int index;
private readonly string metaName;

public ValueSpecification(int index, string metaName, bool required, Maybe<int> min, Maybe<int> max, Maybe<object> defaultValue,
public ValueSpecification(int index, string metaName, bool required, Maybe<int> min, Maybe<int> max, Maybe<object> defaultValue, Maybe<string> env,
string helpText, string metaValue, IEnumerable<string> enumValues,
Type conversionType, TargetType targetType, bool hidden = false)
: base(SpecificationType.Value, required, min, max, defaultValue, helpText, metaValue, enumValues, conversionType, targetType, hidden)
: base(SpecificationType.Value, required, min, max, defaultValue, env, helpText, metaValue, enumValues, conversionType, targetType, hidden)
{
this.index = index;
this.metaName = metaName;
Expand All @@ -29,6 +29,7 @@ public static ValueSpecification FromAttribute(ValueAttribute attribute, Type co
attribute.Min == -1 ? Maybe.Nothing<int>() : Maybe.Just(attribute.Min),
attribute.Max == -1 ? Maybe.Nothing<int>() : Maybe.Just(attribute.Max),
attribute.Default.ToMaybe(),
attribute.Env.ToMaybe(),
attribute.HelpText,
attribute.MetaValue,
enumValues,
Expand Down
8 changes: 5 additions & 3 deletions src/CommandLine/Infrastructure/StringExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,14 @@ public static string JoinTo(this string value, params string[] others)
public static bool IsBooleanString(this string value)
{
return value.Equals("true", StringComparison.OrdinalIgnoreCase)
|| value.Equals("false", StringComparison.OrdinalIgnoreCase);
|| value.Equals("false", StringComparison.OrdinalIgnoreCase)
|| value.Equals("1", StringComparison.OrdinalIgnoreCase)
|| value.Equals("0", StringComparison.OrdinalIgnoreCase);
}

public static bool ToBoolean(this string value)
{
return value.Equals("true", StringComparison.OrdinalIgnoreCase);
return value.Equals("true", StringComparison.OrdinalIgnoreCase) || value.Equals("1", StringComparison.OrdinalIgnoreCase);
}
}
}
}
28 changes: 28 additions & 0 deletions tests/CommandLine.Tests/Fakes/Simple_Options_With_Env.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright 2005-2015 Giacomo Stelluti Scala & Contributors. All rights reserved. See License.md in the project root for license information.

using System.Collections.Generic;

namespace CommandLine.Tests.Fakes
{
class Simple_Options_With_Env
{
[Option('s', Default = "", Env = "StringValue")]
public string StringValue { get; set; }

[Option("bvff", Env = "BoolValueFullFalse", HelpText = "Define a boolean or switch value here.")]
public bool BoolValueFullFalse { get; set; }
[Option("bvft", Env = "BoolValueFullTrue", HelpText = "Define a boolean or switch value here.")]
public bool BoolValueFullTrue { get; set; }
[Option("bvst", Env = "BoolValueShortTrue", HelpText = "Define a boolean or switch value here.")]
public bool BoolValueShortTrue { get; set; }
[Option("bvsf", Env = "BoolValueShortFalse", HelpText = "Define a boolean or switch value here.")]
public bool BoolValueShortFalse { get; set; }


[Option('l', Default = 1, Env = "LongValue")]
public long LongValue { get; set; }

[Option('i', Default = 2, Env = "IntValue")]
public long IntValue { get; set; }
}
}
4 changes: 2 additions & 2 deletions tests/CommandLine.Tests/Unit/Core/NameLookupTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public void Lookup_name_of_sequence_option_with_separator()
// Fixture setup
var expected = Maybe.Just(".");
var specs = new[] { new OptionSpecification(string.Empty, "string-seq",
false, string.Empty, Maybe.Nothing<int>(), Maybe.Nothing<int>(), '.', null, string.Empty, string.Empty, new List<string>(), typeof(IEnumerable<string>), TargetType.Sequence, string.Empty)};
false, string.Empty, Maybe.Nothing<int>(), Maybe.Nothing<int>(), '.', null, null, string.Empty, string.Empty, new List<string>(), typeof(IEnumerable<string>), TargetType.Sequence, string.Empty)};

// Exercize system
var result = NameLookup.HavingSeparator("string-seq", specs, StringComparer.Ordinal);
Expand All @@ -35,7 +35,7 @@ public void Get_name_from_option_specification()

// Fixture setup
var expected = new NameInfo(ShortName, LongName);
var spec = new OptionSpecification(ShortName, LongName, false, string.Empty, Maybe.Nothing<int>(), Maybe.Nothing<int>(), '.', null, string.Empty, string.Empty, new List<string>(), typeof(IEnumerable<string>), TargetType.Sequence, string.Empty);
var spec = new OptionSpecification(ShortName, LongName, false, string.Empty, Maybe.Nothing<int>(), Maybe.Nothing<int>(), '.', null, null, string.Empty, string.Empty, new List<string>(), typeof(IEnumerable<string>), TargetType.Sequence, string.Empty);

// Exercize system
var result = spec.FromOptionSpecification();
Expand Down
2 changes: 1 addition & 1 deletion tests/CommandLine.Tests/Unit/Core/OptionMapperTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public void Map_boolean_switch_creates_boolean_value()
var specProps = new[]
{
SpecificationProperty.Create(
new OptionSpecification("x", string.Empty, false, string.Empty, Maybe.Nothing<int>(), Maybe.Nothing<int>(), '\0', Maybe.Nothing<object>(), string.Empty, string.Empty, new List<string>(), typeof(bool), TargetType.Switch, string.Empty),
new OptionSpecification("x", string.Empty, false, string.Empty, Maybe.Nothing<int>(), Maybe.Nothing<int>(), '\0', Maybe.Nothing<object>(), Maybe.Nothing<string>(), string.Empty, string.Empty, new List<string>(), typeof(bool), TargetType.Switch, string.Empty),
typeof(Simple_Options).GetProperties().Single(p => p.Name.Equals("BoolValue", StringComparison.Ordinal)),
Maybe.Nothing<object>())
};
Expand Down
8 changes: 4 additions & 4 deletions tests/CommandLine.Tests/Unit/Core/TokenPartitionerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ public void Partition_sequence_returns_sequence()
};
var specs = new[]
{
new OptionSpecification(string.Empty, "stringvalue", false, string.Empty, Maybe.Nothing<int>(), Maybe.Nothing<int>(), '\0', null, string.Empty, string.Empty, new List<string>(), typeof(string), TargetType.Scalar, string.Empty),
new OptionSpecification("i", string.Empty, false, string.Empty, Maybe.Just(3), Maybe.Just(4), '\0', null, string.Empty, string.Empty, new List<string>(), typeof(IEnumerable<int>), TargetType.Sequence, string.Empty)
new OptionSpecification(string.Empty, "stringvalue", false, string.Empty, Maybe.Nothing<int>(), Maybe.Nothing<int>(), '\0', null, null, string.Empty, string.Empty, new List<string>(), typeof(string), TargetType.Scalar, string.Empty),
new OptionSpecification("i", string.Empty, false, string.Empty, Maybe.Just(3), Maybe.Just(4), '\0', null, null, string.Empty, string.Empty, new List<string>(), typeof(IEnumerable<int>), TargetType.Sequence, string.Empty)
};

// Exercize system
Expand All @@ -48,8 +48,8 @@ public void Partition_sequence_returns_sequence_with_duplicates()
};
var specs = new[]
{
new OptionSpecification(string.Empty, "stringvalue", false, string.Empty, Maybe.Nothing<int>(), Maybe.Nothing<int>(), '\0', null, string.Empty, string.Empty, new List<string>(), typeof(string), TargetType.Scalar, string.Empty),
new OptionSpecification("i", string.Empty, false, string.Empty, Maybe.Just(3), Maybe.Just(4), '\0', null, string.Empty, string.Empty, new List<string>(), typeof(IEnumerable<int>), TargetType.Sequence, string.Empty)
new OptionSpecification(string.Empty, "stringvalue", false, string.Empty, Maybe.Nothing<int>(), Maybe.Nothing<int>(), '\0', null, null, string.Empty, string.Empty, new List<string>(), typeof(string), TargetType.Scalar, string.Empty),
new OptionSpecification("i", string.Empty, false, string.Empty, Maybe.Just(3), Maybe.Just(4), '\0', null, null, string.Empty, string.Empty, new List<string>(), typeof(IEnumerable<int>), TargetType.Sequence, string.Empty)
};

// Exercize system
Expand Down
4 changes: 2 additions & 2 deletions tests/CommandLine.Tests/Unit/Core/TokenizerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public void Explode_scalar_with_separator_in_odd_args_input_returns_sequence()
var expectedTokens = new[] { Token.Name("i"), Token.Value("10"), Token.Name("string-seq"),
Token.Value("aaa"), Token.Value("bb"), Token.Value("cccc"), Token.Name("switch") };
var specs = new[] { new OptionSpecification(string.Empty, "string-seq",
false, string.Empty, Maybe.Nothing<int>(), Maybe.Nothing<int>(), ',', null, string.Empty, string.Empty, new List<string>(), typeof(IEnumerable<string>), TargetType.Sequence, string.Empty)};
false, string.Empty, Maybe.Nothing<int>(), Maybe.Nothing<int>(), ',', null, null, string.Empty, string.Empty, new List<string>(), typeof(IEnumerable<string>), TargetType.Sequence, string.Empty)};

// Exercize system
var result =
Expand All @@ -44,7 +44,7 @@ public void Explode_scalar_with_separator_in_even_args_input_returns_sequence()
var expectedTokens = new[] { Token.Name("x"), Token.Name("string-seq"),
Token.Value("aaa"), Token.Value("bb"), Token.Value("cccc"), Token.Name("switch") };
var specs = new[] { new OptionSpecification(string.Empty, "string-seq",
false, string.Empty, Maybe.Nothing<int>(), Maybe.Nothing<int>(), ',', null, string.Empty, string.Empty, new List<string>(), typeof(IEnumerable<string>), TargetType.Sequence, string.Empty)};
false, string.Empty, Maybe.Nothing<int>(), Maybe.Nothing<int>(), ',', null, null, string.Empty, string.Empty, new List<string>(), typeof(IEnumerable<string>), TargetType.Sequence, string.Empty)};

// Exercize system
var result =
Expand Down
Loading