Add extensibility point for injecting custom option converters

Closes #19
This commit is contained in:
Alexey Golub
2019-08-26 20:10:37 +03:00
parent ab57a103d1
commit 89aba39964
5 changed files with 55 additions and 5 deletions

View File

@@ -31,6 +31,7 @@ namespace CliFx.Tests
.UseDescription("test")
.UseConsole(new VirtualConsole(TextWriter.Null))
.UseCommandFactory(schema => (ICommand) Activator.CreateInstance(schema.Type))
.UseCommandOptionInputConverter(new CommandOptionInputConverter())
.Build();
}

View File

@@ -25,6 +25,7 @@ namespace CliFx
private string _description;
private IConsole _console;
private ICommandFactory _commandFactory;
private ICommandOptionInputConverter _commandOptionInputConverter;
/// <inheritdoc />
public ICliApplicationBuilder AddCommand(Type commandType)
@@ -108,6 +109,13 @@ namespace CliFx
return this;
}
/// <inheritdoc />
public ICliApplicationBuilder UseCommandOptionInputConverter(ICommandOptionInputConverter converter)
{
_commandOptionInputConverter = converter.GuardNotNull(nameof(converter));
return this;
}
/// <inheritdoc />
public ICliApplication Build()
{
@@ -117,6 +125,7 @@ namespace CliFx
_versionText = _versionText ?? GetDefaultVersionText() ?? "v1.0";
_console = _console ?? new SystemConsole();
_commandFactory = _commandFactory ?? new CommandFactory();
_commandOptionInputConverter = _commandOptionInputConverter ?? new CommandOptionInputConverter();
// Project parameters to expected types
var metadata = new ApplicationMetadata(_title, _executableName, _versionText, _description);
@@ -124,7 +133,7 @@ namespace CliFx
return new CliApplication(metadata, configuration,
_console, new CommandInputParser(), new CommandSchemaResolver(),
_commandFactory, new CommandInitializer(), new HelpTextRenderer());
_commandFactory, new CommandInitializer(_commandOptionInputConverter), new HelpTextRenderer());
}
}

View File

@@ -59,6 +59,11 @@ namespace CliFx
/// </summary>
ICliApplicationBuilder UseCommandFactory(ICommandFactory factory);
/// <summary>
/// Configures application to use specified implementation of <see cref="ICommandOptionInputConverter"/>.
/// </summary>
ICliApplicationBuilder UseCommandOptionInputConverter(ICommandOptionInputConverter converter);
/// <summary>
/// Creates an instance of <see cref="ICliApplication"/> using configured parameters.
/// Default values are used in place of parameters that were not specified.

View File

@@ -31,8 +31,13 @@ namespace CliFx.Services
{
}
private object ConvertValue(string value, Type targetType)
/// <summary>
/// Converts a single string value to specified target type.
/// </summary>
protected virtual object ConvertValue(string value, Type targetType)
{
targetType.GuardNotNull(nameof(targetType));
try
{
// String or object
@@ -126,17 +131,19 @@ namespace CliFx.Services
var parseMethod = GetStaticParseMethod(targetType);
if (parseMethod != null)
return parseMethod.Invoke(null, new object[] {value});
throw new CliFxException($"Can't convert value [{value}] to type [{targetType}].");
}
catch (Exception ex)
{
// An exception was thrown when trying to convert the value
throw new CliFxException($"Can't convert value [{value}] to type [{targetType}].", ex);
}
// Couldn't find a way to convert the value
throw new CliFxException($"Can't convert value [{value}] to type [{targetType}].");
}
/// <inheritdoc />
public object ConvertOptionInput(CommandOptionInput optionInput, Type targetType)
public virtual object ConvertOptionInput(CommandOptionInput optionInput, Type targetType)
{
optionInput.GuardNotNull(nameof(optionInput));
targetType.GuardNotNull(nameof(targetType));

View File

@@ -127,6 +127,34 @@ When resolving options, CliFx can convert string values obtained from the comman
If you want to define an option of your own type, the easiest way to do it is to make sure that your type is string-initializable, as explained above.
It is also possible to configure the application to use your own converter, by calling `UseCommandOptionInputConverter` method on `CliApplicationBuilder`.
```c#
var app = new CliApplicationBuilder()
.AddCommandsFromThisAssembly()
.UseCommandOptionInputConverter(new MyConverter())
.Build();
```
The converter class must implement `ICommandOptionInputConverter` but you can also derive from `CommandOptionInputConverter` to extend the default behavior.
```c#
public class MyConverter : CommandOptionInputConverter
{
protected override object ConvertValue(string value, Type targetType)
{
// Custom conversion for MyType
if (targetType == typeof(MyType))
{
// ...
}
// Default behavior for other types
return base.ConvertValue(value, targetType);
}
}
```
### Reporting errors
You may have noticed that commands in CliFx don't return exit codes. This is by design as exit codes are considered a higher-level concern and thus handled by `CliApplication`, not by individual commands.