Add xml documentation

This commit is contained in:
Alexey Golub
2019-08-13 21:59:57 +03:00
parent 384482a47c
commit 743241cb3b
36 changed files with 482 additions and 7 deletions

View File

@@ -1,20 +1,34 @@
using System;
using CliFx.Internal;
namespace CliFx.Attributes
{
/// <summary>
/// Annotates a type that defines a command.
/// </summary>
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class CommandAttribute : Attribute
{
/// <summary>
/// Command name.
/// </summary>
public string Name { get; }
/// <summary>
/// Command description, which is used in help text.
/// </summary>
public string Description { get; set; }
/// <summary>
/// Initializes an instance of <see cref="CommandAttribute"/>.
/// </summary>
public CommandAttribute(string name)
{
Name = name;
}
/// <summary>
/// Initializes an instance of <see cref="CommandAttribute"/>.
/// </summary>
public CommandAttribute()
: this(null)
{

View File

@@ -2,35 +2,65 @@
namespace CliFx.Attributes
{
/// <summary>
/// Annotates a property that defines a command option.
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public class CommandOptionAttribute : Attribute
{
/// <summary>
/// Option name.
/// </summary>
public string Name { get; }
/// <summary>
/// Option short name.
/// </summary>
public char? ShortName { get; }
/// <summary>
/// Option group name.
/// </summary>
public string GroupName { get; set; }
/// <summary>
/// Whether an option is required.
/// </summary>
public bool IsRequired { get; set; }
/// <summary>
/// Option description, which is used in help text.
/// </summary>
public string Description { get; set; }
/// <summary>
/// Initializes an instance of <see cref="CommandOptionAttribute"/>.
/// </summary>
public CommandOptionAttribute(string name, char? shortName)
{
Name = name;
ShortName = shortName;
}
/// <summary>
/// Initializes an instance of <see cref="CommandOptionAttribute"/>.
/// </summary>
public CommandOptionAttribute(string name, char shortName)
: this(name, (char?) shortName)
{
}
/// <summary>
/// Initializes an instance of <see cref="CommandOptionAttribute"/>.
/// </summary>
public CommandOptionAttribute(string name)
: this(name, null)
{
}
/// <summary>
/// Initializes an instance of <see cref="CommandOptionAttribute"/>.
/// </summary>
public CommandOptionAttribute(char shortName)
: this(null, shortName)
{

View File

@@ -10,6 +10,9 @@ using CliFx.Services;
namespace CliFx
{
/// <summary>
/// Default implementation of <see cref="ICliApplication"/>.
/// </summary>
public partial class CliApplication : ICliApplication
{
private readonly ApplicationMetadata _applicationMetadata;
@@ -22,6 +25,9 @@ namespace CliFx
private readonly ICommandInitializer _commandInitializer;
private readonly ICommandHelpTextRenderer _commandHelpTextRenderer;
/// <summary>
/// Initializes an instance of <see cref="CliApplication"/>.
/// </summary>
public CliApplication(ApplicationMetadata applicationMetadata, IReadOnlyList<Type> commandTypes,
IConsole console, ICommandInputParser commandInputParser, ICommandSchemaResolver commandSchemaResolver,
ICommandFactory commandFactory, ICommandInitializer commandInitializer, ICommandHelpTextRenderer commandHelpTextRenderer)
@@ -98,6 +104,7 @@ namespace CliFx
return result;
}
/// <inheritdoc />
public async Task<int> RunAsync(IReadOnlyList<string> commandLineArguments)
{
try

View File

@@ -9,6 +9,9 @@ using CliFx.Services;
namespace CliFx
{
/// <summary>
/// Default implementation of <see cref="ICliApplicationBuilder"/>.
/// </summary>
public class CliApplicationBuilder : ICliApplicationBuilder
{
private readonly HashSet<Type> _commandTypes = new HashSet<Type>();
@@ -19,12 +22,14 @@ namespace CliFx
private IConsole _console;
private ICommandFactory _commandFactory;
/// <inheritdoc />
public ICliApplicationBuilder WithCommand(Type commandType)
{
_commandTypes.Add(commandType);
return this;
}
/// <inheritdoc />
public ICliApplicationBuilder WithCommandsFrom(Assembly commandAssembly)
{
var commandTypes = commandAssembly.ExportedTypes.Where(t => t.Implements(typeof(ICommand)));
@@ -35,30 +40,35 @@ namespace CliFx
return this;
}
/// <inheritdoc />
public ICliApplicationBuilder UseTitle(string title)
{
_title = title;
return this;
}
public ICliApplicationBuilder UseExecutableName(string exeName)
/// <inheritdoc />
public ICliApplicationBuilder UseExecutableName(string executableName)
{
_executableName = exeName;
_executableName = executableName;
return this;
}
/// <inheritdoc />
public ICliApplicationBuilder UseVersionText(string version)
{
_versionText = version;
return this;
}
/// <inheritdoc />
public ICliApplicationBuilder UseConsole(IConsole console)
{
_console = console;
return this;
}
/// <inheritdoc />
public ICliApplicationBuilder UseCommandFactory(ICommandFactory factory)
{
_commandFactory = factory;
@@ -86,6 +96,7 @@ namespace CliFx
UseCommandFactory(new CommandFactory());
}
/// <inheritdoc />
public ICliApplication Build()
{
// Use defaults for required parameters that were not configured

View File

@@ -2,17 +2,29 @@
namespace CliFx.Exceptions
{
/// <summary>
/// Thrown when an input command option can't be converted.
/// </summary>
public class CannotConvertCommandOptionException : CliFxException
{
/// <summary>
/// Initializes an instance of <see cref="CannotConvertCommandOptionException"/>.
/// </summary>
public CannotConvertCommandOptionException()
{
}
/// <summary>
/// Initializes an instance of <see cref="CannotConvertCommandOptionException"/>.
/// </summary>
public CannotConvertCommandOptionException(string message)
: base(message)
{
}
/// <summary>
/// Initializes an instance of <see cref="CannotConvertCommandOptionException"/>.
/// </summary>
public CannotConvertCommandOptionException(string message, Exception innerException)
: base(message, innerException)
{

View File

@@ -2,18 +2,30 @@
namespace CliFx.Exceptions
{
/// <summary>
/// Domain exception thrown within CliFx.
/// </summary>
public abstract class CliFxException : Exception
{
/// <summary>
/// Initializes an instance of <see cref="CliFxException"/>.
/// </summary>
protected CliFxException(string message)
: base(message)
{
}
/// <summary>
/// Initializes an instance of <see cref="CliFxException"/>.
/// </summary>
protected CliFxException(string message, Exception innerException)
: base(message, innerException)
{
}
/// <summary>
/// Initializes an instance of <see cref="CliFxException"/>.
/// </summary>
protected CliFxException()
{
}

View File

@@ -2,26 +2,45 @@
namespace CliFx.Exceptions
{
/// <summary>
/// Thrown when a command cannot proceed with normal execution due to error.
/// Use this exception if you want to specify an exit code to use when the process terminates.
/// </summary>
public class CommandErrorException : CliFxException
{
/// <summary>
/// Process exit code.
/// </summary>
public int ExitCode { get; }
/// <summary>
/// Initializes an instance of <see cref="CommandErrorException"/>.
/// </summary>
public CommandErrorException(int exitCode, string message, Exception innerException)
: base(message, innerException)
{
ExitCode = exitCode;
}
/// <summary>
/// Initializes an instance of <see cref="CommandErrorException"/>.
/// </summary>
public CommandErrorException(int exitCode, Exception innerException)
: this(exitCode, null, innerException)
{
}
/// <summary>
/// Initializes an instance of <see cref="CommandErrorException"/>.
/// </summary>
public CommandErrorException(int exitCode, string message)
: this(exitCode, message, null)
{
}
/// <summary>
/// Initializes an instance of <see cref="CommandErrorException"/>.
/// </summary>
public CommandErrorException(int exitCode)
: this(exitCode, null, null)
{

View File

@@ -2,17 +2,29 @@
namespace CliFx.Exceptions
{
/// <summary>
/// Thrown when a required command option was not set.
/// </summary>
public class MissingCommandOptionException : CliFxException
{
/// <summary>
/// Initializes an instance of <see cref="MissingCommandOptionException"/>.
/// </summary>
public MissingCommandOptionException()
{
}
/// <summary>
/// Initializes an instance of <see cref="MissingCommandOptionException"/>.
/// </summary>
public MissingCommandOptionException(string message)
: base(message)
{
}
/// <summary>
/// Initializes an instance of <see cref="MissingCommandOptionException"/>.
/// </summary>
public MissingCommandOptionException(string message, Exception innerException)
: base(message, innerException)
{

View File

@@ -5,8 +5,14 @@ using CliFx.Services;
namespace CliFx
{
/// <summary>
/// Extensions for <see cref="CliFx"/>.
/// </summary>
public static class Extensions
{
/// <summary>
/// Adds multiple commands to the application.
/// </summary>
public static ICliApplicationBuilder WithCommands(this ICliApplicationBuilder builder, IReadOnlyList<Type> commandTypes)
{
foreach (var commandType in commandTypes)
@@ -15,6 +21,9 @@ namespace CliFx
return builder;
}
/// <summary>
/// Adds commands from specified assemblies to the application.
/// </summary>
public static ICliApplicationBuilder WithCommandsFrom(this ICliApplicationBuilder builder, IReadOnlyList<Assembly> commandAssemblies)
{
foreach (var commandAssembly in commandAssemblies)
@@ -23,9 +32,15 @@ namespace CliFx
return builder;
}
/// <summary>
/// Adds commands from calling assembly to the application.
/// </summary>
public static ICliApplicationBuilder WithCommandsFromThisAssembly(this ICliApplicationBuilder builder) =>
builder.WithCommandsFrom(Assembly.GetCallingAssembly());
/// <summary>
/// Configures application to use specified factory method for creating new instances of <see cref="ICommand"/>.
/// </summary>
public static ICliApplicationBuilder UseCommandFactory(this ICliApplicationBuilder builder, Func<Type, ICommand> factoryMethod) =>
builder.UseCommandFactory(new DelegateCommandFactory(factoryMethod));
}

View File

@@ -3,8 +3,15 @@ using System.Threading.Tasks;
namespace CliFx
{
/// <summary>
/// Entry point for a command line application.
/// </summary>
public interface ICliApplication
{
/// <summary>
/// Runs application with specified command line arguments.
/// Returns exit code.
/// </summary>
Task<int> RunAsync(IReadOnlyList<string> commandLineArguments);
}
}

View File

@@ -4,22 +4,50 @@ using CliFx.Services;
namespace CliFx
{
/// <summary>
/// Builds an instance of <see cref="ICliApplication"/>.
/// </summary>
public interface ICliApplicationBuilder
{
/// <summary>
/// Adds a command of specified type to the application.
/// </summary>
ICliApplicationBuilder WithCommand(Type commandType);
/// <summary>
/// Adds commands from specified assembly to the application.
/// </summary>
ICliApplicationBuilder WithCommandsFrom(Assembly commandAssembly);
/// <summary>
/// Sets application title, which appears in the help text.
/// </summary>
ICliApplicationBuilder UseTitle(string title);
ICliApplicationBuilder UseExecutableName(string exeName);
/// <summary>
/// Sets application executable name, which appears in the help text.
/// </summary>
ICliApplicationBuilder UseExecutableName(string executableName);
/// <summary>
/// Sets application version text, which appears in the help text and when the user requests version information.
/// </summary>
ICliApplicationBuilder UseVersionText(string version);
/// <summary>
/// Configures application to use specified implementation of <see cref="IConsole"/>.
/// </summary>
ICliApplicationBuilder UseConsole(IConsole console);
/// <summary>
/// Configures application to use specified implementation of <see cref="ICommandFactory"/>.
/// </summary>
ICliApplicationBuilder UseCommandFactory(ICommandFactory factory);
/// <summary>
/// Creates an instance of <see cref="ICliApplication"/> using configured parameters.
/// Default values are used in place of parameters that were not specified.
/// </summary>
ICliApplication Build();
}
}

View File

@@ -3,8 +3,15 @@ using CliFx.Services;
namespace CliFx
{
/// <summary>
/// Point of interaction between a user and command line interface.
/// </summary>
public interface ICommand
{
/// <summary>
/// Executes command using specified implementation of <see cref="IConsole"/>.
/// This method is called when the command is invoked by a user through command line interface.
/// </summary>
Task ExecuteAsync(IConsole console);
}
}

View File

@@ -1,13 +1,28 @@
namespace CliFx.Models
{
/// <summary>
/// Metadata associated with an application.
/// </summary>
public class ApplicationMetadata
{
/// <summary>
/// Application title.
/// </summary>
public string Title { get; }
/// <summary>
/// Application executable name.
/// </summary>
public string ExecutableName { get; }
/// <summary>
/// Application version text.
/// </summary>
public string VersionText { get; }
/// <summary>
/// Initializes an instance of <see cref="ApplicationMetadata"/>.
/// </summary>
public ApplicationMetadata(string title, string executableName, string versionText)
{
Title = title;

View File

@@ -4,33 +4,55 @@ using CliFx.Internal;
namespace CliFx.Models
{
/// <summary>
/// Parsed command line input.
/// </summary>
public partial class CommandInput
{
/// <summary>
/// Specified command name.
/// </summary>
public string CommandName { get; }
/// <summary>
/// Specified options.
/// </summary>
public IReadOnlyList<CommandOptionInput> Options { get; }
/// <summary>
/// Initializes an instance of <see cref="CommandInput"/>.
/// </summary>
public CommandInput(string commandName, IReadOnlyList<CommandOptionInput> options)
{
CommandName = commandName;
Options = options;
}
/// <summary>
/// Initializes an instance of <see cref="CommandInput"/>.
/// </summary>
public CommandInput(IReadOnlyList<CommandOptionInput> options)
: this(null, options)
{
}
/// <summary>
/// Initializes an instance of <see cref="CommandInput"/>.
/// </summary>
public CommandInput(string commandName)
: this(commandName, new CommandOptionInput[0])
{
}
/// <summary>
/// Initializes an instance of <see cref="CommandInput"/>.
/// </summary>
public CommandInput()
: this(null, new CommandOptionInput[0])
{
}
/// <inheritdoc />
public override string ToString()
{
var buffer = new StringBuilder();
@@ -53,6 +75,9 @@ namespace CliFx.Models
public partial class CommandInput
{
/// <summary>
/// Empty input.
/// </summary>
public static CommandInput Empty { get; } = new CommandInput();
}
}

View File

@@ -3,28 +3,47 @@ using System.Text;
namespace CliFx.Models
{
/// <summary>
/// Parsed option from command line input.
/// </summary>
public class CommandOptionInput
{
/// <summary>
/// Specified option alias.
/// </summary>
public string Alias { get; }
/// <summary>
/// Specified values.
/// </summary>
public IReadOnlyList<string> Values { get; }
/// <summary>
/// Initializes an instance of <see cref="CommandOptionInput"/>.
/// </summary>
public CommandOptionInput(string alias, IReadOnlyList<string> values)
{
Alias = alias;
Values = values;
}
/// <summary>
/// Initializes an instance of <see cref="CommandOptionInput"/>.
/// </summary>
public CommandOptionInput(string alias, string value)
: this(alias, new[] {value})
{
}
/// <summary>
/// Initializes an instance of <see cref="CommandOptionInput"/>.
/// </summary>
public CommandOptionInput(string alias)
: this(alias, new string[0])
{
}
/// <inheritdoc />
public override string ToString()
{
var buffer = new StringBuilder();

View File

@@ -4,20 +4,44 @@ using CliFx.Internal;
namespace CliFx.Models
{
/// <summary>
/// Schema of a defined command option.
/// </summary>
public class CommandOptionSchema
{
/// <summary>
/// Underlying property.
/// </summary>
public PropertyInfo Property { get; }
/// <summary>
/// Option name.
/// </summary>
public string Name { get; }
/// <summary>
/// Option short name.
/// </summary>
public char? ShortName { get; }
/// <summary>
/// Option group name.
/// </summary>
public string GroupName { get; }
/// <summary>
/// Whether an option is required.
/// </summary>
public bool IsRequired { get; }
/// <summary>
/// Option description.
/// </summary>
public string Description { get; }
/// <summary>
/// Initializes an instance of <see cref="CommandOptionSchema"/>.
/// </summary>
public CommandOptionSchema(PropertyInfo property, string name, char? shortName,
string groupName, bool isRequired, string description)
{
@@ -29,6 +53,7 @@ namespace CliFx.Models
Description = description;
}
/// <inheritdoc />
public override string ToString()
{
var buffer = new StringBuilder();

View File

@@ -5,16 +5,34 @@ using CliFx.Internal;
namespace CliFx.Models
{
/// <summary>
/// Schema of a defined command.
/// </summary>
public class CommandSchema
{
/// <summary>
/// Underlying type.
/// </summary>
public Type Type { get; }
/// <summary>
/// Command name.
/// </summary>
public string Name { get; }
/// <summary>
/// Command description.
/// </summary>
public string Description { get; }
/// <summary>
/// Command options.
/// </summary>
public IReadOnlyList<CommandOptionSchema> Options { get; }
/// <summary>
/// Initializes an instance of <see cref="CommandSchema"/>.
/// </summary>
public CommandSchema(Type type, string name, string description, IReadOnlyList<CommandOptionSchema> options)
{
Type = type;
@@ -23,6 +41,7 @@ namespace CliFx.Models
Options = options;
}
/// <inheritdoc />
public override string ToString()
{
var buffer = new StringBuilder();

View File

@@ -5,10 +5,19 @@ using CliFx.Internal;
namespace CliFx.Models
{
/// <summary>
/// Extensions for <see cref="Models"/>.
/// </summary>
public static class Extensions
{
/// <summary>
/// Gets whether a command was specified in the input.
/// </summary>
public static bool IsCommandSpecified(this CommandInput commandInput) => !commandInput.CommandName.IsNullOrWhiteSpace();
/// <summary>
/// Gets whether help was requested in the input.
/// </summary>
public static bool IsHelpRequested(this CommandInput commandInput)
{
var firstOptionAlias = commandInput.Options.FirstOrDefault()?.Alias;
@@ -18,6 +27,9 @@ namespace CliFx.Models
string.Equals(firstOptionAlias, "?", StringComparison.Ordinal);
}
/// <summary>
/// Gets whether version information was requested in the input.
/// </summary>
public static bool IsVersionRequested(this CommandInput commandInput)
{
var firstOptionAlias = commandInput.Options.FirstOrDefault()?.Alias;
@@ -25,11 +37,20 @@ namespace CliFx.Models
return string.Equals(firstOptionAlias, "version", StringComparison.OrdinalIgnoreCase);
}
/// <summary>
/// Gets whether this command is the default command, i.e. without a name.
/// </summary>
public static bool IsDefault(this CommandSchema commandSchema) => commandSchema.Name.IsNullOrWhiteSpace();
/// <summary>
/// Finds a command that has specified name, or null if not found.
/// </summary>
public static CommandSchema FindByName(this IReadOnlyList<CommandSchema> commandSchemas, string commandName) =>
commandSchemas.FirstOrDefault(c => string.Equals(c.Name, commandName, StringComparison.OrdinalIgnoreCase));
/// <summary>
/// Finds parent command to the command that has specified name, or null if not found.
/// </summary>
public static CommandSchema FindParent(this IReadOnlyList<CommandSchema> commandSchemas, string commandName)
{
// If command has no name, it's the default command so it doesn't have a parent
@@ -51,6 +72,9 @@ namespace CliFx.Models
return commandSchemas.FirstOrDefault(c => c.IsDefault());
}
/// <summary>
/// Finds an option that matches specified alias, or null if not found.
/// </summary>
public static CommandOptionSchema FindByAlias(this IReadOnlyList<CommandOptionSchema> optionSchemas, string alias)
{
foreach (var optionSchema in optionSchemas)

View File

@@ -2,14 +2,29 @@
namespace CliFx.Models
{
/// <summary>
/// Source information used to generate help text.
/// </summary>
public class HelpTextSource
{
/// <summary>
/// Application metadata.
/// </summary>
public ApplicationMetadata ApplicationMetadata { get; }
/// <summary>
/// Schemas of commands available in the application.
/// </summary>
public IReadOnlyList<CommandSchema> AvailableCommandSchemas { get; }
/// <summary>
/// Schema of the command for which help text is to be generated.
/// </summary>
public CommandSchema TargetCommandSchema { get; }
/// <summary>
/// Initializes an instance of <see cref="HelpTextSource"/>.
/// </summary>
public HelpTextSource(ApplicationMetadata applicationMetadata,
IReadOnlyList<CommandSchema> availableCommandSchemas,
CommandSchema targetCommandSchema)

View File

@@ -2,8 +2,12 @@
namespace CliFx.Services
{
/// <summary>
/// Default implementation of <see cref="ICommandFactory"/>.
/// </summary>
public class CommandFactory : ICommandFactory
{
/// <inheritdoc />
public ICommand CreateCommand(Type commandType) => (ICommand) Activator.CreateInstance(commandType);
}
}

View File

@@ -6,8 +6,12 @@ using CliFx.Models;
namespace CliFx.Services
{
/// <summary>
/// Default implementation of <see cref="ICommandHelpTextRenderer"/>.
/// </summary>
public partial class CommandHelpTextRenderer : ICommandHelpTextRenderer
{
/// <inheritdoc />
public void RenderHelpText(IConsole console, HelpTextSource source) => new Impl(console, source).RenderHelpText();
}

View File

@@ -7,20 +7,30 @@ using CliFx.Models;
namespace CliFx.Services
{
/// <summary>
/// Default implementation of <see cref="ICommandInitializer"/>.
/// </summary>
public class CommandInitializer : ICommandInitializer
{
private readonly ICommandOptionInputConverter _commandOptionInputConverter;
/// <summary>
/// Initializes an instance of <see cref="CommandInitializer"/>.
/// </summary>
public CommandInitializer(ICommandOptionInputConverter commandOptionInputConverter)
{
_commandOptionInputConverter = commandOptionInputConverter;
}
/// <summary>
/// Initializes an instance of <see cref="CommandInitializer"/>.
/// </summary>
public CommandInitializer()
: this(new CommandOptionInputConverter())
{
}
/// <inheritdoc />
public void InitializeCommand(ICommand command, CommandSchema schema, CommandInput input)
{
// Set command options

View File

@@ -6,9 +6,13 @@ using CliFx.Models;
namespace CliFx.Services
{
/// <summary>
/// Default implementation of <see cref="ICommandInputParser"/>.
/// </summary>
public class CommandInputParser : ICommandInputParser
{
// TODO: refactor
/// <inheritdoc />
public CommandInput ParseInput(IReadOnlyList<string> commandLineArguments)
{
// Initialize command name placeholder

View File

@@ -8,15 +8,24 @@ using CliFx.Models;
namespace CliFx.Services
{
/// <summary>
/// Default implementation of <see cref="ICommandOptionInputConverter"/>.
/// </summary>
public class CommandOptionInputConverter : ICommandOptionInputConverter
{
private readonly IFormatProvider _formatProvider;
/// <summary>
/// Initializes an instance of <see cref="CommandOptionInputConverter"/>.
/// </summary>
public CommandOptionInputConverter(IFormatProvider formatProvider)
{
_formatProvider = formatProvider;
}
/// <summary>
/// Initializes an instance of <see cref="CommandOptionInputConverter"/>.
/// </summary>
public CommandOptionInputConverter()
: this(CultureInfo.InvariantCulture)
{
@@ -232,6 +241,7 @@ namespace CliFx.Services
}
// TODO: refactor this
/// <inheritdoc />
public object ConvertOption(CommandOptionInput option, Type targetType)
{
if (targetType != typeof(string) && targetType.IsEnumerable())

View File

@@ -7,6 +7,9 @@ using CliFx.Models;
namespace CliFx.Services
{
/// <summary>
/// Default implementation of <see cref="ICommandSchemaResolver"/>.
/// </summary>
public class CommandSchemaResolver : ICommandSchemaResolver
{
private CommandOptionSchema GetCommandOptionSchema(PropertyInfo optionProperty)
@@ -24,7 +27,7 @@ namespace CliFx.Services
attribute.Description);
}
// TODO: validate stuff like duplicate names, multiple default commands, etc
/// <inheritdoc />
public CommandSchema GetCommandSchema(Type commandType)
{
if (!commandType.Implements(typeof(ICommand)))

View File

@@ -2,15 +2,22 @@
namespace CliFx.Services
{
/// <summary>
/// Implementation of <see cref="ICommandFactory"/> that uses a factory method to create commands.
/// </summary>
public class DelegateCommandFactory : ICommandFactory
{
private readonly Func<Type, ICommand> _factoryMethod;
/// <summary>
/// Initializes an instance of <see cref="DelegateCommandFactory"/>.
/// </summary>
public DelegateCommandFactory(Func<Type, ICommand> factoryMethod)
{
_factoryMethod = factoryMethod;
}
/// <inheritdoc />
public ICommand CreateCommand(Type commandType) => _factoryMethod(commandType);
}
}

View File

@@ -5,12 +5,20 @@ using CliFx.Models;
namespace CliFx.Services
{
/// <summary>
/// Extensions for <see cref="Services"/>
/// </summary>
public static class Extensions
{
/// <summary>
/// Resolves command schemas for commands of specified types.
/// </summary>
public static IReadOnlyList<CommandSchema> GetCommandSchemas(this ICommandSchemaResolver resolver,
IReadOnlyList<Type> commandTypes) =>
commandTypes.Select(resolver.GetCommandSchema).ToArray();
IReadOnlyList<Type> commandTypes) => commandTypes.Select(resolver.GetCommandSchema).ToArray();
/// <summary>
/// Sets console foreground color, executes specified action, and sets the color back to the original value.
/// </summary>
public static void WithForegroundColor(this IConsole console, ConsoleColor foregroundColor, Action action)
{
var lastColor = console.ForegroundColor;
@@ -21,6 +29,9 @@ namespace CliFx.Services
console.ForegroundColor = lastColor;
}
/// <summary>
/// Sets console background color, executes specified action, and sets the color back to the original value.
/// </summary>
public static void WithBackgroundColor(this IConsole console, ConsoleColor backgroundColor, Action action)
{
var lastColor = console.BackgroundColor;
@@ -31,6 +42,9 @@ namespace CliFx.Services
console.BackgroundColor = lastColor;
}
/// <summary>
/// Sets console foreground and background colors, executes specified action, and sets the colors back to the original values.
/// </summary>
public static void WithColors(this IConsole console, ConsoleColor foregroundColor, ConsoleColor backgroundColor, Action action) =>
console.WithForegroundColor(foregroundColor, () => console.WithBackgroundColor(backgroundColor, action));
}

View File

@@ -2,8 +2,14 @@
namespace CliFx.Services
{
/// <summary>
/// Initializes new instances of <see cref="ICommand"/>.
/// </summary>
public interface ICommandFactory
{
/// <summary>
/// Initializes an instance of <see cref="ICommand"/> of specified type.
/// </summary>
ICommand CreateCommand(Type commandType);
}
}

View File

@@ -2,8 +2,14 @@
namespace CliFx.Services
{
/// <summary>
/// Renders help text to the console.
/// </summary>
public interface ICommandHelpTextRenderer
{
/// <summary>
/// Renders help text using specified console and source information.
/// </summary>
void RenderHelpText(IConsole console, HelpTextSource source);
}
}

View File

@@ -2,8 +2,14 @@
namespace CliFx.Services
{
/// <summary>
/// Populates <see cref="ICommand"/> instances with input according to its schema.
/// </summary>
public interface ICommandInitializer
{
/// <summary>
/// Populates an instance of <see cref="ICommand"/> with specified input according to specified schema.
/// </summary>
void InitializeCommand(ICommand command, CommandSchema schema, CommandInput input);
}
}

View File

@@ -3,8 +3,14 @@ using CliFx.Models;
namespace CliFx.Services
{
/// <summary>
/// Parses command line arguments.
/// </summary>
public interface ICommandInputParser
{
/// <summary>
/// Parses specified command line arguments.
/// </summary>
CommandInput ParseInput(IReadOnlyList<string> commandLineArguments);
}
}

View File

@@ -3,8 +3,14 @@ using CliFx.Models;
namespace CliFx.Services
{
/// <summary>
/// Converts input command options.
/// </summary>
public interface ICommandOptionInputConverter
{
/// <summary>
/// Converts an option to specified target type.
/// </summary>
object ConvertOption(CommandOptionInput option, Type targetType);
}
}

View File

@@ -3,8 +3,14 @@ using CliFx.Models;
namespace CliFx.Services
{
/// <summary>
/// Resolves command schemas.
/// </summary>
public interface ICommandSchemaResolver
{
/// <summary>
/// Resolves schema of a command of specified type.
/// </summary>
CommandSchema GetCommandSchema(Type commandType);
}
}

View File

@@ -3,24 +3,54 @@ using System.IO;
namespace CliFx.Services
{
/// <summary>
/// Abstraction for interacting with the console.
/// </summary>
public interface IConsole
{
/// <summary>
/// Input stream (stdin).
/// </summary>
TextReader Input { get; }
/// <summary>
/// Whether the input stream is redirected.
/// </summary>
bool IsInputRedirected { get; }
/// <summary>
/// Output stream (stdout).
/// </summary>
TextWriter Output { get; }
/// <summary>
/// Whether the output stream is redirected.
/// </summary>
bool IsOutputRedirected { get; }
/// <summary>
/// Error stream (stderr).
/// </summary>
TextWriter Error { get; }
/// <summary>
/// Whether the error stream is redirected.
/// </summary>
bool IsErrorRedirected { get; }
/// <summary>
/// Current foreground color.
/// </summary>
ConsoleColor ForegroundColor { get; set; }
/// <summary>
/// Current background color.
/// </summary>
ConsoleColor BackgroundColor { get; set; }
/// <summary>
/// Resets foreground and background color to default values.
/// </summary>
void ResetColor();
}
}

View File

@@ -3,32 +3,44 @@ using System.IO;
namespace CliFx.Services
{
/// <summary>
/// Implementation of <see cref="IConsole"/> that wraps around <see cref="Console"/>.
/// </summary>
public class SystemConsole : IConsole
{
/// <inheritdoc />
public TextReader Input => Console.In;
/// <inheritdoc />
public bool IsInputRedirected => Console.IsInputRedirected;
/// <inheritdoc />
public TextWriter Output => Console.Out;
/// <inheritdoc />
public bool IsOutputRedirected => Console.IsOutputRedirected;
/// <inheritdoc />
public TextWriter Error => Console.Error;
/// <inheritdoc />
public bool IsErrorRedirected => Console.IsErrorRedirected;
/// <inheritdoc />
public ConsoleColor ForegroundColor
{
get => Console.ForegroundColor;
set => Console.ForegroundColor = value;
}
/// <inheritdoc />
public ConsoleColor BackgroundColor
{
get => Console.BackgroundColor;
set => Console.BackgroundColor = value;
}
/// <inheritdoc />
public void ResetColor() => Console.ResetColor();
}
}

View File

@@ -3,24 +3,40 @@ using System.IO;
namespace CliFx.Services
{
/// <summary>
/// Implementation of <see cref="IConsole"/> that routes data to specified streams.
/// Does not leak to <see cref="Console"/> in any way.
/// Provides an isolated instance of <see cref="IConsole"/> which is useful for testing purposes.
/// </summary>
public class TestConsole : IConsole
{
/// <inheritdoc />
public TextReader Input { get; }
/// <inheritdoc />
public bool IsInputRedirected => true;
/// <inheritdoc />
public TextWriter Output { get; }
/// <inheritdoc />
public bool IsOutputRedirected => true;
/// <inheritdoc />
public TextWriter Error { get; }
/// <inheritdoc />
public bool IsErrorRedirected => true;
/// <inheritdoc />
public ConsoleColor ForegroundColor { get; set; } = ConsoleColor.Gray;
/// <inheritdoc />
public ConsoleColor BackgroundColor { get; set; } = ConsoleColor.Black;
/// <summary>
/// Initializes an instance of <see cref="TestConsole"/>.
/// </summary>
public TestConsole(TextReader input, TextWriter output, TextWriter error)
{
Input = input;
@@ -28,16 +44,25 @@ namespace CliFx.Services
Error = error;
}
/// <summary>
/// Initializes an instance of <see cref="TestConsole"/> using output stream (stdout) and error stream (stderr).
/// Input stream (stdin) is considered empty.
/// </summary>
public TestConsole(TextWriter output, TextWriter error)
: this(TextReader.Null, output, error)
{
}
/// <summary>
/// Initializes an instance of <see cref="TestConsole"/> using output stream (stdout).
/// Input stream (stdin) and error stream (stderr) are considered empty.
/// </summary>
public TestConsole(TextWriter output)
: this(output, TextWriter.Null)
{
}
/// <inheritdoc />
public void ResetColor()
{
ForegroundColor = ConsoleColor.Gray;