diff --git a/src/Spectre.Console.Cli/Internal/Binding/CommandValueResolver.cs b/src/Spectre.Console.Cli/Internal/Binding/CommandValueResolver.cs
index e27702867..3b534199e 100644
--- a/src/Spectre.Console.Cli/Internal/Binding/CommandValueResolver.cs
+++ b/src/Spectre.Console.Cli/Internal/Binding/CommandValueResolver.cs
@@ -78,18 +78,26 @@ public static CommandValueLookup GetParameterValues(CommandTree? tree, ITypeReso
}
else
{
- var converter = GetConverter(lookup, binder, resolver, mapped.Parameter);
+ var (converter, stringConstructor) = GetConverter(lookup, binder, resolver, mapped.Parameter);
if (converter == null)
{
throw CommandRuntimeException.NoConverterFound(mapped.Parameter);
}
object? value;
+ var mappedValue = mapped.Value ?? string.Empty;
try
{
- value = converter.ConvertFromInvariantString(mapped.Value ?? string.Empty);
+ try
+ {
+ value = converter.ConvertFromInvariantString(mappedValue);
+ }
+ catch (NotSupportedException) when (stringConstructor != null)
+ {
+ value = stringConstructor.Invoke(new object[] { mappedValue });
+ }
}
- catch (Exception exception)
+ catch (Exception exception) when (exception is not CommandRuntimeException)
{
throw CommandRuntimeException.ConversionFailed(mapped, converter, exception);
}
@@ -122,7 +130,7 @@ public static CommandValueLookup GetParameterValues(CommandTree? tree, ITypeReso
{
if (result != null && result.GetType() != parameter.ParameterType)
{
- var converter = GetConverter(lookup, binder, resolver, parameter);
+ var (converter, _) = GetConverter(lookup, binder, resolver, parameter);
if (converter != null)
{
result = converter.ConvertFrom(result);
@@ -133,8 +141,14 @@ public static CommandValueLookup GetParameterValues(CommandTree? tree, ITypeReso
}
[SuppressMessage("Style", "IDE0019:Use pattern matching", Justification = "It's OK")]
- private static TypeConverter? GetConverter(CommandValueLookup lookup, CommandValueBinder binder, ITypeResolver resolver, CommandParameter parameter)
+ private static (TypeConverter? Converter, ConstructorInfo? StringConstructor) GetConverter(CommandValueLookup lookup, CommandValueBinder binder, ITypeResolver resolver, CommandParameter parameter)
{
+ static ConstructorInfo? GetStringConstructor(Type type)
+ {
+ var constructor = type.GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, new[] { typeof(string) }, null);
+ return constructor?.GetParameters()[0].ParameterType == typeof(string) ? constructor : null;
+ }
+
if (parameter.Converter == null)
{
if (parameter.ParameterType.IsArray)
@@ -146,12 +160,12 @@ public static CommandValueLookup GetParameterValues(CommandTree? tree, ITypeReso
throw new InvalidOperationException("Could not get element type");
}
- return TypeDescriptor.GetConverter(elementType);
+ return (TypeDescriptor.GetConverter(elementType), GetStringConstructor(elementType));
}
if (parameter.IsFlagValue())
{
- // Is the optional value instanciated?
+ // Is the optional value instantiated?
var value = lookup.GetValue(parameter) as IFlagValue;
if (value == null)
{
@@ -161,18 +175,18 @@ public static CommandValueLookup GetParameterValues(CommandTree? tree, ITypeReso
value = lookup.GetValue(parameter) as IFlagValue;
if (value == null)
{
- throw new InvalidOperationException("Could not intialize optional value.");
+ throw new InvalidOperationException("Could not initialize optional value.");
}
}
// Return a converter for the flag element type.
- return TypeDescriptor.GetConverter(value.Type);
+ return (TypeDescriptor.GetConverter(value.Type), GetStringConstructor(value.Type));
}
- return TypeDescriptor.GetConverter(parameter.ParameterType);
+ return (TypeDescriptor.GetConverter(parameter.ParameterType), GetStringConstructor(parameter.ParameterType));
}
var type = Type.GetType(parameter.Converter.ConverterTypeName);
- return resolver.Resolve(type) as TypeConverter;
+ return (resolver.Resolve(type) as TypeConverter, null);
}
}
\ No newline at end of file
diff --git a/test/Spectre.Console.Cli.Tests/Data/Settings/HorseSettings.cs b/test/Spectre.Console.Cli.Tests/Data/Settings/HorseSettings.cs
index 7231b5ebd..7a8fa77ec 100644
--- a/test/Spectre.Console.Cli.Tests/Data/Settings/HorseSettings.cs
+++ b/test/Spectre.Console.Cli.Tests/Data/Settings/HorseSettings.cs
@@ -1,7 +1,15 @@
+using System.IO;
+
namespace Spectre.Console.Tests.Data;
public class HorseSettings : MammalSettings
{
[CommandOption("-d|--day")]
public DayOfWeek Day { get; set; }
+
+ [CommandOption("--file")]
+ public FileInfo File { get; set; }
+
+ [CommandOption("--directory")]
+ public DirectoryInfo Directory { get; set; }
}
\ No newline at end of file
diff --git a/test/Spectre.Console.Cli.Tests/Expectations/Xml/Test_1.Output.verified.txt b/test/Spectre.Console.Cli.Tests/Expectations/Xml/Test_1.Output.verified.txt
index c161f0225..021426c55 100644
--- a/test/Spectre.Console.Cli.Tests/Expectations/Xml/Test_1.Output.verified.txt
+++ b/test/Spectre.Console.Cli.Tests/Expectations/Xml/Test_1.Output.verified.txt
@@ -30,6 +30,8 @@
+
+
diff --git a/test/Spectre.Console.Cli.Tests/Expectations/Xml/Test_3.Output.verified.txt b/test/Spectre.Console.Cli.Tests/Expectations/Xml/Test_3.Output.verified.txt
index cf55628ad..d59c67eb6 100644
--- a/test/Spectre.Console.Cli.Tests/Expectations/Xml/Test_3.Output.verified.txt
+++ b/test/Spectre.Console.Cli.Tests/Expectations/Xml/Test_3.Output.verified.txt
@@ -26,6 +26,8 @@
+
+
diff --git a/test/Spectre.Console.Cli.Tests/Expectations/Xml/Test_6.Output.verified.txt b/test/Spectre.Console.Cli.Tests/Expectations/Xml/Test_6.Output.verified.txt
index c0a5c45ce..bc1611801 100644
--- a/test/Spectre.Console.Cli.Tests/Expectations/Xml/Test_6.Output.verified.txt
+++ b/test/Spectre.Console.Cli.Tests/Expectations/Xml/Test_6.Output.verified.txt
@@ -32,6 +32,8 @@
Indicates whether or not the animal is alive.
+
+
diff --git a/test/Spectre.Console.Cli.Tests/Unit/CommandAppTests.TypeConverters.cs b/test/Spectre.Console.Cli.Tests/Unit/CommandAppTests.TypeConverters.cs
index a115c0e7b..38a089247 100644
--- a/test/Spectre.Console.Cli.Tests/Unit/CommandAppTests.TypeConverters.cs
+++ b/test/Spectre.Console.Cli.Tests/Unit/CommandAppTests.TypeConverters.cs
@@ -1,3 +1,5 @@
+using System.IO;
+
namespace Spectre.Console.Tests.Unit.Cli;
public sealed partial class CommandAppTests
@@ -76,5 +78,27 @@ public void Should_List_All_Valid_Enum_Values_On_Conversion_Error()
result.Output.ShouldContain(nameof(DayOfWeek.Friday));
result.Output.ShouldContain(nameof(DayOfWeek.Saturday));
}
+
+ [Fact]
+ public void Should_Convert_FileInfo_And_DirectoryInfo()
+ {
+ // Given
+ var app = new CommandAppTester();
+ app.Configure(config =>
+ {
+ config.AddCommand("horse");
+ });
+
+ // When
+ var result = app.Run(new[] { "horse", "--file", "ntp.conf", "--directory", "etc" });
+
+ // Then
+ result.ExitCode.ShouldBe(0);
+ result.Settings.ShouldBeOfType().And(horse =>
+ {
+ horse.File.Name.ShouldBe("ntp.conf");
+ horse.Directory.Name.ShouldBe("etc");
+ });
+ }
}
}