Add CliApplicationBuilder

This commit is contained in:
Alexey Golub
2019-08-11 00:32:52 +03:00
parent f47cd3774e
commit b0d9626e74
7 changed files with 169 additions and 54 deletions

View File

@@ -4,6 +4,10 @@ namespace CliFx.Tests.Dummy
{
public static class Program
{
public static Task<int> Main(string[] args) => new CliApplication().RunAsync(args);
public static Task<int> Main(string[] args) =>
new CliApplicationBuilder()
.WithCommandsFromThisAssembly()
.Build()
.RunAsync(args);
}
}

View File

@@ -204,7 +204,7 @@ namespace CliFx.Tests
public async Task RunAsync_Test(IReadOnlyList<Type> commandTypes, IReadOnlyList<string> commandLineArguments)
{
// Arrange
var application = new CliApplication(commandTypes);
var application = new CliApplicationBuilder().WithCommands(commandTypes).Build();
// Act
var exitCodeValue = await application.RunAsync(commandLineArguments);
@@ -218,7 +218,7 @@ namespace CliFx.Tests
public async Task RunAsync_Negative_Test(IReadOnlyList<Type> commandTypes, IReadOnlyList<string> commandLineArguments)
{
// Arrange
var application = new CliApplication(commandTypes);
var application = new CliApplicationBuilder().WithCommands(commandTypes).Build();
// Act
var exitCodeValue = await application.RunAsync(commandLineArguments);

View File

@@ -1,12 +1,9 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using CliFx.Attributes;
using CliFx.Exceptions;
using CliFx.Internal;
using CliFx.Models;
using CliFx.Services;
@@ -39,28 +36,6 @@ namespace CliFx
_commandHelpTextRenderer = commandHelpTextRenderer;
}
public CliApplication(ApplicationMetadata applicationMetadata, IReadOnlyList<Type> commandTypes, IConsole console)
: this(applicationMetadata, commandTypes,
console, new CommandInputParser(), new CommandSchemaResolver(),
new CommandFactory(), new CommandInitializer(), new CommandHelpTextRenderer(console))
{
}
public CliApplication(ApplicationMetadata applicationMetadata, IReadOnlyList<Type> commandTypes)
: this(applicationMetadata, commandTypes, new SystemConsole())
{
}
public CliApplication(IReadOnlyList<Type> commandTypes)
: this(GetDefaultApplicationMetadata(), commandTypes)
{
}
public CliApplication()
: this(GetDefaultCommandTypes())
{
}
public async Task<int> RunAsync(IReadOnlyList<string> commandLineArguments)
{
try
@@ -144,32 +119,6 @@ namespace CliFx
}
}
public partial class CliApplication
{
private static ApplicationMetadata GetDefaultApplicationMetadata()
{
// Entry assembly is null in tests
var entryAssembly = Assembly.GetEntryAssembly();
var title = entryAssembly?.GetName().Name ?? "App";
var executableName = Path.GetFileNameWithoutExtension(entryAssembly?.Location) ?? "app";
var versionText = entryAssembly?.GetName().Version.ToString() ?? "1.0";
return new ApplicationMetadata(title, executableName, versionText);
}
private static IReadOnlyList<Type> GetDefaultCommandTypes()
{
// Entry assembly is null in tests
var entryAssembly = Assembly.GetEntryAssembly();
if (entryAssembly == null)
return Type.EmptyTypes;
return entryAssembly.ExportedTypes.Where(t => t.Implements(typeof(ICommand))).ToArray();
}
}
public partial class CliApplication
{
[Command]

View File

@@ -0,0 +1,89 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using CliFx.Internal;
using CliFx.Models;
using CliFx.Services;
namespace CliFx
{
public class CliApplicationBuilder : ICliApplicationBuilder
{
private readonly HashSet<Type> _commandTypes = new HashSet<Type>();
private string _title;
private string _executableName;
private string _versionText;
private IConsole _console;
private ICommandFactory _commandFactory;
public ICliApplicationBuilder WithCommand(Type commandType)
{
_commandTypes.Add(commandType);
return this;
}
public ICliApplicationBuilder WithCommandsFrom(Assembly commandAssembly)
{
var commandTypes = commandAssembly.ExportedTypes.Where(t => t.Implements(typeof(ICommand)));
foreach (var commandType in commandTypes)
WithCommand(commandType);
return this;
}
public ICliApplicationBuilder UseTitle(string title)
{
_title = title;
return this;
}
public ICliApplicationBuilder UseExecutableName(string exeName)
{
_executableName = exeName;
return this;
}
public ICliApplicationBuilder UseVersionText(string version)
{
_versionText = version;
return this;
}
public ICliApplicationBuilder UseConsole(IConsole console)
{
_console = console;
return this;
}
public ICliApplicationBuilder UseCommandFactory(ICommandFactory factory)
{
_commandFactory = factory;
return this;
}
public ICliApplication Build()
{
// Entry assembly is null in tests
var entryAssembly = Assembly.GetEntryAssembly();
// Use defaults for required parameters that were not configured
var title = _title ?? entryAssembly?.GetName().Name ?? "App";
var executableName = _executableName ?? Path.GetFileNameWithoutExtension(entryAssembly?.Location) ?? "app";
var versionText = _versionText ?? entryAssembly?.GetName().Version.ToString() ?? "1.0";
var console = _console ?? new SystemConsole();
var commandFactory = _commandFactory ?? new CommandFactory();
// Project parameters to expected types
var applicationMetadata = new ApplicationMetadata(title, executableName, versionText);
var commandTypes = _commandTypes.ToArray();
return new CliApplication(applicationMetadata, commandTypes,
console, new CommandInputParser(), new CommandSchemaResolver(),
commandFactory, new CommandInitializer(), new CommandHelpTextRenderer(console));
}
}
}

32
CliFx/Extensions.cs Normal file
View File

@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using CliFx.Services;
namespace CliFx
{
public static class Extensions
{
public static ICliApplicationBuilder WithCommands(this ICliApplicationBuilder builder, IReadOnlyList<Type> commandTypes)
{
foreach (var commandType in commandTypes)
builder.WithCommand(commandType);
return builder;
}
public static ICliApplicationBuilder WithCommandsFrom(this ICliApplicationBuilder builder, IReadOnlyList<Assembly> commandAssemblies)
{
foreach (var commandAssembly in commandAssemblies)
builder.WithCommandsFrom(commandAssembly);
return builder;
}
public static ICliApplicationBuilder WithCommandsFromThisAssembly(this ICliApplicationBuilder builder) =>
builder.WithCommandsFrom(Assembly.GetCallingAssembly());
public static ICliApplicationBuilder UseCommandFactory(this ICliApplicationBuilder builder, Func<Type, ICommand> factoryMethod) =>
builder.UseCommandFactory(new DelegateCommandFactory(factoryMethod));
}
}

View File

@@ -0,0 +1,25 @@
using System;
using System.Reflection;
using CliFx.Services;
namespace CliFx
{
public interface ICliApplicationBuilder
{
ICliApplicationBuilder WithCommand(Type commandType);
ICliApplicationBuilder WithCommandsFrom(Assembly commandAssembly);
ICliApplicationBuilder UseTitle(string title);
ICliApplicationBuilder UseExecutableName(string exeName);
ICliApplicationBuilder UseVersionText(string version);
ICliApplicationBuilder UseConsole(IConsole console);
ICliApplicationBuilder UseCommandFactory(ICommandFactory factory);
ICliApplication Build();
}
}

View File

@@ -0,0 +1,16 @@
using System;
namespace CliFx.Services
{
public class DelegateCommandFactory : ICommandFactory
{
private readonly Func<Type, ICommand> _factoryMethod;
public DelegateCommandFactory(Func<Type, ICommand> factoryMethod)
{
_factoryMethod = factoryMethod;
}
public ICommand CreateCommand(Type commandType) => _factoryMethod(commandType);
}
}