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 Shouldly;
using Spectre.Console.Cli;
@ -15,7 +18,8 @@ namespace Spectre.Console.Tests.Unit.Cli
{
[CommandOption("-f|--foo <VALUE>")]
[IntegerValueProvider(32)]
public int Foo { get; set; }
[TypeConverter(typeof(HexConverter))]
public string Foo { get; set; }
}
public sealed class IntegerValueProvider : ParameterValueProviderAttribute
@ -29,13 +33,10 @@ namespace Spectre.Console.Tests.Unit.Cli
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;
return true;
}
result = _value;
return true;
}
result = null;
@ -43,16 +44,28 @@ namespace Spectre.Console.Tests.Unit.Cli
}
}
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]
public void Should_Use_Provided_Value_If_No_Value_Was_Specified()
{
// Given
var app = new CommandAppTester();
app.SetDefaultCommand<GenericCommand<ValueProviderSettings>>();
app.Configure(config =>
{
config.PropagateExceptions();
});
app.Configure(config => config.PropagateExceptions());
// When
var result = app.Run();
@ -60,7 +73,7 @@ namespace Spectre.Console.Tests.Unit.Cli
// Then
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
var app = new CommandAppTester();
app.SetDefaultCommand<GenericCommand<ValueProviderSettings>>();
app.Configure(config =>
{
config.PropagateExceptions();
});
app.Configure(config => config.PropagateExceptions());
// When
var result = app.Run("--foo", "12");
@ -81,7 +91,7 @@ namespace Spectre.Console.Tests.Unit.Cli
// Then
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);
if (parameter.ValueProvider.TryGetValue(context, out var result))
{
result = ConvertValue(resolver, lookup, binder, parameter, result);
lookup.SetValue(parameter, result);
CommandValidator.ValidateParameter(parameter, lookup, resolver);
continue;
@ -42,16 +44,7 @@ namespace Spectre.Console.Cli
if (parameter.DefaultValue != null)
{
var value = parameter.DefaultValue?.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);
}
}
value = ConvertValue(resolver, lookup, binder, parameter, value);
binder.Bind(parameter, resolver, value);
CommandValidator.ValidateParameter(parameter, lookup, resolver);
@ -74,12 +67,6 @@ namespace Spectre.Console.Cli
}
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 is CommandOption option && option.DefaultValue != null)
@ -95,6 +82,12 @@ namespace Spectre.Console.Cli
}
else
{
var converter = GetConverter(lookup, binder, resolver, mapped.Parameter);
if (converter == null)
{
throw CommandRuntimeException.NoConverterFound(mapped.Parameter);
}
// Assign the value to the parameter.
binder.Bind(mapped.Parameter, resolver, converter.ConvertFromInvariantString(mapped.Value));
}
@ -119,6 +112,20 @@ namespace Spectre.Console.Cli
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")]
private static TypeConverter? GetConverter(CommandValueLookup lookup, CommandValueBinder binder, ITypeResolver resolver, CommandParameter parameter)
{