Play nice with type converters

This commit is contained in:
Patrik Svensson 2021-05-09 08:50:39 +02:00 committed by Phil Scott
parent 1dd1945297
commit 4d88a6ab69
2 changed files with 50 additions and 33 deletions

View File

@ -1,3 +1,6 @@
using System;
using System.ComponentModel;
using System.Globalization;
using System.Linq; using System.Linq;
using Shouldly; using Shouldly;
using Spectre.Console.Cli; using Spectre.Console.Cli;
@ -15,7 +18,8 @@ namespace Spectre.Console.Tests.Unit.Cli
{ {
[CommandOption("-f|--foo <VALUE>")] [CommandOption("-f|--foo <VALUE>")]
[IntegerValueProvider(32)] [IntegerValueProvider(32)]
public int Foo { get; set; } [TypeConverter(typeof(HexConverter))]
public string Foo { get; set; }
} }
public sealed class IntegerValueProvider : ParameterValueProviderAttribute public sealed class IntegerValueProvider : ParameterValueProviderAttribute
@ -28,31 +32,40 @@ namespace Spectre.Console.Tests.Unit.Cli
} }
public override bool TryGetValue(CommandParameterContext context, out object result) public override bool TryGetValue(CommandParameterContext context, out object result)
{
if (context.Parameter.ParameterType == typeof(int))
{ {
if (context.Value == null) if (context.Value == null)
{ {
result = _value; result = _value;
return true; return true;
} }
}
result = null; result = null;
return false; return false;
} }
} }
public sealed class HexConverter : TypeConverter
{
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is int integer)
{
return integer.ToString("X");
}
return value is string stringValue && int.TryParse(stringValue, out var intValue)
? intValue.ToString("X")
: base.ConvertFrom(context, culture, value);
}
}
[Fact] [Fact]
public void Should_Use_Provided_Value_If_No_Value_Was_Specified() public void Should_Use_Provided_Value_If_No_Value_Was_Specified()
{ {
// Given // Given
var app = new CommandAppTester(); var app = new CommandAppTester();
app.SetDefaultCommand<GenericCommand<ValueProviderSettings>>(); app.SetDefaultCommand<GenericCommand<ValueProviderSettings>>();
app.Configure(config => app.Configure(config => config.PropagateExceptions());
{
config.PropagateExceptions();
});
// When // When
var result = app.Run(); var result = app.Run();
@ -60,7 +73,7 @@ namespace Spectre.Console.Tests.Unit.Cli
// Then // Then
result.Settings.ShouldBeOfType<ValueProviderSettings>().And(settings => result.Settings.ShouldBeOfType<ValueProviderSettings>().And(settings =>
{ {
settings.Foo.ShouldBe(32); settings.Foo.ShouldBe("20"); // 32 is 0x20
}); });
} }
@ -70,10 +83,7 @@ namespace Spectre.Console.Tests.Unit.Cli
// Given // Given
var app = new CommandAppTester(); var app = new CommandAppTester();
app.SetDefaultCommand<GenericCommand<ValueProviderSettings>>(); app.SetDefaultCommand<GenericCommand<ValueProviderSettings>>();
app.Configure(config => app.Configure(config => config.PropagateExceptions());
{
config.PropagateExceptions();
});
// When // When
var result = app.Run("--foo", "12"); var result = app.Run("--foo", "12");
@ -81,7 +91,7 @@ namespace Spectre.Console.Tests.Unit.Cli
// Then // Then
result.Settings.ShouldBeOfType<ValueProviderSettings>().And(settings => result.Settings.ShouldBeOfType<ValueProviderSettings>().And(settings =>
{ {
settings.Foo.ShouldBe(12); settings.Foo.ShouldBe("C"); // 12 is 0xC
}); });
} }
} }

View File

@ -24,6 +24,8 @@ namespace Spectre.Console.Cli
var context = new CommandParameterContext(parameter, resolver, null); var context = new CommandParameterContext(parameter, resolver, null);
if (parameter.ValueProvider.TryGetValue(context, out var result)) if (parameter.ValueProvider.TryGetValue(context, out var result))
{ {
result = ConvertValue(resolver, lookup, binder, parameter, result);
lookup.SetValue(parameter, result); lookup.SetValue(parameter, result);
CommandValidator.ValidateParameter(parameter, lookup, resolver); CommandValidator.ValidateParameter(parameter, lookup, resolver);
continue; continue;
@ -42,16 +44,7 @@ namespace Spectre.Console.Cli
if (parameter.DefaultValue != null) if (parameter.DefaultValue != null)
{ {
var value = parameter.DefaultValue?.Value; var value = parameter.DefaultValue?.Value;
value = ConvertValue(resolver, lookup, binder, parameter, value);
// Need to convert the default value?
if (value != null && value.GetType() != parameter.ParameterType)
{
var converter = GetConverter(lookup, binder, resolver, parameter);
if (converter != null)
{
value = converter.ConvertFrom(value);
}
}
binder.Bind(parameter, resolver, value); binder.Bind(parameter, resolver, value);
CommandValidator.ValidateParameter(parameter, lookup, resolver); CommandValidator.ValidateParameter(parameter, lookup, resolver);
@ -74,12 +67,6 @@ namespace Spectre.Console.Cli
} }
else else
{ {
var converter = GetConverter(lookup, binder, resolver, mapped.Parameter);
if (converter == null)
{
throw CommandRuntimeException.NoConverterFound(mapped.Parameter);
}
if (mapped.Parameter.IsFlagValue() && mapped.Value == null) if (mapped.Parameter.IsFlagValue() && mapped.Value == null)
{ {
if (mapped.Parameter is CommandOption option && option.DefaultValue != null) if (mapped.Parameter is CommandOption option && option.DefaultValue != null)
@ -95,6 +82,12 @@ namespace Spectre.Console.Cli
} }
else else
{ {
var converter = GetConverter(lookup, binder, resolver, mapped.Parameter);
if (converter == null)
{
throw CommandRuntimeException.NoConverterFound(mapped.Parameter);
}
// Assign the value to the parameter. // Assign the value to the parameter.
binder.Bind(mapped.Parameter, resolver, converter.ConvertFromInvariantString(mapped.Value)); binder.Bind(mapped.Parameter, resolver, converter.ConvertFromInvariantString(mapped.Value));
} }
@ -119,6 +112,20 @@ namespace Spectre.Console.Cli
return lookup; return lookup;
} }
private static object? ConvertValue(ITypeResolver resolver, CommandValueLookup lookup, CommandValueBinder binder, CommandParameter parameter, object? result)
{
if (result != null && result.GetType() != parameter.ParameterType)
{
var converter = GetConverter(lookup, binder, resolver, parameter);
if (converter != null)
{
result = converter.ConvertFrom(result);
}
}
return result;
}
[SuppressMessage("Style", "IDE0019:Use pattern matching", Justification = "It's OK")] [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? GetConverter(CommandValueLookup lookup, CommandValueBinder binder, ITypeResolver resolver, CommandParameter parameter)
{ {