From 39a947653a2d9484d081c5494784752e64138871 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Tue, 15 Nov 2022 19:45:49 +0100 Subject: [PATCH] all default value factories should be generic --- src/Common/ArgumentBuilder.cs | 18 ++++- src/Common/OptionBuilder.cs | 30 +++++++- ...ommandLine_api_is_not_changed.approved.txt | 12 +-- .../CommandLine.cs | 39 ++-------- src/System.CommandLine/Argument.cs | 47 +----------- src/System.CommandLine/Argument{T}.cs | 76 ++++++++++++++++--- src/System.CommandLine/Option.cs | 15 ---- src/System.CommandLine/Option{T}.cs | 14 ++++ 8 files changed, 136 insertions(+), 115 deletions(-) diff --git a/src/Common/ArgumentBuilder.cs b/src/Common/ArgumentBuilder.cs index 6aa7453862..b5e01e9f60 100644 --- a/src/Common/ArgumentBuilder.cs +++ b/src/Common/ArgumentBuilder.cs @@ -17,13 +17,25 @@ public static Argument CreateArgument(Type valueType, string name = "value") var argumentType = typeof(Argument<>).MakeGenericType(valueType); #if NET6_0_OR_GREATER - var ctor = (ConstructorInfo)argumentType.GetMemberWithSameMetadataDefinitionAs(_ctor); + var ctor = (ConstructorInfo)argumentType.GetMemberWithSameMetadataDefinitionAs(_ctor); #else var ctor = argumentType.GetConstructor(new[] { typeof(string), typeof(string) }); #endif - var option = (Argument)ctor.Invoke(new object[] { name, null }); + return (Argument)ctor.Invoke(new object[] { name, null }); + } + + internal static Argument CreateArgument(ParameterInfo argsParam) + { + if (!argsParam.HasDefaultValue) + { + return CreateArgument(argsParam.ParameterType, argsParam.Name); + } + + var argumentType = typeof(Argument<>).MakeGenericType(argsParam.ParameterType); + + var ctor = argumentType.GetConstructor(new[] { typeof(string), argsParam.ParameterType, typeof(string) }); - return option; + return (Argument)ctor.Invoke(new object[] { argsParam.Name, argsParam.DefaultValue, null }); } } \ No newline at end of file diff --git a/src/Common/OptionBuilder.cs b/src/Common/OptionBuilder.cs index aafc6df100..08e0938d62 100644 --- a/src/Common/OptionBuilder.cs +++ b/src/Common/OptionBuilder.cs @@ -14,7 +14,7 @@ static OptionBuilder() _ctor = typeof(Option).GetConstructor(new[] { typeof(string), typeof(string) }); } - public static Option CreateOption(string name, Type valueType) + public static Option CreateOption(string name, Type valueType, string description = null) { var optionType = typeof(Option<>).MakeGenericType(valueType); @@ -24,8 +24,34 @@ public static Option CreateOption(string name, Type valueType) var ctor = optionType.GetConstructor(new[] { typeof(string), typeof(string) }); #endif - var option = (Option)ctor.Invoke(new object[] { name, null }); + var option = (Option)ctor.Invoke(new object[] { name, description }); return option; } + + public static Option CreateOption(string name, Type valueType, string description, Func defaultValueFactory) + { + if (defaultValueFactory == null) + { + return CreateOption(name, valueType, description); + } + + var optionType = typeof(Bridge<>).MakeGenericType(valueType); + + var ctor = optionType.GetConstructor(new[] { typeof(string), typeof(Func), typeof(string) }); + + var option = (Option)ctor.Invoke(new object[] { name, defaultValueFactory, description }); + + return option; + } + + private class Bridge : Option + { + public Bridge(string name, Func defaultValueFactory, string description) + : base(name, + () => (T)defaultValueFactory(), // this type exists only for an easy Func => Func transformation + description) + { + } + } } \ No newline at end of file diff --git a/src/System.CommandLine.ApiCompatibility.Tests/ApiCompatibilityApprovalTests.System_CommandLine_api_is_not_changed.approved.txt b/src/System.CommandLine.ApiCompatibility.Tests/ApiCompatibilityApprovalTests.System_CommandLine_api_is_not_changed.approved.txt index 0da540bf5f..d42c624ad9 100644 --- a/src/System.CommandLine.ApiCompatibility.Tests/ApiCompatibilityApprovalTests.System_CommandLine_api_is_not_changed.approved.txt +++ b/src/System.CommandLine.ApiCompatibility.Tests/ApiCompatibilityApprovalTests.System_CommandLine_api_is_not_changed.approved.txt @@ -9,17 +9,16 @@ System.CommandLine public System.Object GetDefaultValue() public ParseResult Parse(System.String commandLine) public ParseResult Parse(System.String[] args) - public System.Void SetDefaultValue(System.Object value) - public System.Void SetDefaultValueFactory(System.Func defaultValueFactory) - public System.Void SetDefaultValueFactory(System.Func defaultValueFactory) public System.String ToString() public class Argument : Argument, IValueDescriptor, System.CommandLine.Binding.IValueDescriptor .ctor() .ctor(System.String name, System.String description = null) .ctor(System.String name, Func defaultValueFactory, System.String description = null) + .ctor(System.String name, T defaultValue, System.String description = null) .ctor(Func defaultValueFactory) .ctor(System.String name, Func parse, System.Boolean isDefault = False, System.String description = null) .ctor(Func parse, System.Boolean isDefault = False) + public System.Boolean HasDefaultValue { get; } public System.Type ValueType { get; } public Argument AcceptLegalFileNamesOnly() public Argument AcceptLegalFilePathsOnly() @@ -28,6 +27,9 @@ System.CommandLine public Argument AddCompletions(System.Func> completionsDelegate) public Argument AddCompletions(System.Func> completionsDelegate) public Argument AddValidator(System.Action validate) + public System.Void SetDefaultValue(T value) + public System.Void SetDefaultValueFactory(Func defaultValueFactory) + public System.Void SetDefaultValueFactory(Func defaultValueFactory) public struct ArgumentArity : System.ValueType, System.IEquatable public static ArgumentArity ExactlyOne { get; } public static ArgumentArity OneOrMore { get; } @@ -197,8 +199,6 @@ System.CommandLine public System.Boolean HasAliasIgnoringPrefix(System.String alias) public ParseResult Parse(System.String commandLine) public ParseResult Parse(System.String[] args) - public System.Void SetDefaultValue(System.Object value) - public System.Void SetDefaultValueFactory(System.Func defaultValueFactory) public class Option : Option, IValueDescriptor, System.CommandLine.Binding.IValueDescriptor .ctor(System.String name, System.String description = null) .ctor(System.String[] aliases, System.String description = null) @@ -213,6 +213,8 @@ System.CommandLine public Option AddCompletions(System.Func> completionsDelegate) public Option AddCompletions(System.Func> completionsDelegate) public Option AddValidator(System.Action validate) + public System.Void SetDefaultValue(T value) + public System.Void SetDefaultValueFactory(Func defaultValueFactory) public static class OptionValidation public static Option AcceptExistingOnly(this Option option) public static Option AcceptExistingOnly(this Option option) diff --git a/src/System.CommandLine.DragonFruit/CommandLine.cs b/src/System.CommandLine.DragonFruit/CommandLine.cs index a5f56e30d3..e9c407aa87 100644 --- a/src/System.CommandLine.DragonFruit/CommandLine.cs +++ b/src/System.CommandLine.DragonFruit/CommandLine.cs @@ -165,21 +165,7 @@ public static void ConfigureFromMethod( if (method.GetParameters().FirstOrDefault(p => _argumentParameterNames.Contains(p.Name)) is { } argsParam) { - var argument = ArgumentBuilder.CreateArgument(argsParam.ParameterType, argsParam.Name); - - if (argsParam.HasDefaultValue) - { - if (argsParam.DefaultValue is not null) - { - argument.SetDefaultValue(argsParam.DefaultValue); - } - else - { - argument.SetDefaultValueFactory(() => null); - } - } - - command.AddArgument(argument); + command.AddArgument(ArgumentBuilder.CreateArgument(argsParam)); } command.Handler = CommandHandler.Create(method, target); @@ -285,24 +271,11 @@ public static IEnumerable