mirror of
				https://github.com/nsnail/spectre.console.git
				synced 2025-10-31 09:09:25 +08:00 
			
		
		
		
	Add support for arrays in [DefaultValue] attributes (#1164)
Fixes #1163
This commit is contained in:
		| @@ -65,6 +65,11 @@ internal sealed class CommandValueBinder | ||||
|  | ||||
|     private object GetArray(CommandParameter parameter, object? value) | ||||
|     { | ||||
|         if (value is Array) | ||||
|         { | ||||
|             return value; | ||||
|         } | ||||
|  | ||||
|         // Add a new item to the array | ||||
|         var array = (Array?)_lookup.GetValue(parameter); | ||||
|         Array newArray; | ||||
|   | ||||
| @@ -133,13 +133,33 @@ internal static class CommandValueResolver | ||||
|             var (converter, _) = GetConverter(lookup, binder, resolver, parameter); | ||||
|             if (converter != null) | ||||
|             { | ||||
|                 result = converter.ConvertFrom(result); | ||||
|                 result = result is Array array ? ConvertArray(array, converter) : converter.ConvertFrom(result); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     private static Array ConvertArray(Array sourceArray, TypeConverter converter) | ||||
|     { | ||||
|         Array? targetArray = null; | ||||
|         for (var i = 0; i < sourceArray.Length; i++) | ||||
|         { | ||||
|             var item = sourceArray.GetValue(i); | ||||
|             if (item != null) | ||||
|             { | ||||
|                 var converted = converter.ConvertFrom(item); | ||||
|                 if (converted != null) | ||||
|                 { | ||||
|                     targetArray ??= Array.CreateInstance(converted.GetType(), sourceArray.Length); | ||||
|                     targetArray.SetValue(converted, i); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return targetArray ?? sourceArray; | ||||
|     } | ||||
|  | ||||
|     [SuppressMessage("Style", "IDE0019:Use pattern matching", Justification = "It's OK")] | ||||
|     private static (TypeConverter? Converter, ConstructorInfo? StringConstructor) GetConverter(CommandValueLookup lookup, CommandValueBinder binder, ITypeResolver resolver, CommandParameter parameter) | ||||
|     { | ||||
|   | ||||
| @@ -348,7 +348,17 @@ internal static class HelpWriter | ||||
|             var columns = new List<string> { GetOptionParts(option) }; | ||||
|             if (defaultValueColumn) | ||||
|             { | ||||
|                 columns.Add(option.DefaultValue == null ? " " : $"[bold]{option.DefaultValue.ToString().EscapeMarkup()}[/]"); | ||||
|                 static string Bold(object obj) => $"[bold]{obj.ToString().EscapeMarkup()}[/]"; | ||||
|  | ||||
|                 var defaultValue = option.DefaultValue switch | ||||
|                 { | ||||
|                     null => " ", | ||||
|                     "" => " ", | ||||
|                     Array { Length: 0 } => " ", | ||||
|                     Array array => string.Join(", ", array.Cast<object>().Select(Bold)), | ||||
|                     _ => Bold(option.DefaultValue), | ||||
|                 }; | ||||
|                 columns.Add(defaultValue); | ||||
|             } | ||||
|  | ||||
|             columns.Add(option.Description?.TrimEnd('.') ?? " "); | ||||
|   | ||||
| @@ -9,4 +9,9 @@ public class LionSettings : CatSettings | ||||
|     [CommandOption("-c <CHILDREN>")] | ||||
|     [Description("The number of children the lion has.")] | ||||
|     public int Children { get; set; } | ||||
|  | ||||
|     [CommandOption("-d <DAY>")] | ||||
|     [Description("The days the lion goes hunting.")] | ||||
|     [DefaultValue(new[] { DayOfWeek.Monday, DayOfWeek.Thursday })] | ||||
|     public DayOfWeek[] HuntDays { get; set; } | ||||
| } | ||||
|   | ||||
| @@ -33,3 +33,18 @@ public sealed class RequiredArgumentWithDefaultValueSettings : CommandSettings | ||||
|     [DefaultValue("Hello World")] | ||||
|     public string Greeting { get; set; } | ||||
| } | ||||
|  | ||||
| public sealed class OptionWithArrayOfEnumDefaultValueSettings : CommandSettings | ||||
| { | ||||
|     [CommandOption("--days")] | ||||
|     [DefaultValue(new[] { DayOfWeek.Sunday, DayOfWeek.Saturday })] | ||||
|     public DayOfWeek[] Days { get; set; } | ||||
| } | ||||
|  | ||||
| public sealed class OptionWithArrayOfStringDefaultValueAndTypeConverterSettings : CommandSettings | ||||
| { | ||||
|     [CommandOption("--numbers")] | ||||
|     [DefaultValue(new[] { "2", "3" })] | ||||
|     [TypeConverter(typeof(StringToIntegerConverter))] | ||||
|     public int[] Numbers { get; set; } | ||||
| } | ||||
|   | ||||
| @@ -10,8 +10,9 @@ ARGUMENTS: | ||||
|  | ||||
| OPTIONS: | ||||
|                              DEFAULT | ||||
|     -h, --help                          Prints help information | ||||
|     -a, --alive                         Indicates whether or not the animal is alive | ||||
|     -h, --help                                   Prints help information | ||||
|     -a, --alive                                  Indicates whether or not the animal is alive | ||||
|     -n, --name <VALUE> | ||||
|         --agility <VALUE>    10         The agility between 0 and 100 | ||||
|     -c <CHILDREN>                       The number of children the lion has | ||||
|         --agility <VALUE>    10                  The agility between 0 and 100 | ||||
|     -c <CHILDREN>                                The number of children the lion has | ||||
|     -d <DAY>                 Monday, Thursday    The days the lion goes hunting | ||||
| @@ -13,8 +13,9 @@ ARGUMENTS: | ||||
|  | ||||
| OPTIONS: | ||||
|                              DEFAULT | ||||
|     -h, --help                          Prints help information | ||||
|     -a, --alive                         Indicates whether or not the animal is alive | ||||
|     -h, --help                                   Prints help information | ||||
|     -a, --alive                                  Indicates whether or not the animal is alive | ||||
|     -n, --name <VALUE> | ||||
|         --agility <VALUE>    10         The agility between 0 and 100 | ||||
|     -c <CHILDREN>                       The number of children the lion has | ||||
|         --agility <VALUE>    10                  The agility between 0 and 100 | ||||
|     -c <CHILDREN>                                The number of children the lion has | ||||
|     -d <DAY>                 Monday, Thursday    The days the lion goes hunting | ||||
| @@ -10,8 +10,9 @@ ARGUMENTS: | ||||
|  | ||||
| OPTIONS: | ||||
|                              DEFAULT | ||||
|     -h, --help                          Prints help information | ||||
|     -a, --alive                         Indicates whether or not the animal is alive | ||||
|     -h, --help                                   Prints help information | ||||
|     -a, --alive                                  Indicates whether or not the animal is alive | ||||
|     -n, --name <VALUE> | ||||
|         --agility <VALUE>    10         The agility between 0 and 100 | ||||
|     -c <CHILDREN>                       The number of children the lion has | ||||
|         --agility <VALUE>    10                  The agility between 0 and 100 | ||||
|     -c <CHILDREN>                                The number of children the lion has | ||||
|     -d <DAY>                 Monday, Thursday    The days the lion goes hunting | ||||
| @@ -10,11 +10,12 @@ ARGUMENTS: | ||||
|  | ||||
| OPTIONS: | ||||
|                              DEFAULT | ||||
|     -h, --help                          Prints help information | ||||
|     -a, --alive                         Indicates whether or not the animal is alive | ||||
|     -h, --help                                   Prints help information | ||||
|     -a, --alive                                  Indicates whether or not the animal is alive | ||||
|     -n, --name <VALUE> | ||||
|         --agility <VALUE>    10         The agility between 0 and 100 | ||||
|     -c <CHILDREN>                       The number of children the lion has | ||||
|         --agility <VALUE>    10                  The agility between 0 and 100 | ||||
|     -c <CHILDREN>                                The number of children the lion has | ||||
|     -d <DAY>                 Monday, Thursday    The days the lion goes hunting | ||||
|  | ||||
| COMMANDS: | ||||
|     giraffe <LENGTH>    The giraffe command | ||||
| @@ -8,5 +8,7 @@ ARGUMENTS: | ||||
|     <TEETH>    The number of teeth the lion has | ||||
|  | ||||
| OPTIONS: | ||||
|     -h, --help       Prints help information | ||||
|     -c <CHILDREN>    The number of children the lion has | ||||
|                      DEFAULT | ||||
|     -h, --help                           Prints help information | ||||
|     -c <CHILDREN>                        The number of children the lion has | ||||
|     -d <DAY>         Monday, Thursday    The days the lion goes hunting | ||||
| @@ -396,6 +396,50 @@ public sealed partial class CommandAppTests | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     [Fact] | ||||
|     public void Should_Assign_Array_Default_Value_To_Command_Option() | ||||
|     { | ||||
|         // Given | ||||
|         var app = new CommandAppTester(); | ||||
|         app.SetDefaultCommand<GenericCommand<OptionWithArrayOfEnumDefaultValueSettings>>(); | ||||
|         app.Configure(config => | ||||
|         { | ||||
|             config.PropagateExceptions(); | ||||
|         }); | ||||
|  | ||||
|         // When | ||||
|         var result = app.Run(Array.Empty<string>()); | ||||
|  | ||||
|         // Then | ||||
|         result.ExitCode.ShouldBe(0); | ||||
|         result.Settings.ShouldBeOfType<OptionWithArrayOfEnumDefaultValueSettings>().And(settings => | ||||
|         { | ||||
|             settings.Days.ShouldBe(new[] { DayOfWeek.Sunday, DayOfWeek.Saturday }); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     [Fact] | ||||
|     public void Should_Assign_Array_Default_Value_To_Command_Option_Using_Converter_If_Necessary() | ||||
|     { | ||||
|         // Given | ||||
|         var app = new CommandAppTester(); | ||||
|         app.SetDefaultCommand<GenericCommand<OptionWithArrayOfStringDefaultValueAndTypeConverterSettings>>(); | ||||
|         app.Configure(config => | ||||
|         { | ||||
|             config.PropagateExceptions(); | ||||
|         }); | ||||
|  | ||||
|         // When | ||||
|         var result = app.Run(Array.Empty<string>()); | ||||
|  | ||||
|         // Then | ||||
|         result.ExitCode.ShouldBe(0); | ||||
|         result.Settings.ShouldBeOfType<OptionWithArrayOfStringDefaultValueAndTypeConverterSettings>().And(settings => | ||||
|         { | ||||
|             settings.Numbers.ShouldBe(new[] { 2, 3 }); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     [Fact] | ||||
|     public void Should_Throw_If_Required_Argument_Have_Default_Value() | ||||
|     { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Cédric Luthi
					Cédric Luthi