mirror of
https://github.com/Tyrrrz/CliFx.git
synced 2025-10-25 15:19:17 +00:00
Custom value converters (#81)
This commit is contained in:
committed by
GitHub
parent
5e53107def
commit
6a38c04c11
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Threading.Tasks;
|
||||
using CliFx.Tests.Commands;
|
||||
using CliFx.Tests.Commands.Converters;
|
||||
using CliFx.Tests.Internal;
|
||||
using FluentAssertions;
|
||||
using Xunit;
|
||||
@@ -1348,5 +1349,70 @@ namespace CliFx.Tests
|
||||
|
||||
_output.WriteLine(stdErr.GetString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Property_of_custom_type_is_bound_when_the_valid_converter_type_is_specified()
|
||||
{
|
||||
// Arrange
|
||||
const string foo = "foo";
|
||||
|
||||
var (console, stdOut, _) = VirtualConsole.CreateBuffered();
|
||||
|
||||
var application = new CliApplicationBuilder()
|
||||
.AddCommand<CommandWithParameterOfCustomType>()
|
||||
.UseConsole(console)
|
||||
.Build();
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(new[]
|
||||
{
|
||||
"cmd", "--prop", foo
|
||||
});
|
||||
|
||||
// Assert
|
||||
exitCode.Should().Be(0);
|
||||
|
||||
var commandInstance = stdOut.GetString().DeserializeJson<CommandWithParameterOfCustomType>();
|
||||
|
||||
commandInstance.Should().BeEquivalentTo(new CommandWithParameterOfCustomType()
|
||||
{
|
||||
MyProperty = (CustomType) new CustomTypeConverter().ConvertFrom(foo)
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Enumerable_of_the_custom_type_is_bound_when_the_valid_converter_type_is_specified()
|
||||
{
|
||||
// Arrange
|
||||
string foo = "foo";
|
||||
string bar = "bar";
|
||||
|
||||
var (console, stdOut, _) = VirtualConsole.CreateBuffered();
|
||||
|
||||
var application = new CliApplicationBuilder()
|
||||
.AddCommand<CommandWithEnumerableOfParametersOfCustomType>()
|
||||
.UseConsole(console)
|
||||
.Build();
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(new[]
|
||||
{
|
||||
"cmd", "--prop", foo, bar
|
||||
});
|
||||
|
||||
// Assert
|
||||
exitCode.Should().Be(0);
|
||||
|
||||
var commandInstance = stdOut.GetString().DeserializeJson<CommandWithEnumerableOfParametersOfCustomType>();
|
||||
|
||||
commandInstance.Should().BeEquivalentTo(new CommandWithEnumerableOfParametersOfCustomType()
|
||||
{
|
||||
MyProperties = new List<CustomType>
|
||||
{
|
||||
(CustomType) new CustomTypeConverter().ConvertFrom(foo),
|
||||
(CustomType) new CustomTypeConverter().ConvertFrom(bar)
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
27
CliFx.Tests/Commands/CommandWithParameterOfCustomType.cs
Normal file
27
CliFx.Tests/Commands/CommandWithParameterOfCustomType.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using CliFx.Attributes;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using CliFx.Tests.Commands.Converters;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace CliFx.Tests.Commands
|
||||
{
|
||||
public class CustomType
|
||||
{
|
||||
public int SomeValue { get; set; }
|
||||
}
|
||||
|
||||
[Command("cmd")]
|
||||
public class CommandWithParameterOfCustomType : SelfSerializeCommandBase
|
||||
{
|
||||
[CommandOption("prop", Converter = typeof(CustomTypeConverter))]
|
||||
public CustomType? MyProperty { get; set; }
|
||||
}
|
||||
|
||||
[Command("cmd")]
|
||||
public class CommandWithEnumerableOfParametersOfCustomType : SelfSerializeCommandBase
|
||||
{
|
||||
[CommandOption("prop", Converter = typeof(CustomTypeConverter))]
|
||||
public List<CustomType>? MyProperties { get; set; }
|
||||
}
|
||||
}
|
||||
8
CliFx.Tests/Commands/Converters/CustomTypeConverter.cs
Normal file
8
CliFx.Tests/Commands/Converters/CustomTypeConverter.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace CliFx.Tests.Commands.Converters
|
||||
{
|
||||
public class CustomTypeConverter : IArgumentValueConverter
|
||||
{
|
||||
public object ConvertFrom(string value) =>
|
||||
new CustomType { SomeValue = value.Length };
|
||||
}
|
||||
}
|
||||
@@ -37,6 +37,11 @@ namespace CliFx.Attributes
|
||||
/// </summary>
|
||||
public string? EnvironmentVariableName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Type of a converter to use for the option value evaluating.
|
||||
/// </summary>
|
||||
public Type? Converter { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes an instance of <see cref="CommandOptionAttribute"/>.
|
||||
/// </summary>
|
||||
|
||||
@@ -26,6 +26,11 @@ namespace CliFx.Attributes
|
||||
/// </summary>
|
||||
public string? Description { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Type of a converter to use for the parameter value evaluating.
|
||||
/// </summary>
|
||||
public Type? Converter { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes an instance of <see cref="CommandParameterAttribute"/>.
|
||||
/// </summary>
|
||||
|
||||
@@ -17,10 +17,13 @@ namespace CliFx.Domain
|
||||
|
||||
public bool IsScalar => TryGetEnumerableArgumentUnderlyingType() == null;
|
||||
|
||||
protected CommandArgumentSchema(PropertyInfo? property, string? description)
|
||||
protected Type? Converter { get; set; }
|
||||
|
||||
protected CommandArgumentSchema(PropertyInfo? property, string? description, Type? converter = null)
|
||||
{
|
||||
Property = property;
|
||||
Description = description;
|
||||
Converter = converter;
|
||||
}
|
||||
|
||||
private Type? TryGetEnumerableArgumentUnderlyingType() =>
|
||||
@@ -62,6 +65,9 @@ namespace CliFx.Domain
|
||||
var parseMethod = targetType.GetStaticParseMethod();
|
||||
if (parseMethod != null)
|
||||
return parseMethod.Invoke(null, new object[] {value!});
|
||||
|
||||
if (Converter != null)
|
||||
return Converter.InstanceOf<IArgumentValueConverter>().ConvertFrom(value!);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -23,8 +23,9 @@ namespace CliFx.Domain
|
||||
char? shortName,
|
||||
string? environmentVariableName,
|
||||
bool isRequired,
|
||||
string? description)
|
||||
: base(property, description)
|
||||
string? description,
|
||||
Type? converter = null)
|
||||
: base(property, description, converter)
|
||||
{
|
||||
Name = name;
|
||||
ShortName = shortName;
|
||||
@@ -97,7 +98,8 @@ namespace CliFx.Domain
|
||||
attribute.ShortName,
|
||||
attribute.EnvironmentVariableName,
|
||||
attribute.IsRequired,
|
||||
attribute.Description
|
||||
attribute.Description,
|
||||
attribute.Converter
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using CliFx.Attributes;
|
||||
@@ -11,8 +12,8 @@ namespace CliFx.Domain
|
||||
|
||||
public string Name { get; }
|
||||
|
||||
public CommandParameterSchema(PropertyInfo? property, int order, string name, string? description)
|
||||
: base(property, description)
|
||||
public CommandParameterSchema(PropertyInfo? property, int order, string name, string? description, Type? converter = null)
|
||||
: base(property, description, converter)
|
||||
{
|
||||
Order = order;
|
||||
Name = name;
|
||||
@@ -50,7 +51,8 @@ namespace CliFx.Domain
|
||||
property,
|
||||
attribute.Order,
|
||||
name,
|
||||
attribute.Description
|
||||
attribute.Description,
|
||||
attribute.Converter
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
13
CliFx/IArgumentValueConverter.cs
Normal file
13
CliFx/IArgumentValueConverter.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
namespace CliFx
|
||||
{
|
||||
/// <summary>
|
||||
/// Used as an interface for implementing custom parameter/option converters.
|
||||
/// </summary>
|
||||
public interface IArgumentValueConverter
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts an input value to object of required type.
|
||||
/// </summary>
|
||||
public object ConvertFrom(string value);
|
||||
}
|
||||
}
|
||||
@@ -56,5 +56,10 @@ namespace CliFx.Internal.Extensions
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
public static T InstanceOf<T>(this Type type) =>
|
||||
type.Implements(typeof(T))
|
||||
? (T) Activator.CreateInstance(type)
|
||||
: throw new ArgumentException();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user