Encapsulate application title, executable name, and version to ApplicationMetadata

This commit is contained in:
Alexey Golub
2019-07-26 00:17:31 +03:00
parent 5ac9b33056
commit 4e9effe481
5 changed files with 90 additions and 29 deletions

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
@@ -12,6 +13,7 @@ namespace CliFx
{
public partial class CliApplication : ICliApplication
{
private readonly ApplicationMetadata _applicationMetadata;
private readonly IReadOnlyList<Type> _commandTypes;
private readonly ICommandInputParser _commandInputParser;
private readonly ICommandSchemaResolver _commandSchemaResolver;
@@ -19,10 +21,11 @@ namespace CliFx
private readonly ICommandInitializer _commandInitializer;
private readonly ICommandHelpTextBuilder _commandHelpTextBuilder;
public CliApplication(IReadOnlyList<Type> commandTypes,
public CliApplication(ApplicationMetadata applicationMetadata, IReadOnlyList<Type> commandTypes,
ICommandInputParser commandInputParser, ICommandSchemaResolver commandSchemaResolver,
ICommandFactory commandFactory, ICommandInitializer commandInitializer, ICommandHelpTextBuilder commandHelpTextBuilder)
{
_applicationMetadata = applicationMetadata;
_commandTypes = commandTypes;
_commandInputParser = commandInputParser;
_commandSchemaResolver = commandSchemaResolver;
@@ -31,13 +34,18 @@ namespace CliFx
_commandHelpTextBuilder = commandHelpTextBuilder;
}
public CliApplication(IReadOnlyList<Type> commandTypes)
: this(commandTypes,
public CliApplication(ApplicationMetadata applicationMetadata, IReadOnlyList<Type> commandTypes)
: this(applicationMetadata, commandTypes,
new CommandInputParser(), new CommandSchemaResolver(), new CommandFactory(),
new CommandInitializer(), new CommandHelpTextBuilder())
{
}
public CliApplication(IReadOnlyList<Type> commandTypes)
: this(GetDefaultApplicationMetadata(), commandTypes)
{
}
public CliApplication()
: this(GetDefaultCommandTypes())
{
@@ -78,15 +86,14 @@ namespace CliFx
// Show version if it was requested without specifying a command
if (commandInput.IsVersionRequested() && !commandInput.IsCommandSpecified())
{
var versionText = Assembly.GetEntryAssembly()?.GetName().Version.ToString();
stdOut.WriteLine(versionText);
stdOut.WriteLine(_applicationMetadata.VersionText);
return 0;
}
// Show help if it was requested
if (commandInput.IsHelpRequested())
{
var helpText = _commandHelpTextBuilder.Build(availableCommandSchemas, matchingCommandSchema);
var helpText = _commandHelpTextBuilder.Build(_applicationMetadata, availableCommandSchemas, matchingCommandSchema);
stdOut.WriteLine(helpText);
return 0;
}
@@ -100,7 +107,10 @@ namespace CliFx
_commandInitializer.InitializeCommand(command, matchingCommandSchema, commandInput);
// Create context and execute command
var commandContext = new CommandContext(commandInput, availableCommandSchemas, matchingCommandSchema, stdOut, stdErr);
var commandContext = new CommandContext(_applicationMetadata,
availableCommandSchemas, matchingCommandSchema,
commandInput, stdOut, stdErr);
await command.ExecuteAsync(commandContext);
return 0;
@@ -120,9 +130,28 @@ namespace CliFx
public partial class CliApplication
{
private static IReadOnlyList<Type> GetDefaultCommandTypes() =>
Assembly.GetEntryAssembly()?.ExportedTypes.Where(t => t.Implements(typeof(ICommand))).ToArray() ??
Type.EmptyTypes;
private static ApplicationMetadata GetDefaultApplicationMetadata()
{
// Entry assembly is null in tests
var entryAssembly = Assembly.GetEntryAssembly();
var title = entryAssembly?.GetName().FullName ?? "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();
}
private sealed class StubDefaultCommand : ICommand
{
@@ -135,8 +164,12 @@ namespace CliFx
public Task ExecuteAsync(CommandContext context)
{
var helpText = _commandHelpTextBuilder.Build(context.AvailableCommandSchemas, context.MatchingCommandSchema);
var helpText = _commandHelpTextBuilder.Build(context.ApplicationMetadata,
context.AvailableCommandSchemas,
context.MatchingCommandSchema);
context.Output.WriteLine(helpText);
return Task.CompletedTask;
}
}

View File

@@ -0,0 +1,18 @@
namespace CliFx.Models
{
public class ApplicationMetadata
{
public string Title { get; }
public string ExecutableName { get; }
public string VersionText { get; }
public ApplicationMetadata(string title, string executableName, string versionText)
{
Title = title;
ExecutableName = executableName;
VersionText = versionText;
}
}
}

View File

@@ -5,23 +5,26 @@ namespace CliFx.Models
{
public class CommandContext
{
public CommandInput CommandInput { get; }
public ApplicationMetadata ApplicationMetadata { get; }
public IReadOnlyList<CommandSchema> AvailableCommandSchemas { get; }
public CommandSchema MatchingCommandSchema { get; }
public CommandInput CommandInput { get; }
public IConsoleWriter Output { get; }
public IConsoleWriter Error { get; }
public CommandContext(CommandInput commandInput,
public CommandContext(ApplicationMetadata applicationMetadata,
IReadOnlyList<CommandSchema> availableCommandSchemas, CommandSchema matchingCommandSchema,
IConsoleWriter output, IConsoleWriter error)
CommandInput commandInput, IConsoleWriter output, IConsoleWriter error)
{
CommandInput = commandInput;
ApplicationMetadata = applicationMetadata;
AvailableCommandSchemas = availableCommandSchemas;
MatchingCommandSchema = matchingCommandSchema;
CommandInput = commandInput;
Output = output;
Error = error;
}

View File

@@ -1,7 +1,5 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using CliFx.Internal;
using CliFx.Models;
@@ -11,10 +9,6 @@ namespace CliFx.Services
// TODO: add color
public class CommandHelpTextBuilder : ICommandHelpTextBuilder
{
// TODO: move to context?
// Entry assembly is null in tests
private string GetExeName() => Path.GetFileNameWithoutExtension(Assembly.GetEntryAssembly()?.Location) ?? "app";
private void AddDescription(StringBuilder buffer, CommandSchema commands)
{
if (commands.Description.IsNullOrWhiteSpace())
@@ -28,12 +22,13 @@ namespace CliFx.Services
buffer.AppendLine();
}
private void AddUsage(StringBuilder buffer, CommandSchema command, IReadOnlyList<CommandSchema> subCommands)
private void AddUsage(StringBuilder buffer, ApplicationMetadata applicationMetadata, CommandSchema command,
IReadOnlyList<CommandSchema> subCommands)
{
buffer.AppendLine("Usage:");
buffer.Append(" ");
buffer.Append(GetExeName());
buffer.Append(applicationMetadata.ExecutableName);
if (!command.Name.IsNullOrWhiteSpace())
{
@@ -119,21 +114,31 @@ namespace CliFx.Services
buffer.AppendLine();
}
public string Build(IReadOnlyList<CommandSchema> availableCommandSchemas, CommandSchema matchingCommandSchema)
public string Build(ApplicationMetadata applicationMetadata,
IReadOnlyList<CommandSchema> availableCommandSchemas,
CommandSchema matchingCommandSchema)
{
var buffer = new StringBuilder();
var subCommands = availableCommandSchemas.FindSubCommandSchemas(matchingCommandSchema.Name);
var buffer = new StringBuilder();
if (matchingCommandSchema.IsDefault())
{
buffer.Append(applicationMetadata.Title);
buffer.Append(" v");
buffer.Append(applicationMetadata.VersionText);
buffer.AppendLine().AppendLine();
}
AddDescription(buffer, matchingCommandSchema);
AddUsage(buffer, matchingCommandSchema, subCommands);
AddUsage(buffer, applicationMetadata, matchingCommandSchema, subCommands);
AddOptions(buffer, matchingCommandSchema);
AddSubCommands(buffer, subCommands);
if (matchingCommandSchema.IsDefault() && subCommands.Any())
{
buffer.Append("You can run ");
buffer.Append('`').Append(GetExeName()).Append(" [command] --help").Append('`');
buffer.Append('`').Append(applicationMetadata.ExecutableName).Append(" [command] --help").Append('`');
buffer.Append(" to show help on a specific command.");
buffer.AppendLine();
}

View File

@@ -5,6 +5,8 @@ namespace CliFx.Services
{
public interface ICommandHelpTextBuilder
{
string Build(IReadOnlyList<CommandSchema> availableCommandSchemas, CommandSchema matchingCommandSchema);
string Build(ApplicationMetadata applicationMetadata,
IReadOnlyList<CommandSchema> availableCommandSchemas,
CommandSchema matchingCommandSchema);
}
}