From ed72571ddc17c2e3205dbbe6fcc5b0825c1134f8 Mon Sep 17 00:00:00 2001 From: Alexey Golub Date: Tue, 30 Jul 2019 23:08:08 +0300 Subject: [PATCH] Refactor --- CliFx/CliApplication.cs | 43 +++++++------- CliFx/Models/Extensions.cs | 44 ++++++++++++++ CliFx/Services/CommandHelpTextRenderer.cs | 57 +++---------------- CliFx/Services/CommandInitializer.cs | 21 +------ CliFx/Services/CommandOptionInputConverter.cs | 2 +- CliFx/Services/Extensions.cs | 7 +++ 6 files changed, 82 insertions(+), 92 deletions(-) diff --git a/CliFx/CliApplication.cs b/CliFx/CliApplication.cs index 84e0494..4131b4e 100644 --- a/CliFx/CliApplication.cs +++ b/CliFx/CliApplication.cs @@ -39,10 +39,15 @@ namespace CliFx _commandHelpTextRenderer = commandHelpTextRenderer; } - public CliApplication(ApplicationMetadata applicationMetadata, IReadOnlyList commandTypes) + public CliApplication(ApplicationMetadata applicationMetadata, IReadOnlyList commandTypes, IConsole console) : this(applicationMetadata, commandTypes, - new SystemConsole(), new CommandInputParser(), new CommandSchemaResolver(), - new CommandFactory(), new CommandInitializer(), new CommandHelpTextRenderer()) + console, new CommandInputParser(), new CommandSchemaResolver(), + new CommandFactory(), new CommandInitializer(), new CommandHelpTextRenderer(console)) + { + } + + public CliApplication(ApplicationMetadata applicationMetadata, IReadOnlyList commandTypes) + : this(applicationMetadata, commandTypes, new SystemConsole()) { } @@ -56,22 +61,14 @@ namespace CliFx { } - private IReadOnlyList GetAvailableCommandSchemas() => - _commandTypes.Select(_commandSchemaResolver.GetCommandSchema).ToArray(); - - private CommandSchema GetMatchingCommandSchema(IReadOnlyList availableCommandSchemas, string commandName) => - availableCommandSchemas.FirstOrDefault(c => string.Equals(c.Name, commandName, StringComparison.OrdinalIgnoreCase)); - - - public async Task RunAsync(IReadOnlyList commandLineArguments) { try { var commandInput = _commandInputParser.ParseInput(commandLineArguments); - var availableCommandSchemas = GetAvailableCommandSchemas(); - var matchingCommandSchema = GetMatchingCommandSchema(availableCommandSchemas, commandInput.CommandName); + var availableCommandSchemas = _commandSchemaResolver.GetCommandSchemas(_commandTypes); + var matchingCommandSchema = availableCommandSchemas.FindByName(commandInput.CommandName); // Fail if there are no commands defined if (!availableCommandSchemas.Any()) @@ -85,27 +82,31 @@ namespace CliFx // Handle cases where requested command is not defined if (matchingCommandSchema == null) { + var isError = false; + // If specified a command - show error if (commandInput.IsCommandSpecified()) { + isError = true; + _console.WithColor(ConsoleColor.Red, c => c.Error.WriteLine($"Specified command [{commandInput.CommandName}] is not defined.")); } - // Get default command schema - var defaultCommandSchema = availableCommandSchemas.FirstOrDefault(c => c.IsDefault()); + // Get parent command schema + var parentCommandSchema = availableCommandSchemas.FindParent(commandInput.CommandName); - // Use a stub if default command schema is not defined - if (defaultCommandSchema == null) + // Use a stub if parent command schema is not found + if (parentCommandSchema == null) { - defaultCommandSchema = _commandSchemaResolver.GetCommandSchema(typeof(StubDefaultCommand)); - availableCommandSchemas = availableCommandSchemas.Concat(new[] {defaultCommandSchema}).ToArray(); + parentCommandSchema = _commandSchemaResolver.GetCommandSchema(typeof(StubDefaultCommand)); + availableCommandSchemas = availableCommandSchemas.Concat(new[] { parentCommandSchema }).ToArray(); } // Show help - _commandHelpTextRenderer.RenderHelpText(_applicationMetadata, availableCommandSchemas, defaultCommandSchema); + _commandHelpTextRenderer.RenderHelpText(_applicationMetadata, availableCommandSchemas, parentCommandSchema); - return commandInput.IsCommandSpecified() ? -1 : 0; + return isError ? -1 : 0; } // Show version if it was requested without specifying a command diff --git a/CliFx/Models/Extensions.cs b/CliFx/Models/Extensions.cs index 72a9a16..9e14210 100644 --- a/CliFx/Models/Extensions.cs +++ b/CliFx/Models/Extensions.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using CliFx.Internal; @@ -25,5 +26,48 @@ namespace CliFx.Models } public static bool IsDefault(this CommandSchema commandSchema) => commandSchema.Name.IsNullOrWhiteSpace(); + + public static CommandSchema FindByName(this IReadOnlyList commandSchemas, string commandName) => + commandSchemas.FirstOrDefault(c => string.Equals(c.Name, commandName, StringComparison.OrdinalIgnoreCase)); + + public static CommandSchema FindParent(this IReadOnlyList commandSchemas, string commandName) + { + // If command has no name, it's the default command so it doesn't have a parent + if (commandName.IsNullOrWhiteSpace()) + return null; + + // Repeatedly cut off individual words from the name until we find a command with that name + var temp = commandName; + while (temp.Contains(" ")) + { + temp = temp.SubstringUntilLast(" "); + + var parent = commandSchemas.FindByName(temp); + if (parent != null) + return parent; + } + + // If no parent is matched by name, then the parent is the default command + return commandSchemas.FirstOrDefault(c => c.IsDefault()); + } + + public static CommandOptionSchema FindByAlias(this IReadOnlyList optionSchemas, string alias) + { + foreach (var optionSchema in optionSchemas) + { + var matchesByName = + !optionSchema.Name.IsNullOrWhiteSpace() && + string.Equals(optionSchema.Name, alias, StringComparison.OrdinalIgnoreCase); + + var matchesByShortName = + optionSchema.ShortName != null && + string.Equals(optionSchema.ShortName.Value.AsString(), alias, StringComparison.OrdinalIgnoreCase); + + if (matchesByName || matchesByShortName) + return optionSchema; + } + + return null; + } } } \ No newline at end of file diff --git a/CliFx/Services/CommandHelpTextRenderer.cs b/CliFx/Services/CommandHelpTextRenderer.cs index a3b5626..d08ac78 100644 --- a/CliFx/Services/CommandHelpTextRenderer.cs +++ b/CliFx/Services/CommandHelpTextRenderer.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; using CliFx.Internal; using CliFx.Models; @@ -16,11 +15,6 @@ namespace CliFx.Services _console = console; } - public CommandHelpTextRenderer() - : this(new SystemConsole()) - { - } - private void RenderDescription(CommandSchema commandSchema) { if (commandSchema.Description.IsNullOrWhiteSpace()) @@ -170,7 +164,9 @@ namespace CliFx.Services public void RenderHelpText(ApplicationMetadata applicationMetadata, IReadOnlyList availableCommandSchemas, CommandSchema matchingCommandSchema) { - var childCommandSchemas = GetChildCommandSchemas(availableCommandSchemas, matchingCommandSchema); + var childCommandSchemas = availableCommandSchemas + .Where(c => availableCommandSchemas.FindParent(c.Name) == matchingCommandSchema) + .ToArray(); // Render application info if (matchingCommandSchema.IsDefault()) @@ -191,48 +187,9 @@ namespace CliFx.Services public partial class CommandHelpTextRenderer { - private static CommandSchema GetParentOrNull(CommandSchema commandSchema, IReadOnlyList availableCommandSchemas) - { - if (commandSchema.IsDefault()) - return null; - - var nameBuffer = commandSchema.Name; - while (nameBuffer.Contains(' ')) - { - nameBuffer = nameBuffer.SubstringUntilLast(" "); - var parent = availableCommandSchemas.FirstOrDefault(c => - string.Equals(c.Name, nameBuffer, StringComparison.OrdinalIgnoreCase)); - - if (parent != null) - return parent; - } - - return availableCommandSchemas.FirstOrDefault(c => c.IsDefault()); - } - - private static IReadOnlyList GetChildCommandSchemas(IReadOnlyList availableCommandSchemas, - CommandSchema parentCommandSchema) - { - var result = new List(); - - foreach (var commandSchema in availableCommandSchemas) - { - if (commandSchema == parentCommandSchema) - continue; - - if (GetParentOrNull(commandSchema, availableCommandSchemas) == parentCommandSchema) - result.Add(commandSchema); - } - - return result; - } - - private static string GetRelativeCommandName(CommandSchema commandSchema, CommandSchema parentCommandSchema) - { - if (parentCommandSchema.Name.IsNullOrWhiteSpace()) - return commandSchema.Name; - - return commandSchema.Name.Substring(parentCommandSchema.Name.Length + 1); - } + private static string GetRelativeCommandName(CommandSchema commandSchema, CommandSchema parentCommandSchema) => + parentCommandSchema.Name.IsNullOrWhiteSpace() + ? commandSchema.Name + : commandSchema.Name.Substring(parentCommandSchema.Name.Length + 1); } } \ No newline at end of file diff --git a/CliFx/Services/CommandInitializer.cs b/CliFx/Services/CommandInitializer.cs index 72e90e7..101bdc8 100644 --- a/CliFx/Services/CommandInitializer.cs +++ b/CliFx/Services/CommandInitializer.cs @@ -21,25 +21,6 @@ namespace CliFx.Services { } - private CommandOptionSchema GetMatchingOptionSchema(CommandSchema commandSchema, CommandOptionInput optionInput) - { - foreach (var optionSchema in commandSchema.Options) - { - var matchesByName = - !optionSchema.Name.IsNullOrWhiteSpace() && - string.Equals(optionSchema.Name, optionInput.Alias, StringComparison.OrdinalIgnoreCase); - - var matchesByShortName = - optionSchema.ShortName != null && - string.Equals(optionSchema.ShortName.Value.AsString(), optionInput.Alias, StringComparison.OrdinalIgnoreCase); - - if (matchesByName || matchesByShortName) - return optionSchema; - } - - return null; - } - public void InitializeCommand(ICommand command, CommandSchema schema, CommandInput input) { // Set command options @@ -48,7 +29,7 @@ namespace CliFx.Services var properties = new HashSet(); foreach (var option in input.Options) { - var optionSchema = GetMatchingOptionSchema(schema, option); + var optionSchema = schema.Options.FindByAlias(option.Alias); if (optionSchema == null) continue; diff --git a/CliFx/Services/CommandOptionInputConverter.cs b/CliFx/Services/CommandOptionInputConverter.cs index ac8e9f8..15a8334 100644 --- a/CliFx/Services/CommandOptionInputConverter.cs +++ b/CliFx/Services/CommandOptionInputConverter.cs @@ -217,7 +217,7 @@ namespace CliFx.Services var parseMethodWithFormatProvider = GetStaticParseMethodWithFormatProvider(targetType); if (parseMethodWithFormatProvider != null) { - return parseMethodWithFormatProvider.Invoke(null, new object[] { value, _formatProvider }); + return parseMethodWithFormatProvider.Invoke(null, new object[] {value, _formatProvider}); } // Has a static parse method that accepts a single string diff --git a/CliFx/Services/Extensions.cs b/CliFx/Services/Extensions.cs index 621be44..e2aafec 100644 --- a/CliFx/Services/Extensions.cs +++ b/CliFx/Services/Extensions.cs @@ -1,9 +1,16 @@ using System; +using System.Collections.Generic; +using System.Linq; +using CliFx.Models; namespace CliFx.Services { public static class Extensions { + public static IReadOnlyList GetCommandSchemas(this ICommandSchemaResolver resolver, + IReadOnlyList commandTypes) => + commandTypes.Select(resolver.GetCommandSchema).ToArray(); + public static void WithColor(this IConsole console, ConsoleColor foregroundColor, Action action) { var lastForegroundColor = console.ForegroundColor;