From b0d9626e74d8ad8f9aca9bfefc7a8b3fedd6b9fc Mon Sep 17 00:00:00 2001 From: Alexey Golub Date: Sun, 11 Aug 2019 00:32:52 +0300 Subject: [PATCH] Add CliApplicationBuilder --- CliFx.Tests.Dummy/Program.cs | 6 +- CliFx.Tests/CliApplicationTests.cs | 4 +- CliFx/CliApplication.cs | 51 -------------- CliFx/CliApplicationBuilder.cs | 89 ++++++++++++++++++++++++ CliFx/Extensions.cs | 32 +++++++++ CliFx/ICliApplicationBuilder.cs | 25 +++++++ CliFx/Services/DelegateCommandFactory.cs | 16 +++++ 7 files changed, 169 insertions(+), 54 deletions(-) create mode 100644 CliFx/CliApplicationBuilder.cs create mode 100644 CliFx/Extensions.cs create mode 100644 CliFx/ICliApplicationBuilder.cs create mode 100644 CliFx/Services/DelegateCommandFactory.cs diff --git a/CliFx.Tests.Dummy/Program.cs b/CliFx.Tests.Dummy/Program.cs index c76bc5e..24b79cb 100644 --- a/CliFx.Tests.Dummy/Program.cs +++ b/CliFx.Tests.Dummy/Program.cs @@ -4,6 +4,10 @@ namespace CliFx.Tests.Dummy { public static class Program { - public static Task Main(string[] args) => new CliApplication().RunAsync(args); + public static Task Main(string[] args) => + new CliApplicationBuilder() + .WithCommandsFromThisAssembly() + .Build() + .RunAsync(args); } } \ No newline at end of file diff --git a/CliFx.Tests/CliApplicationTests.cs b/CliFx.Tests/CliApplicationTests.cs index 4fa3ed1..f302b64 100644 --- a/CliFx.Tests/CliApplicationTests.cs +++ b/CliFx.Tests/CliApplicationTests.cs @@ -204,7 +204,7 @@ namespace CliFx.Tests public async Task RunAsync_Test(IReadOnlyList commandTypes, IReadOnlyList 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 commandTypes, IReadOnlyList commandLineArguments) { // Arrange - var application = new CliApplication(commandTypes); + var application = new CliApplicationBuilder().WithCommands(commandTypes).Build(); // Act var exitCodeValue = await application.RunAsync(commandLineArguments); diff --git a/CliFx/CliApplication.cs b/CliFx/CliApplication.cs index 4131b4e..c6adebd 100644 --- a/CliFx/CliApplication.cs +++ b/CliFx/CliApplication.cs @@ -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 commandTypes, IConsole console) - : this(applicationMetadata, commandTypes, - console, new CommandInputParser(), new CommandSchemaResolver(), - new CommandFactory(), new CommandInitializer(), new CommandHelpTextRenderer(console)) - { - } - - public CliApplication(ApplicationMetadata applicationMetadata, IReadOnlyList commandTypes) - : this(applicationMetadata, commandTypes, new SystemConsole()) - { - } - - public CliApplication(IReadOnlyList commandTypes) - : this(GetDefaultApplicationMetadata(), commandTypes) - { - } - - public CliApplication() - : this(GetDefaultCommandTypes()) - { - } - public async Task RunAsync(IReadOnlyList 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 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] diff --git a/CliFx/CliApplicationBuilder.cs b/CliFx/CliApplicationBuilder.cs new file mode 100644 index 0000000..cc74e92 --- /dev/null +++ b/CliFx/CliApplicationBuilder.cs @@ -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 _commandTypes = new HashSet(); + + 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)); + } + } +} \ No newline at end of file diff --git a/CliFx/Extensions.cs b/CliFx/Extensions.cs new file mode 100644 index 0000000..e377310 --- /dev/null +++ b/CliFx/Extensions.cs @@ -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 commandTypes) + { + foreach (var commandType in commandTypes) + builder.WithCommand(commandType); + + return builder; + } + + public static ICliApplicationBuilder WithCommandsFrom(this ICliApplicationBuilder builder, IReadOnlyList 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 factoryMethod) => + builder.UseCommandFactory(new DelegateCommandFactory(factoryMethod)); + } +} \ No newline at end of file diff --git a/CliFx/ICliApplicationBuilder.cs b/CliFx/ICliApplicationBuilder.cs new file mode 100644 index 0000000..c12f0c8 --- /dev/null +++ b/CliFx/ICliApplicationBuilder.cs @@ -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(); + } +} \ No newline at end of file diff --git a/CliFx/Services/DelegateCommandFactory.cs b/CliFx/Services/DelegateCommandFactory.cs new file mode 100644 index 0000000..008c4d6 --- /dev/null +++ b/CliFx/Services/DelegateCommandFactory.cs @@ -0,0 +1,16 @@ +using System; + +namespace CliFx.Services +{ + public class DelegateCommandFactory : ICommandFactory + { + private readonly Func _factoryMethod; + + public DelegateCommandFactory(Func factoryMethod) + { + _factoryMethod = factoryMethod; + } + + public ICommand CreateCommand(Type commandType) => _factoryMethod(commandType); + } +} \ No newline at end of file