diff --git a/CliFx.Tests/ArgumentBindingSpecs.cs b/CliFx.Tests/ArgumentBindingSpecs.cs index eb8b462..1d6873f 100644 --- a/CliFx.Tests/ArgumentBindingSpecs.cs +++ b/CliFx.Tests/ArgumentBindingSpecs.cs @@ -933,7 +933,7 @@ namespace CliFx.Tests } [Fact] - public void Property_annotated_as_a_required_option_must_always_be_bound_to_some_value() + public void Property_annotated_as_a_required_option_must_always_be_set() { // Arrange var schema = ApplicationSchema.Resolve(new[] {typeof(RequiredOptionCommand)}); @@ -946,6 +946,20 @@ namespace CliFx.Tests Assert.Throws(() => schema.InitializeEntryPoint(input)); } + [Fact] + public void Property_annotated_as_a_required_option_must_always_be_bound_to_some_value() + { + // Arrange + var schema = ApplicationSchema.Resolve(new[] {typeof(RequiredOptionCommand)}); + + var input = new CommandLineInputBuilder() + .AddOption(nameof(RequiredOptionCommand.OptionB)) + .Build(); + + // Act & assert + Assert.Throws(() => schema.InitializeEntryPoint(input)); + } + [Fact] public void Property_annotated_as_parameter_is_bound_directly_from_argument_value_according_to_the_order() { diff --git a/CliFx/Domain/CommandSchema.cs b/CliFx/Domain/CommandSchema.cs index cdc201b..682db9c 100644 --- a/CliFx/Domain/CommandSchema.cs +++ b/CliFx/Domain/CommandSchema.cs @@ -97,15 +97,15 @@ namespace CliFx.Domain var unsetRequiredOptions = Options.Where(o => o.IsRequired).ToList(); // Environment variables - foreach (var environmentVariable in environmentVariables) + foreach (var (name, value) in environmentVariables) { - var option = Options.FirstOrDefault(o => o.MatchesEnvironmentVariableName(environmentVariable.Key)); + var option = Options.FirstOrDefault(o => o.MatchesEnvironmentVariableName(name)); if (option != null) { var values = option.IsScalar - ? new[] {environmentVariable.Value} - : environmentVariable.Value.Split(Path.PathSeparator); + ? new[] {value} + : value.Split(Path.PathSeparator); option.Inject(command, values); unsetRequiredOptions.Remove(option); @@ -122,12 +122,14 @@ namespace CliFx.Domain if (inputs.Any()) { - option.Inject(command, inputs.SelectMany(i => i.Values).ToArray()); + var inputValues = inputs.SelectMany(i => i.Values).ToArray(); + option.Inject(command, inputValues); foreach (var input in inputs) remainingOptionInputs.Remove(input); - unsetRequiredOptions.Remove(option); + if (inputValues.Any()) + unsetRequiredOptions.Remove(option); } } diff --git a/CliFx/Internal/Polyfills.cs b/CliFx/Internal/Polyfills.cs index 126c272..9070038 100644 --- a/CliFx/Internal/Polyfills.cs +++ b/CliFx/Internal/Polyfills.cs @@ -1,18 +1,20 @@ // ReSharper disable CheckNamespace -#if NET45 || NETSTANDARD2_0 -using System.Collections.Generic; -using System.Text; +// Polyfills to bridge the missing APIs in older versions of the framework/standard. -namespace CliFx.Internal +#if NETSTANDARD2_0 || NET45 +namespace System.Collections.Generic { - internal static class Polyfills + internal static class Extensions { - public static TValue GetValueOrDefault(this IReadOnlyDictionary self, TKey key) => - self.TryGetValue(key, out var value) ? value : default; + public static void Deconstruct(this KeyValuePair pair, out TKey key, out TValue value) + { + key = pair.Key; + value = pair.Value; + } - public static StringBuilder AppendJoin(this StringBuilder self, string separator, IEnumerable items) => - self.Append(string.Join(separator, items)); + public static TValue GetValueOrDefault(this IReadOnlyDictionary dic, TKey key) => + dic.TryGetValue(key, out var result) ? result! : default!; } } #endif \ No newline at end of file