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.Globalization;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using CliFx.Tests.Commands;
|
using CliFx.Tests.Commands;
|
||||||
|
using CliFx.Tests.Commands.Converters;
|
||||||
using CliFx.Tests.Internal;
|
using CliFx.Tests.Internal;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
@@ -1348,5 +1349,70 @@ namespace CliFx.Tests
|
|||||||
|
|
||||||
_output.WriteLine(stdErr.GetString());
|
_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>
|
/// </summary>
|
||||||
public string? EnvironmentVariableName { get; set; }
|
public string? EnvironmentVariableName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Type of a converter to use for the option value evaluating.
|
||||||
|
/// </summary>
|
||||||
|
public Type? Converter { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes an instance of <see cref="CommandOptionAttribute"/>.
|
/// Initializes an instance of <see cref="CommandOptionAttribute"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -26,6 +26,11 @@ namespace CliFx.Attributes
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public string? Description { get; set; }
|
public string? Description { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Type of a converter to use for the parameter value evaluating.
|
||||||
|
/// </summary>
|
||||||
|
public Type? Converter { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes an instance of <see cref="CommandParameterAttribute"/>.
|
/// Initializes an instance of <see cref="CommandParameterAttribute"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -17,10 +17,13 @@ namespace CliFx.Domain
|
|||||||
|
|
||||||
public bool IsScalar => TryGetEnumerableArgumentUnderlyingType() == null;
|
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;
|
Property = property;
|
||||||
Description = description;
|
Description = description;
|
||||||
|
Converter = converter;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Type? TryGetEnumerableArgumentUnderlyingType() =>
|
private Type? TryGetEnumerableArgumentUnderlyingType() =>
|
||||||
@@ -62,6 +65,9 @@ namespace CliFx.Domain
|
|||||||
var parseMethod = targetType.GetStaticParseMethod();
|
var parseMethod = targetType.GetStaticParseMethod();
|
||||||
if (parseMethod != null)
|
if (parseMethod != null)
|
||||||
return parseMethod.Invoke(null, new object[] {value!});
|
return parseMethod.Invoke(null, new object[] {value!});
|
||||||
|
|
||||||
|
if (Converter != null)
|
||||||
|
return Converter.InstanceOf<IArgumentValueConverter>().ConvertFrom(value!);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -23,8 +23,9 @@ namespace CliFx.Domain
|
|||||||
char? shortName,
|
char? shortName,
|
||||||
string? environmentVariableName,
|
string? environmentVariableName,
|
||||||
bool isRequired,
|
bool isRequired,
|
||||||
string? description)
|
string? description,
|
||||||
: base(property, description)
|
Type? converter = null)
|
||||||
|
: base(property, description, converter)
|
||||||
{
|
{
|
||||||
Name = name;
|
Name = name;
|
||||||
ShortName = shortName;
|
ShortName = shortName;
|
||||||
@@ -97,7 +98,8 @@ namespace CliFx.Domain
|
|||||||
attribute.ShortName,
|
attribute.ShortName,
|
||||||
attribute.EnvironmentVariableName,
|
attribute.EnvironmentVariableName,
|
||||||
attribute.IsRequired,
|
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.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using CliFx.Attributes;
|
using CliFx.Attributes;
|
||||||
@@ -11,8 +12,8 @@ namespace CliFx.Domain
|
|||||||
|
|
||||||
public string Name { get; }
|
public string Name { get; }
|
||||||
|
|
||||||
public CommandParameterSchema(PropertyInfo? property, int order, string name, string? description)
|
public CommandParameterSchema(PropertyInfo? property, int order, string name, string? description, Type? converter = null)
|
||||||
: base(property, description)
|
: base(property, description, converter)
|
||||||
{
|
{
|
||||||
Order = order;
|
Order = order;
|
||||||
Name = name;
|
Name = name;
|
||||||
@@ -50,7 +51,8 @@ namespace CliFx.Domain
|
|||||||
property,
|
property,
|
||||||
attribute.Order,
|
attribute.Order,
|
||||||
name,
|
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;
|
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