mirror of
https://github.com/Tyrrrz/CliFx.git
synced 2025-10-25 15:19:17 +00:00
asd
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace CliFx.Demo.Domain;
|
||||
@@ -24,5 +23,5 @@ public partial record Library(IReadOnlyList<Book> Books)
|
||||
|
||||
public partial record Library
|
||||
{
|
||||
public static Library Empty { get; } = new(Array.Empty<Book>());
|
||||
public static Library Empty { get; } = new([]);
|
||||
}
|
||||
|
||||
6
CliFx.Demo/Domain/LibraryJsonContext.cs
Normal file
6
CliFx.Demo/Domain/LibraryJsonContext.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace CliFx.Demo.Domain;
|
||||
|
||||
[JsonSerializable(typeof(Library))]
|
||||
public partial class LibraryJsonContext : JsonSerializerContext;
|
||||
@@ -11,7 +11,7 @@ public class LibraryProvider
|
||||
|
||||
private void StoreLibrary(Library library)
|
||||
{
|
||||
var data = JsonSerializer.Serialize(library);
|
||||
var data = JsonSerializer.Serialize(library, LibraryJsonContext.Default.Library);
|
||||
File.WriteAllText(StorageFilePath, data);
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ public class LibraryProvider
|
||||
|
||||
var data = File.ReadAllText(StorageFilePath);
|
||||
|
||||
return JsonSerializer.Deserialize<Library>(data) ?? Library.Empty;
|
||||
return JsonSerializer.Deserialize(data, LibraryJsonContext.Default.Library) ?? Library.Empty;
|
||||
}
|
||||
|
||||
public Book? TryGetBook(string title) =>
|
||||
|
||||
@@ -19,7 +19,7 @@ public class ApplicationSpecs(ITestOutputHelper testOutput) : SpecsBase(testOutp
|
||||
.UseConsole(FakeConsole)
|
||||
.Build();
|
||||
|
||||
var exitCode = await app.RunAsync(Array.Empty<string>(), new Dictionary<string, string>());
|
||||
var exitCode = await app.RunAsync([], new Dictionary<string, string>());
|
||||
|
||||
// Assert
|
||||
exitCode.Should().Be(0);
|
||||
@@ -45,7 +45,7 @@ public class ApplicationSpecs(ITestOutputHelper testOutput) : SpecsBase(testOutp
|
||||
.UseTypeActivator(Activator.CreateInstance!)
|
||||
.Build();
|
||||
|
||||
var exitCode = await app.RunAsync(Array.Empty<string>(), new Dictionary<string, string>());
|
||||
var exitCode = await app.RunAsync([], new Dictionary<string, string>());
|
||||
|
||||
// Assert
|
||||
exitCode.Should().Be(0);
|
||||
@@ -60,7 +60,7 @@ public class ApplicationSpecs(ITestOutputHelper testOutput) : SpecsBase(testOutp
|
||||
.UseConsole(FakeConsole)
|
||||
.Build();
|
||||
|
||||
var exitCode = await app.RunAsync(Array.Empty<string>(), new Dictionary<string, string>());
|
||||
var exitCode = await app.RunAsync([], new Dictionary<string, string>());
|
||||
|
||||
// Assert
|
||||
exitCode.Should().NotBe(0);
|
||||
|
||||
@@ -94,7 +94,7 @@ public class CancellationSpecs(ITestOutputHelper testOutput) : SpecsBase(testOut
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
Array.Empty<string>(),
|
||||
[],
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ public class ConsoleSpecs(ITestOutputHelper testOutput) : SpecsBase(testOutput)
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
Array.Empty<string>(),
|
||||
[],
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -144,7 +144,7 @@ public class ConsoleSpecs(ITestOutputHelper testOutput) : SpecsBase(testOutput)
|
||||
FakeConsole.WriteInput("Hello world");
|
||||
|
||||
var exitCode = await application.RunAsync(
|
||||
Array.Empty<string>(),
|
||||
[],
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -191,7 +191,7 @@ public class ConsoleSpecs(ITestOutputHelper testOutput) : SpecsBase(testOutput)
|
||||
FakeConsole.EnqueueKey(new ConsoleKeyInfo('\0', ConsoleKey.Backspace, false, false, false));
|
||||
|
||||
var exitCode = await application.RunAsync(
|
||||
Array.Empty<string>(),
|
||||
[],
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ public class EnvironmentSpecs(ITestOutputHelper testOutput) : SpecsBase(testOutp
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
Array.Empty<string>(),
|
||||
[],
|
||||
new Dictionary<string, string> { ["ENV_FOO"] = $"bar{Path.PathSeparator}baz" }
|
||||
);
|
||||
|
||||
@@ -130,7 +130,7 @@ public class EnvironmentSpecs(ITestOutputHelper testOutput) : SpecsBase(testOutp
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
Array.Empty<string>(),
|
||||
[],
|
||||
new Dictionary<string, string> { ["ENV_FOO"] = $"bar{Path.PathSeparator}baz" }
|
||||
);
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ public class ErrorReportingSpecs(ITestOutputHelper testOutput) : SpecsBase(testO
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
Array.Empty<string>(),
|
||||
[],
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -73,7 +73,7 @@ public class ErrorReportingSpecs(ITestOutputHelper testOutput) : SpecsBase(testO
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
Array.Empty<string>(),
|
||||
[],
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -119,7 +119,7 @@ public class ErrorReportingSpecs(ITestOutputHelper testOutput) : SpecsBase(testO
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
Array.Empty<string>(),
|
||||
[],
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -156,7 +156,7 @@ public class ErrorReportingSpecs(ITestOutputHelper testOutput) : SpecsBase(testO
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
Array.Empty<string>(),
|
||||
[],
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -194,7 +194,7 @@ public class ErrorReportingSpecs(ITestOutputHelper testOutput) : SpecsBase(testO
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
Array.Empty<string>(),
|
||||
[],
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ public class HelpTextSpecs(ITestOutputHelper testOutput) : SpecsBase(testOutput)
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
Array.Empty<string>(),
|
||||
[],
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
|
||||
@@ -612,7 +612,7 @@ public class OptionBindingSpecs(ITestOutputHelper testOutput) : SpecsBase(testOu
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
Array.Empty<string>(),
|
||||
[],
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ public class RoutingSpecs(ITestOutputHelper testOutput) : SpecsBase(testOutput)
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
Array.Empty<string>(),
|
||||
[],
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ public class TypeActivationSpecs(ITestOutputHelper testOutput) : SpecsBase(testO
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
Array.Empty<string>(),
|
||||
[],
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -75,7 +75,7 @@ public class TypeActivationSpecs(ITestOutputHelper testOutput) : SpecsBase(testO
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
Array.Empty<string>(),
|
||||
[],
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -117,7 +117,7 @@ public class TypeActivationSpecs(ITestOutputHelper testOutput) : SpecsBase(testO
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
Array.Empty<string>(),
|
||||
[],
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -172,7 +172,7 @@ public class TypeActivationSpecs(ITestOutputHelper testOutput) : SpecsBase(testO
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
Array.Empty<string>(),
|
||||
[],
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -210,7 +210,7 @@ public class TypeActivationSpecs(ITestOutputHelper testOutput) : SpecsBase(testO
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
Array.Empty<string>(),
|
||||
[],
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ using System.Threading.Tasks;
|
||||
using CliFx.Exceptions;
|
||||
using CliFx.Formatting;
|
||||
using CliFx.Infrastructure;
|
||||
using CliFx.Input;
|
||||
using CliFx.Parsing;
|
||||
using CliFx.Schema;
|
||||
using CliFx.Utils;
|
||||
using CliFx.Utils.Extensions;
|
||||
@@ -33,11 +33,11 @@ public class CliApplication(
|
||||
/// </summary>
|
||||
public ApplicationConfiguration Configuration { get; } = configuration;
|
||||
|
||||
private bool IsDebugModeEnabled(CommandInput commandInput) =>
|
||||
Configuration.IsDebugModeAllowed && commandInput.IsDebugDirectiveSpecified;
|
||||
private bool IsDebugModeEnabled(CommandArguments commandArguments) =>
|
||||
Configuration.IsDebugModeAllowed && commandArguments.IsDebugDirectiveSpecified;
|
||||
|
||||
private bool IsPreviewModeEnabled(CommandInput commandInput) =>
|
||||
Configuration.IsPreviewModeAllowed && commandInput.IsPreviewDirectiveSpecified;
|
||||
private bool IsPreviewModeEnabled(CommandArguments commandArguments) =>
|
||||
Configuration.IsPreviewModeAllowed && commandArguments.IsPreviewDirectiveSpecified;
|
||||
|
||||
private async ValueTask PromptDebuggerAsync()
|
||||
{
|
||||
@@ -57,7 +57,8 @@ public class CliApplication(
|
||||
|
||||
private async ValueTask<int> RunAsync(
|
||||
ApplicationSchema applicationSchema,
|
||||
CommandInput commandInput
|
||||
CommandArguments commandArguments,
|
||||
IReadOnlyDictionary<string, string?> environmentVariables
|
||||
)
|
||||
{
|
||||
// Console colors may have already been overridden by the parent process,
|
||||
@@ -65,26 +66,26 @@ public class CliApplication(
|
||||
console.ResetColor();
|
||||
|
||||
// Handle the debug directive
|
||||
if (IsDebugModeEnabled(commandInput))
|
||||
if (IsDebugModeEnabled(commandArguments))
|
||||
{
|
||||
await PromptDebuggerAsync();
|
||||
}
|
||||
|
||||
// Handle the preview directive
|
||||
if (IsPreviewModeEnabled(commandInput))
|
||||
if (IsPreviewModeEnabled(commandArguments))
|
||||
{
|
||||
console.WriteCommandInput(commandInput);
|
||||
console.WriteCommandInput(commandArguments);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Try to get the command schema that matches the input
|
||||
var commandSchema =
|
||||
var command =
|
||||
(
|
||||
!string.IsNullOrWhiteSpace(commandInput.CommandName)
|
||||
!string.IsNullOrWhiteSpace(commandArguments.CommandName)
|
||||
// If the command name is specified, try to find the command by name.
|
||||
// This should always succeed, because the input parsing relies on
|
||||
// the list of available command names.
|
||||
? applicationSchema.TryFindCommand(commandInput.CommandName)
|
||||
? applicationSchema.TryFindCommand(commandArguments.CommandName)
|
||||
// Otherwise, try to find the default command
|
||||
: applicationSchema.TryFindDefaultCommand()
|
||||
)
|
||||
@@ -95,16 +96,16 @@ public class CliApplication(
|
||||
|
||||
// Initialize an instance of the command type
|
||||
var commandInstance =
|
||||
commandSchema == FallbackDefaultCommand.Schema
|
||||
command == FallbackDefaultCommand.Schema
|
||||
? new FallbackDefaultCommand() // bypass the activator
|
||||
: typeActivator.CreateInstance<ICommand>(commandSchema.Type);
|
||||
: typeActivator.CreateInstance<ICommand>(command.Type);
|
||||
|
||||
// Assemble the help context
|
||||
var helpContext = new HelpContext(
|
||||
Metadata,
|
||||
applicationSchema,
|
||||
commandSchema,
|
||||
commandSchema.GetValues(commandInstance)
|
||||
command,
|
||||
command.GetValues(commandInstance)
|
||||
);
|
||||
|
||||
// Starting from this point, we may produce exceptions that are meant for the
|
||||
@@ -113,8 +114,8 @@ public class CliApplication(
|
||||
// propagate further.
|
||||
try
|
||||
{
|
||||
// Activate the command instance with the provided input
|
||||
commandSchema.Activate(commandInstance, commandInput);
|
||||
// Activate the command instance with the provided user input
|
||||
command.Activate(commandInstance, commandArguments, environmentVariables);
|
||||
|
||||
// Handle the version option
|
||||
if (commandInstance is ICommandWithVersionOption { IsVersionRequested: true })
|
||||
@@ -163,18 +164,18 @@ public class CliApplication(
|
||||
/// </remarks>
|
||||
public async ValueTask<int> RunAsync(
|
||||
IReadOnlyList<string> commandLineArguments,
|
||||
IReadOnlyDictionary<string, string> environmentVariables
|
||||
IReadOnlyDictionary<string, string?> environmentVariables
|
||||
)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await RunAsync(
|
||||
Configuration.Schema,
|
||||
CommandInput.Parse(
|
||||
CommandArguments.Parse(
|
||||
commandLineArguments,
|
||||
environmentVariables,
|
||||
Configuration.Schema.GetCommandNames()
|
||||
)
|
||||
),
|
||||
environmentVariables
|
||||
);
|
||||
}
|
||||
// To prevent the app from showing the annoying troubleshooting dialog on Windows,
|
||||
@@ -203,7 +204,7 @@ public class CliApplication(
|
||||
commandLineArguments,
|
||||
Environment
|
||||
.GetEnvironmentVariables()
|
||||
.ToDictionary<string, string>(StringComparer.Ordinal)
|
||||
.ToDictionary<string, string?>(StringComparer.Ordinal)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace CliFx;
|
||||
/// </summary>
|
||||
public partial class CliApplicationBuilder
|
||||
{
|
||||
private readonly HashSet<CommandSchema> _commandSchemas = [];
|
||||
private readonly HashSet<CommandSchema> _commands = [];
|
||||
|
||||
private bool _isDebugModeAllowed = true;
|
||||
private bool _isPreviewModeAllowed = true;
|
||||
@@ -33,19 +33,19 @@ public partial class CliApplicationBuilder
|
||||
/// <summary>
|
||||
/// Adds a command to the application.
|
||||
/// </summary>
|
||||
public CliApplicationBuilder AddCommand(CommandSchema commandSchema)
|
||||
public CliApplicationBuilder AddCommand(CommandSchema command)
|
||||
{
|
||||
_commandSchemas.Add(commandSchema);
|
||||
_commands.Add(command);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds multiple commands to the application.
|
||||
/// </summary>
|
||||
public CliApplicationBuilder AddCommands(IReadOnlyList<CommandSchema> commandSchemas)
|
||||
public CliApplicationBuilder AddCommands(IReadOnlyList<CommandSchema> commands)
|
||||
{
|
||||
foreach (var commandSchema in commandSchemas)
|
||||
AddCommand(commandSchema);
|
||||
foreach (var command in commands)
|
||||
AddCommand(command);
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -157,7 +157,7 @@ public partial class CliApplicationBuilder
|
||||
);
|
||||
|
||||
var configuration = new ApplicationConfiguration(
|
||||
new ApplicationSchema(_commandSchemas.ToArray()),
|
||||
new ApplicationSchema(_commands.ToArray()),
|
||||
_isDebugModeAllowed,
|
||||
_isPreviewModeAllowed
|
||||
);
|
||||
|
||||
@@ -8,8 +8,8 @@ namespace CliFx.Extensibility;
|
||||
public abstract class BindingConverter<T> : IBindingConverter
|
||||
{
|
||||
/// <inheritdoc cref="IBindingConverter.Convert" />
|
||||
public abstract T? Convert(string? rawArgument, IFormatProvider? formatProvider);
|
||||
public abstract T? Convert(string? rawValue, IFormatProvider? formatProvider);
|
||||
|
||||
object? IBindingConverter.Convert(string? rawArgument, IFormatProvider? formatProvider) =>
|
||||
Convert(rawArgument, formatProvider);
|
||||
object? IBindingConverter.Convert(string? rawValue, IFormatProvider? formatProvider) =>
|
||||
Convert(rawValue, formatProvider);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,6 @@ namespace CliFx.Extensibility;
|
||||
public class BoolBindingConverter : BindingConverter<bool>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override bool Convert(string? rawArgument, IFormatProvider? formatProvider) =>
|
||||
string.IsNullOrWhiteSpace(rawArgument) || bool.Parse(rawArgument);
|
||||
public override bool Convert(string? rawValue, IFormatProvider? formatProvider) =>
|
||||
string.IsNullOrWhiteSpace(rawValue) || bool.Parse(rawValue);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,6 @@ public class ConvertibleBindingConverter<T> : BindingConverter<T>
|
||||
where T : IConvertible
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override T? Convert(string? rawArgument, IFormatProvider? formatProvider) =>
|
||||
(T?)System.Convert.ChangeType(rawArgument, typeof(T), formatProvider);
|
||||
public override T? Convert(string? rawValue, IFormatProvider? formatProvider) =>
|
||||
(T?)System.Convert.ChangeType(rawValue, typeof(T), formatProvider);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,6 @@ namespace CliFx.Extensibility;
|
||||
public class DateTimeOffsetBindingConverter : BindingConverter<DateTimeOffset>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override DateTimeOffset Convert(string? rawArgument, IFormatProvider? formatProvider) =>
|
||||
DateTimeOffset.Parse(rawArgument!, formatProvider);
|
||||
public override DateTimeOffset Convert(string? rawValue, IFormatProvider? formatProvider) =>
|
||||
DateTimeOffset.Parse(rawValue!, formatProvider);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,6 @@ public class DelegateBindingConverter<T>(Func<string?, IFormatProvider?, T> conv
|
||||
: this((rawArgument, _) => convert(rawArgument)) { }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override T Convert(string? rawArgument, IFormatProvider? formatProvider) =>
|
||||
convert(rawArgument, formatProvider);
|
||||
public override T Convert(string? rawValue, IFormatProvider? formatProvider) =>
|
||||
convert(rawValue, formatProvider);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,6 @@ public class EnumBindingConverter<T> : BindingConverter<T>
|
||||
where T : struct, Enum
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override T Convert(string? rawArgument, IFormatProvider? formatProvider) =>
|
||||
(T)Enum.Parse(typeof(T), rawArgument!, true);
|
||||
public override T Convert(string? rawValue, IFormatProvider? formatProvider) =>
|
||||
(T)Enum.Parse(typeof(T), rawValue!, true);
|
||||
}
|
||||
|
||||
@@ -13,5 +13,5 @@ public interface IBindingConverter
|
||||
/// <summary>
|
||||
/// Parses the value from a raw command-line argument.
|
||||
/// </summary>
|
||||
object? Convert(string? rawArgument, IFormatProvider? formatProvider);
|
||||
object? Convert(string? rawValue, IFormatProvider? formatProvider);
|
||||
}
|
||||
|
||||
@@ -8,5 +8,5 @@ namespace CliFx.Extensibility;
|
||||
public class NoopBindingConverter : IBindingConverter
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public object? Convert(string? rawArgument, IFormatProvider? formatProvider) => rawArgument;
|
||||
public object? Convert(string? rawValue, IFormatProvider? formatProvider) => rawValue;
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@ public class NullableBindingConverter<T>(BindingConverter<T> innerConverter) : B
|
||||
where T : struct
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override T? Convert(string? rawArgument, IFormatProvider? formatProvider) =>
|
||||
!string.IsNullOrWhiteSpace(rawArgument)
|
||||
? innerConverter.Convert(rawArgument, formatProvider)
|
||||
public override T? Convert(string? rawValue, IFormatProvider? formatProvider) =>
|
||||
!string.IsNullOrWhiteSpace(rawValue)
|
||||
? innerConverter.Convert(rawValue, formatProvider)
|
||||
: null;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,6 @@ namespace CliFx.Extensibility;
|
||||
public class TimeSpanBindingConverter : BindingConverter<TimeSpan>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override TimeSpan Convert(string? rawArgument, IFormatProvider? formatProvider) =>
|
||||
TimeSpan.Parse(rawArgument!, formatProvider);
|
||||
public override TimeSpan Convert(string? rawValue, IFormatProvider? formatProvider) =>
|
||||
TimeSpan.Parse(rawValue!, formatProvider);
|
||||
}
|
||||
|
||||
65
CliFx/Formatting/CommandArgumentsConsoleFormatter.cs
Normal file
65
CliFx/Formatting/CommandArgumentsConsoleFormatter.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using CliFx.Infrastructure;
|
||||
using CliFx.Parsing;
|
||||
|
||||
namespace CliFx.Formatting;
|
||||
|
||||
internal class CommandArgumentsConsoleFormatter(ConsoleWriter consoleWriter)
|
||||
: ConsoleFormatter(consoleWriter)
|
||||
{
|
||||
public void WriteCommandArguments(CommandArguments commandArguments)
|
||||
{
|
||||
// Command name
|
||||
if (!string.IsNullOrWhiteSpace(commandArguments.CommandName))
|
||||
{
|
||||
Write(ConsoleColor.Cyan, commandArguments.CommandName);
|
||||
Write(' ');
|
||||
}
|
||||
|
||||
// Parameters
|
||||
foreach (var parameterInput in commandArguments.Parameters)
|
||||
{
|
||||
Write('<');
|
||||
Write(ConsoleColor.White, parameterInput.Value);
|
||||
Write('>');
|
||||
Write(' ');
|
||||
}
|
||||
|
||||
// Options
|
||||
foreach (var optionInput in commandArguments.Options)
|
||||
{
|
||||
Write('[');
|
||||
|
||||
// Identifier
|
||||
Write(ConsoleColor.White, optionInput.FormattedIdentifier);
|
||||
|
||||
// Value(s)
|
||||
foreach (var value in optionInput.Values)
|
||||
{
|
||||
Write(' ');
|
||||
Write('"');
|
||||
Write(value);
|
||||
Write('"');
|
||||
}
|
||||
|
||||
Write(']');
|
||||
Write(' ');
|
||||
}
|
||||
|
||||
WriteLine();
|
||||
}
|
||||
}
|
||||
|
||||
internal static class CommandInputConsoleFormatterExtensions
|
||||
{
|
||||
public static void WriteCommandInput(
|
||||
this ConsoleWriter consoleWriter,
|
||||
CommandArguments commandArguments
|
||||
) =>
|
||||
new CommandArgumentsConsoleFormatter(consoleWriter).WriteCommandArguments(commandArguments);
|
||||
|
||||
public static void WriteCommandInput(
|
||||
this IConsole console,
|
||||
CommandArguments commandArguments
|
||||
) => console.Output.WriteCommandInput(commandArguments);
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
using System;
|
||||
using CliFx.Infrastructure;
|
||||
using CliFx.Input;
|
||||
|
||||
namespace CliFx.Formatting;
|
||||
|
||||
internal class CommandInputConsoleFormatter(ConsoleWriter consoleWriter)
|
||||
: ConsoleFormatter(consoleWriter)
|
||||
{
|
||||
private void WriteCommandLineArguments(CommandInput commandInput)
|
||||
{
|
||||
Write("Command-line:");
|
||||
WriteLine();
|
||||
|
||||
WriteHorizontalMargin();
|
||||
|
||||
// Command name
|
||||
if (!string.IsNullOrWhiteSpace(commandInput.CommandName))
|
||||
{
|
||||
Write(ConsoleColor.Cyan, commandInput.CommandName);
|
||||
Write(' ');
|
||||
}
|
||||
|
||||
// Parameters
|
||||
foreach (var parameterInput in commandInput.Parameters)
|
||||
{
|
||||
Write('<');
|
||||
Write(ConsoleColor.White, parameterInput.Value);
|
||||
Write('>');
|
||||
Write(' ');
|
||||
}
|
||||
|
||||
// Options
|
||||
foreach (var optionInput in commandInput.Options)
|
||||
{
|
||||
Write('[');
|
||||
|
||||
// Identifier
|
||||
Write(ConsoleColor.White, optionInput.FormattedIdentifier);
|
||||
|
||||
// Value(s)
|
||||
foreach (var value in optionInput.Values)
|
||||
{
|
||||
Write(' ');
|
||||
Write('"');
|
||||
Write(value);
|
||||
Write('"');
|
||||
}
|
||||
|
||||
Write(']');
|
||||
Write(' ');
|
||||
}
|
||||
|
||||
WriteLine();
|
||||
}
|
||||
|
||||
private void WriteEnvironmentVariables(CommandInput commandInput)
|
||||
{
|
||||
Write("Environment:");
|
||||
WriteLine();
|
||||
|
||||
// Environment variables
|
||||
foreach (var environmentVariableInput in commandInput.EnvironmentVariables)
|
||||
{
|
||||
WriteHorizontalMargin();
|
||||
|
||||
// Name
|
||||
Write(ConsoleColor.White, environmentVariableInput.Name);
|
||||
|
||||
Write('=');
|
||||
|
||||
// Value
|
||||
Write('"');
|
||||
Write(environmentVariableInput.Value);
|
||||
Write('"');
|
||||
|
||||
WriteLine();
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteCommandInput(CommandInput commandInput)
|
||||
{
|
||||
WriteCommandLineArguments(commandInput);
|
||||
WriteLine();
|
||||
WriteEnvironmentVariables(commandInput);
|
||||
}
|
||||
}
|
||||
|
||||
internal static class CommandInputConsoleFormatterExtensions
|
||||
{
|
||||
public static void WriteCommandInput(
|
||||
this ConsoleWriter consoleWriter,
|
||||
CommandInput commandInput
|
||||
) => new CommandInputConsoleFormatter(consoleWriter).WriteCommandInput(commandInput);
|
||||
|
||||
public static void WriteCommandInput(this IConsole console, CommandInput commandInput) =>
|
||||
console.Output.WriteCommandInput(commandInput);
|
||||
}
|
||||
@@ -20,13 +20,13 @@ internal class HelpConsoleFormatter(ConsoleWriter consoleWriter, HelpContext con
|
||||
|
||||
private void WriteCommandInvocation()
|
||||
{
|
||||
Write(context.ApplicationMetadata.ExecutableName);
|
||||
Write(context.Metadata.ExecutableName);
|
||||
|
||||
// Command name
|
||||
if (!string.IsNullOrWhiteSpace(context.CommandSchema.Name))
|
||||
if (!string.IsNullOrWhiteSpace(context.Command.Name))
|
||||
{
|
||||
Write(' ');
|
||||
Write(ConsoleColor.Cyan, context.CommandSchema.Name);
|
||||
Write(ConsoleColor.Cyan, context.Command.Name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,16 +36,16 @@ internal class HelpConsoleFormatter(ConsoleWriter consoleWriter, HelpContext con
|
||||
WriteVerticalMargin();
|
||||
|
||||
// Title and version
|
||||
Write(ConsoleColor.White, context.ApplicationMetadata.Title);
|
||||
Write(ConsoleColor.White, context.Metadata.Title);
|
||||
Write(' ');
|
||||
Write(ConsoleColor.Yellow, context.ApplicationMetadata.Version);
|
||||
Write(ConsoleColor.Yellow, context.Metadata.Version);
|
||||
WriteLine();
|
||||
|
||||
// Description
|
||||
if (!string.IsNullOrWhiteSpace(context.ApplicationMetadata.Description))
|
||||
if (!string.IsNullOrWhiteSpace(context.Metadata.Description))
|
||||
{
|
||||
WriteHorizontalMargin();
|
||||
Write(context.ApplicationMetadata.Description);
|
||||
Write(context.Metadata.Description);
|
||||
WriteLine();
|
||||
}
|
||||
}
|
||||
@@ -65,7 +65,7 @@ internal class HelpConsoleFormatter(ConsoleWriter consoleWriter, HelpContext con
|
||||
Write(' ');
|
||||
|
||||
// Parameters
|
||||
foreach (var parameter in context.CommandSchema.Parameters.OrderBy(p => p.Order))
|
||||
foreach (var parameter in context.Command.Parameters.OrderBy(p => p.Order))
|
||||
{
|
||||
Write(
|
||||
ConsoleColor.DarkCyan,
|
||||
@@ -75,7 +75,7 @@ internal class HelpConsoleFormatter(ConsoleWriter consoleWriter, HelpContext con
|
||||
}
|
||||
|
||||
// Required options
|
||||
foreach (var option in context.CommandSchema.Options.Where(o => o.IsRequired))
|
||||
foreach (var option in context.Command.Options.Where(o => o.IsRequired))
|
||||
{
|
||||
Write(
|
||||
ConsoleColor.Yellow,
|
||||
@@ -90,7 +90,7 @@ internal class HelpConsoleFormatter(ConsoleWriter consoleWriter, HelpContext con
|
||||
}
|
||||
|
||||
// Placeholder for non-required options
|
||||
if (context.CommandSchema.Options.Any(o => !o.IsRequired))
|
||||
if (context.Command.Options.Any(o => !o.IsRequired))
|
||||
{
|
||||
Write(ConsoleColor.Yellow, "[options]");
|
||||
}
|
||||
@@ -99,11 +99,9 @@ internal class HelpConsoleFormatter(ConsoleWriter consoleWriter, HelpContext con
|
||||
}
|
||||
|
||||
// Child command usage
|
||||
var childCommandSchemas = context.ApplicationSchema.GetChildCommands(
|
||||
context.CommandSchema.Name
|
||||
);
|
||||
var childCommands = context.Application.GetChildCommands(context.Command.Name);
|
||||
|
||||
if (childCommandSchemas.Any())
|
||||
if (childCommands.Any())
|
||||
{
|
||||
WriteHorizontalMargin();
|
||||
|
||||
@@ -123,7 +121,7 @@ internal class HelpConsoleFormatter(ConsoleWriter consoleWriter, HelpContext con
|
||||
|
||||
private void WriteCommandDescription()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(context.CommandSchema.Description))
|
||||
if (string.IsNullOrWhiteSpace(context.Command.Description))
|
||||
return;
|
||||
|
||||
if (!IsEmpty)
|
||||
@@ -133,13 +131,13 @@ internal class HelpConsoleFormatter(ConsoleWriter consoleWriter, HelpContext con
|
||||
|
||||
WriteHorizontalMargin();
|
||||
|
||||
Write(context.CommandSchema.Description);
|
||||
Write(context.Command.Description);
|
||||
WriteLine();
|
||||
}
|
||||
|
||||
private void WriteCommandParameters()
|
||||
{
|
||||
if (!context.CommandSchema.Parameters.Any())
|
||||
if (!context.Command.Parameters.Any())
|
||||
return;
|
||||
|
||||
if (!IsEmpty)
|
||||
@@ -147,9 +145,9 @@ internal class HelpConsoleFormatter(ConsoleWriter consoleWriter, HelpContext con
|
||||
|
||||
WriteHeader("Parameters");
|
||||
|
||||
foreach (var parameterSchema in context.CommandSchema.Parameters.OrderBy(p => p.Order))
|
||||
foreach (var parameter in context.Command.Parameters.OrderBy(p => p.Order))
|
||||
{
|
||||
if (parameterSchema.IsRequired)
|
||||
if (parameter.IsRequired)
|
||||
{
|
||||
Write(ConsoleColor.Red, "* ");
|
||||
}
|
||||
@@ -158,19 +156,19 @@ internal class HelpConsoleFormatter(ConsoleWriter consoleWriter, HelpContext con
|
||||
WriteHorizontalMargin();
|
||||
}
|
||||
|
||||
Write(ConsoleColor.DarkCyan, $"{parameterSchema.Name}");
|
||||
Write(ConsoleColor.DarkCyan, $"{parameter.Name}");
|
||||
|
||||
WriteColumnMargin();
|
||||
|
||||
// Description
|
||||
if (!string.IsNullOrWhiteSpace(parameterSchema.Description))
|
||||
if (!string.IsNullOrWhiteSpace(parameter.Description))
|
||||
{
|
||||
Write(parameterSchema.Description);
|
||||
Write(parameter.Description);
|
||||
Write(' ');
|
||||
}
|
||||
|
||||
// Valid values
|
||||
var validValues = parameterSchema.Property.TryGetValidValues();
|
||||
var validValues = parameter.Property.TryGetValidValues();
|
||||
if (validValues?.Any() == true)
|
||||
{
|
||||
Write(ConsoleColor.White, "Choices: ");
|
||||
@@ -200,9 +198,9 @@ internal class HelpConsoleFormatter(ConsoleWriter consoleWriter, HelpContext con
|
||||
}
|
||||
|
||||
// Default value
|
||||
if (!parameterSchema.IsRequired)
|
||||
if (!parameter.IsRequired)
|
||||
{
|
||||
WriteDefaultValue(parameterSchema);
|
||||
WriteDefaultValue(parameter);
|
||||
}
|
||||
|
||||
WriteLine();
|
||||
@@ -216,9 +214,7 @@ internal class HelpConsoleFormatter(ConsoleWriter consoleWriter, HelpContext con
|
||||
|
||||
WriteHeader("Options");
|
||||
|
||||
foreach (
|
||||
var optionSchema in context.CommandSchema.Options.OrderByDescending(o => o.IsRequired)
|
||||
)
|
||||
foreach (var optionSchema in context.Command.Options.OrderByDescending(o => o.IsRequired))
|
||||
{
|
||||
if (optionSchema.IsRequired)
|
||||
{
|
||||
@@ -358,12 +354,12 @@ internal class HelpConsoleFormatter(ConsoleWriter consoleWriter, HelpContext con
|
||||
|
||||
private void WriteCommandChildren()
|
||||
{
|
||||
var childCommandSchemas = context
|
||||
.ApplicationSchema.GetChildCommands(context.CommandSchema.Name)
|
||||
var childCommands = context
|
||||
.Application.GetChildCommands(context.Command.Name)
|
||||
.OrderBy(a => a.Name, StringComparer.Ordinal)
|
||||
.ToArray();
|
||||
|
||||
if (!childCommandSchemas.Any())
|
||||
if (!childCommands.Any())
|
||||
return;
|
||||
|
||||
if (!IsEmpty)
|
||||
@@ -371,14 +367,14 @@ internal class HelpConsoleFormatter(ConsoleWriter consoleWriter, HelpContext con
|
||||
|
||||
WriteHeader("Commands");
|
||||
|
||||
foreach (var childCommandSchema in childCommandSchemas)
|
||||
foreach (var childCommandSchema in childCommands)
|
||||
{
|
||||
// Name
|
||||
WriteHorizontalMargin();
|
||||
Write(
|
||||
ConsoleColor.Cyan,
|
||||
// Relative to current command
|
||||
childCommandSchema.Name?.Substring(context.CommandSchema.Name?.Length ?? 0).Trim()
|
||||
childCommandSchema.Name?.Substring(context.Command.Name?.Length ?? 0).Trim()
|
||||
);
|
||||
|
||||
WriteColumnMargin();
|
||||
@@ -391,17 +387,17 @@ internal class HelpConsoleFormatter(ConsoleWriter consoleWriter, HelpContext con
|
||||
}
|
||||
|
||||
// Child commands of child command
|
||||
var grandChildCommandSchemas = context
|
||||
.ApplicationSchema.GetChildCommands(childCommandSchema.Name)
|
||||
var grandChildCommands = context
|
||||
.Application.GetChildCommands(childCommandSchema.Name)
|
||||
.OrderBy(c => c.Name, StringComparer.Ordinal)
|
||||
.ToArray();
|
||||
|
||||
if (grandChildCommandSchemas.Any())
|
||||
if (grandChildCommands.Any())
|
||||
{
|
||||
Write(ConsoleColor.White, "Subcommands: ");
|
||||
|
||||
var isFirst = true;
|
||||
foreach (var grandChildCommandSchema in grandChildCommandSchemas)
|
||||
foreach (var grandChildCommandSchema in grandChildCommands)
|
||||
{
|
||||
if (isFirst)
|
||||
{
|
||||
@@ -416,7 +412,7 @@ internal class HelpConsoleFormatter(ConsoleWriter consoleWriter, HelpContext con
|
||||
ConsoleColor.Cyan,
|
||||
// Relative to current command (not the parent)
|
||||
grandChildCommandSchema
|
||||
.Name?.Substring(context.CommandSchema.Name?.Length ?? 0)
|
||||
.Name?.Substring(context.Command.Name?.Length ?? 0)
|
||||
.Trim()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,17 +4,17 @@ using CliFx.Schema;
|
||||
namespace CliFx.Formatting;
|
||||
|
||||
internal class HelpContext(
|
||||
ApplicationMetadata applicationMetadata,
|
||||
ApplicationSchema applicationSchema,
|
||||
CommandSchema commandSchema,
|
||||
ApplicationMetadata metadata,
|
||||
ApplicationSchema application,
|
||||
CommandSchema command,
|
||||
IReadOnlyDictionary<CommandInputSchema, object?> commandDefaultValues
|
||||
)
|
||||
{
|
||||
public ApplicationMetadata ApplicationMetadata { get; } = applicationMetadata;
|
||||
public ApplicationMetadata Metadata { get; } = metadata;
|
||||
|
||||
public ApplicationSchema ApplicationSchema { get; } = applicationSchema;
|
||||
public ApplicationSchema Application { get; } = application;
|
||||
|
||||
public CommandSchema CommandSchema { get; } = commandSchema;
|
||||
public CommandSchema Command { get; } = command;
|
||||
|
||||
public IReadOnlyDictionary<CommandInputSchema, object?> CommandDefaultValues { get; } =
|
||||
commandDefaultValues;
|
||||
|
||||
@@ -1,232 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using CliFx.Utils.Extensions;
|
||||
|
||||
namespace CliFx.Input;
|
||||
|
||||
/// <summary>
|
||||
/// Input provided by the user for a command.
|
||||
/// </summary>
|
||||
public partial class CommandInput(
|
||||
string? commandName,
|
||||
IReadOnlyList<CommandDirectiveInput> directives,
|
||||
IReadOnlyList<CommandParameterInput> parameters,
|
||||
IReadOnlyList<CommandOptionInput> options,
|
||||
IReadOnlyList<EnvironmentVariableInput> environmentVariables
|
||||
)
|
||||
{
|
||||
/// <summary>
|
||||
/// Name of the requested command.
|
||||
/// </summary>
|
||||
public string? CommandName { get; } = commandName;
|
||||
|
||||
/// <summary>
|
||||
/// Provided directives.
|
||||
/// </summary>
|
||||
public IReadOnlyList<CommandDirectiveInput> Directives { get; } = directives;
|
||||
|
||||
/// <summary>
|
||||
/// Provided parameters.
|
||||
/// </summary>
|
||||
public IReadOnlyList<CommandParameterInput> Parameters { get; } = parameters;
|
||||
|
||||
/// <summary>
|
||||
/// Provided options.
|
||||
/// </summary>
|
||||
public IReadOnlyList<CommandOptionInput> Options { get; } = options;
|
||||
|
||||
/// <summary>
|
||||
/// Provided environment variables.
|
||||
/// </summary>
|
||||
public IReadOnlyList<EnvironmentVariableInput> EnvironmentVariables { get; } =
|
||||
environmentVariables;
|
||||
|
||||
internal bool IsDebugDirectiveSpecified => Directives.Any(d => d.IsDebugDirective);
|
||||
|
||||
internal bool IsPreviewDirectiveSpecified => Directives.Any(d => d.IsPreviewDirective);
|
||||
}
|
||||
|
||||
public partial class CommandInput
|
||||
{
|
||||
private static IReadOnlyList<CommandDirectiveInput> ParseDirectives(
|
||||
IReadOnlyList<string> commandLineArguments,
|
||||
ref int index
|
||||
)
|
||||
{
|
||||
var result = new List<CommandDirectiveInput>();
|
||||
|
||||
// Consume all consecutive directive arguments
|
||||
for (; index < commandLineArguments.Count; index++)
|
||||
{
|
||||
var argument = commandLineArguments[index];
|
||||
|
||||
// Break on the first non-directive argument
|
||||
if (!argument.StartsWith('[') || !argument.EndsWith(']'))
|
||||
break;
|
||||
|
||||
var directiveName = argument.Substring(1, argument.Length - 2);
|
||||
result.Add(new CommandDirectiveInput(directiveName));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static string? ParseCommandName(
|
||||
IReadOnlyList<string> commandLineArguments,
|
||||
ISet<string> commandNames,
|
||||
ref int index
|
||||
)
|
||||
{
|
||||
var potentialCommandNameComponents = new List<string>();
|
||||
var commandName = default(string?);
|
||||
|
||||
var lastIndex = index;
|
||||
|
||||
// Append arguments to a buffer until we find the longest sequence that represents
|
||||
// a valid command name.
|
||||
for (var i = index; i < commandLineArguments.Count; i++)
|
||||
{
|
||||
var argument = commandLineArguments[i];
|
||||
|
||||
potentialCommandNameComponents.Add(argument);
|
||||
|
||||
var potentialCommandName = potentialCommandNameComponents.JoinToString(" ");
|
||||
if (commandNames.Contains(potentialCommandName))
|
||||
{
|
||||
// Record the position but continue the loop in case we find
|
||||
// a longer (more specific) match.
|
||||
commandName = potentialCommandName;
|
||||
lastIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
// Move the index to the position where the command name ended
|
||||
if (!string.IsNullOrWhiteSpace(commandName))
|
||||
index = lastIndex + 1;
|
||||
|
||||
return commandName;
|
||||
}
|
||||
|
||||
private static IReadOnlyList<CommandParameterInput> ParseParameters(
|
||||
IReadOnlyList<string> commandLineArguments,
|
||||
ref int index
|
||||
)
|
||||
{
|
||||
var result = new List<CommandParameterInput>();
|
||||
|
||||
// Consume all arguments until the first option identifier
|
||||
for (; index < commandLineArguments.Count; index++)
|
||||
{
|
||||
var argument = commandLineArguments[index];
|
||||
|
||||
var isOptionIdentifier =
|
||||
// Name
|
||||
argument.StartsWith("--", StringComparison.Ordinal)
|
||||
&& argument.Length > 2
|
||||
&& char.IsLetter(argument[2])
|
||||
||
|
||||
// Short name
|
||||
argument.StartsWith('-')
|
||||
&& argument.Length > 1
|
||||
&& char.IsLetter(argument[1]);
|
||||
|
||||
// Break on the first option identifier
|
||||
if (isOptionIdentifier)
|
||||
break;
|
||||
|
||||
result.Add(new CommandParameterInput(index, argument));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static IReadOnlyList<CommandOptionInput> ParseOptions(
|
||||
IReadOnlyList<string> commandLineArguments,
|
||||
ref int index
|
||||
)
|
||||
{
|
||||
var result = new List<CommandOptionInput>();
|
||||
|
||||
var lastOptionIdentifier = default(string?);
|
||||
var lastOptionValues = new List<string>();
|
||||
|
||||
// Consume and group all remaining arguments into options
|
||||
for (; index < commandLineArguments.Count; index++)
|
||||
{
|
||||
var argument = commandLineArguments[index];
|
||||
|
||||
// Name
|
||||
if (
|
||||
argument.StartsWith("--", StringComparison.Ordinal)
|
||||
&& argument.Length > 2
|
||||
&& char.IsLetter(argument[2])
|
||||
)
|
||||
{
|
||||
// Flush previous
|
||||
if (!string.IsNullOrWhiteSpace(lastOptionIdentifier))
|
||||
result.Add(new CommandOptionInput(lastOptionIdentifier, lastOptionValues));
|
||||
|
||||
lastOptionIdentifier = argument[2..];
|
||||
lastOptionValues = [];
|
||||
}
|
||||
// Short name
|
||||
else if (argument.StartsWith('-') && argument.Length > 1 && char.IsLetter(argument[1]))
|
||||
{
|
||||
foreach (var identifier in argument[1..])
|
||||
{
|
||||
// Flush previous
|
||||
if (!string.IsNullOrWhiteSpace(lastOptionIdentifier))
|
||||
result.Add(new CommandOptionInput(lastOptionIdentifier, lastOptionValues));
|
||||
|
||||
lastOptionIdentifier = identifier.AsString();
|
||||
lastOptionValues = [];
|
||||
}
|
||||
}
|
||||
// Value
|
||||
else if (!string.IsNullOrWhiteSpace(lastOptionIdentifier))
|
||||
{
|
||||
lastOptionValues.Add(argument);
|
||||
}
|
||||
}
|
||||
|
||||
// Flush the last option
|
||||
if (!string.IsNullOrWhiteSpace(lastOptionIdentifier))
|
||||
result.Add(new CommandOptionInput(lastOptionIdentifier, lastOptionValues));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static CommandInput Parse(
|
||||
IReadOnlyList<string> commandLineArguments,
|
||||
IReadOnlyDictionary<string, string> environmentVariables,
|
||||
IReadOnlyList<string> availableCommandNames
|
||||
)
|
||||
{
|
||||
var index = 0;
|
||||
|
||||
var parsedDirectives = ParseDirectives(commandLineArguments, ref index);
|
||||
|
||||
var parsedCommandName = ParseCommandName(
|
||||
commandLineArguments,
|
||||
availableCommandNames.ToHashSet(StringComparer.OrdinalIgnoreCase),
|
||||
ref index
|
||||
);
|
||||
|
||||
var parsedParameters = ParseParameters(commandLineArguments, ref index);
|
||||
|
||||
var parsedOptions = ParseOptions(commandLineArguments, ref index);
|
||||
|
||||
var parsedEnvironmentVariables = environmentVariables
|
||||
.Select(kvp => new EnvironmentVariableInput(kvp.Key, kvp.Value))
|
||||
.ToArray();
|
||||
|
||||
return new CommandInput(
|
||||
parsedCommandName,
|
||||
parsedDirectives,
|
||||
parsedParameters,
|
||||
parsedOptions,
|
||||
parsedEnvironmentVariables
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace CliFx.Input;
|
||||
|
||||
/// <summary>
|
||||
/// Input provided by the means of an environment variable.
|
||||
/// </summary>
|
||||
public class EnvironmentVariableInput(string name, string value)
|
||||
{
|
||||
/// <summary>
|
||||
/// Environment variable name.
|
||||
/// </summary>
|
||||
public string Name { get; } = name;
|
||||
|
||||
/// <summary>
|
||||
/// Environment variable value.
|
||||
/// </summary>
|
||||
public string Value { get; } = value;
|
||||
|
||||
internal IReadOnlyList<string> SplitValues() => Value.Split(Path.PathSeparator);
|
||||
}
|
||||
218
CliFx/Parsing/CommandArguments.cs
Normal file
218
CliFx/Parsing/CommandArguments.cs
Normal file
@@ -0,0 +1,218 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using CliFx.Utils.Extensions;
|
||||
|
||||
namespace CliFx.Parsing;
|
||||
|
||||
/// <summary>
|
||||
/// Command-line arguments provided by the user, parsed into their semantic form.
|
||||
/// </summary>
|
||||
public partial class CommandArguments(
|
||||
string? commandName,
|
||||
IReadOnlyList<CommandDirectiveToken> directives,
|
||||
IReadOnlyList<CommandParameterToken> parameters,
|
||||
IReadOnlyList<CommandOptionToken> options
|
||||
)
|
||||
{
|
||||
/// <summary>
|
||||
/// Name of the requested command.
|
||||
/// </summary>
|
||||
public string? CommandName { get; } = commandName;
|
||||
|
||||
/// <summary>
|
||||
/// Provided directives.
|
||||
/// </summary>
|
||||
public IReadOnlyList<CommandDirectiveToken> Directives { get; } = directives;
|
||||
|
||||
/// <summary>
|
||||
/// Provided parameters.
|
||||
/// </summary>
|
||||
public IReadOnlyList<CommandParameterToken> Parameters { get; } = parameters;
|
||||
|
||||
/// <summary>
|
||||
/// Provided options.
|
||||
/// </summary>
|
||||
public IReadOnlyList<CommandOptionToken> Options { get; } = options;
|
||||
|
||||
internal bool IsDebugDirectiveSpecified => Directives.Any(d => d.IsDebugDirective);
|
||||
|
||||
internal bool IsPreviewDirectiveSpecified => Directives.Any(d => d.IsPreviewDirective);
|
||||
}
|
||||
|
||||
public partial class CommandArguments
|
||||
{
|
||||
private static IReadOnlyList<CommandDirectiveToken> ParseDirectives(
|
||||
IReadOnlyList<string> rawArguments,
|
||||
ref int index
|
||||
)
|
||||
{
|
||||
var result = new List<CommandDirectiveToken>();
|
||||
|
||||
// Consume all consecutive directive arguments
|
||||
for (; index < rawArguments.Count; index++)
|
||||
{
|
||||
var rawArgument = rawArguments[index];
|
||||
|
||||
// Break on the first non-directive argument
|
||||
if (!rawArgument.StartsWith('[') || !rawArgument.EndsWith(']'))
|
||||
break;
|
||||
|
||||
var directiveName = rawArgument.Substring(1, rawArgument.Length - 2);
|
||||
result.Add(new CommandDirectiveToken(directiveName));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static string? ParseCommandName(
|
||||
IReadOnlyList<string> rawArguments,
|
||||
ISet<string> commandNames,
|
||||
ref int index
|
||||
)
|
||||
{
|
||||
var potentialCommandNameComponents = new List<string>();
|
||||
var commandName = default(string?);
|
||||
|
||||
var lastIndex = index;
|
||||
|
||||
// Append arguments to a buffer until we find the longest sequence that represents
|
||||
// a valid command name.
|
||||
for (var i = index; i < rawArguments.Count; i++)
|
||||
{
|
||||
var rawArgument = rawArguments[i];
|
||||
|
||||
potentialCommandNameComponents.Add(rawArgument);
|
||||
|
||||
var potentialCommandName = potentialCommandNameComponents.JoinToString(" ");
|
||||
if (commandNames.Contains(potentialCommandName))
|
||||
{
|
||||
// Record the position but continue the loop in case we find
|
||||
// a longer (more specific) match.
|
||||
commandName = potentialCommandName;
|
||||
lastIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
// Move the index to the position where the command name ended
|
||||
if (!string.IsNullOrWhiteSpace(commandName))
|
||||
index = lastIndex + 1;
|
||||
|
||||
return commandName;
|
||||
}
|
||||
|
||||
private static IReadOnlyList<CommandParameterToken> ParseParameters(
|
||||
IReadOnlyList<string> rawArguments,
|
||||
ref int index
|
||||
)
|
||||
{
|
||||
var result = new List<CommandParameterToken>();
|
||||
|
||||
// Consume all arguments until the first option identifier
|
||||
for (; index < rawArguments.Count; index++)
|
||||
{
|
||||
var rawArgument = rawArguments[index];
|
||||
|
||||
var isOptionIdentifier =
|
||||
// Name
|
||||
rawArgument.StartsWith("--", StringComparison.Ordinal)
|
||||
&& rawArgument.Length > 2
|
||||
&& char.IsLetter(rawArgument[2])
|
||||
||
|
||||
// Short name
|
||||
rawArgument.StartsWith('-')
|
||||
&& rawArgument.Length > 1
|
||||
&& char.IsLetter(rawArgument[1]);
|
||||
|
||||
// Break on the first option identifier
|
||||
if (isOptionIdentifier)
|
||||
break;
|
||||
|
||||
result.Add(new CommandParameterToken(index, rawArgument));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static IReadOnlyList<CommandOptionToken> ParseOptions(
|
||||
IReadOnlyList<string> rawArguments,
|
||||
ref int index
|
||||
)
|
||||
{
|
||||
var result = new List<CommandOptionToken>();
|
||||
|
||||
var lastOptionIdentifier = default(string?);
|
||||
var lastOptionValues = new List<string>();
|
||||
|
||||
// Consume and group all remaining arguments into options
|
||||
for (; index < rawArguments.Count; index++)
|
||||
{
|
||||
var rawArgument = rawArguments[index];
|
||||
|
||||
// Name
|
||||
if (
|
||||
rawArgument.StartsWith("--", StringComparison.Ordinal)
|
||||
&& rawArgument.Length > 2
|
||||
&& char.IsLetter(rawArgument[2])
|
||||
)
|
||||
{
|
||||
// Flush previous
|
||||
if (!string.IsNullOrWhiteSpace(lastOptionIdentifier))
|
||||
result.Add(new CommandOptionToken(lastOptionIdentifier, lastOptionValues));
|
||||
|
||||
lastOptionIdentifier = rawArgument[2..];
|
||||
lastOptionValues = [];
|
||||
}
|
||||
// Short name
|
||||
else if (
|
||||
rawArgument.StartsWith('-')
|
||||
&& rawArgument.Length > 1
|
||||
&& char.IsLetter(rawArgument[1])
|
||||
)
|
||||
{
|
||||
foreach (var identifier in rawArgument[1..])
|
||||
{
|
||||
// Flush previous
|
||||
if (!string.IsNullOrWhiteSpace(lastOptionIdentifier))
|
||||
result.Add(new CommandOptionToken(lastOptionIdentifier, lastOptionValues));
|
||||
|
||||
lastOptionIdentifier = identifier.AsString();
|
||||
lastOptionValues = [];
|
||||
}
|
||||
}
|
||||
// Value
|
||||
else if (!string.IsNullOrWhiteSpace(lastOptionIdentifier))
|
||||
{
|
||||
lastOptionValues.Add(rawArgument);
|
||||
}
|
||||
}
|
||||
|
||||
// Flush the last option
|
||||
if (!string.IsNullOrWhiteSpace(lastOptionIdentifier))
|
||||
result.Add(new CommandOptionToken(lastOptionIdentifier, lastOptionValues));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static CommandArguments Parse(
|
||||
IReadOnlyList<string> rawArguments,
|
||||
IReadOnlyList<string> availableCommandNames
|
||||
)
|
||||
{
|
||||
var index = 0;
|
||||
|
||||
var directives = ParseDirectives(rawArguments, ref index);
|
||||
|
||||
var commandName = ParseCommandName(
|
||||
rawArguments,
|
||||
availableCommandNames.ToHashSet(StringComparer.OrdinalIgnoreCase),
|
||||
ref index
|
||||
);
|
||||
|
||||
var parameters = ParseParameters(rawArguments, ref index);
|
||||
|
||||
var options = ParseOptions(rawArguments, ref index);
|
||||
|
||||
return new CommandArguments(commandName, directives, parameters, options);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace CliFx.Input;
|
||||
namespace CliFx.Parsing;
|
||||
|
||||
/// <summary>
|
||||
/// Input provided by the means of a directive.
|
||||
/// Command-line argument that sets a directive.
|
||||
/// </summary>
|
||||
public class CommandDirectiveInput(string name)
|
||||
public class CommandDirectiveToken(string name)
|
||||
{
|
||||
/// <summary>
|
||||
/// Directive name.
|
||||
@@ -1,14 +1,14 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace CliFx.Input;
|
||||
namespace CliFx.Parsing;
|
||||
|
||||
/// <summary>
|
||||
/// Input provided by the means of an option.
|
||||
/// Command-line arguments that provide one or more values to an option input of a command.
|
||||
/// </summary>
|
||||
public class CommandOptionInput(string identifier, IReadOnlyList<string> values)
|
||||
public class CommandOptionToken(string identifier, IReadOnlyList<string> values)
|
||||
{
|
||||
/// <summary>
|
||||
/// Option identifier (either the name or the short name).
|
||||
/// Option identifier (either name or short name).
|
||||
/// </summary>
|
||||
public string Identifier { get; } = identifier;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
namespace CliFx.Input;
|
||||
namespace CliFx.Parsing;
|
||||
|
||||
/// <summary>
|
||||
/// Input provided by the means of a parameter.
|
||||
/// Command-line argument that provide a value to a parameter input of a command.
|
||||
/// </summary>
|
||||
public class CommandParameterInput(int order, string value)
|
||||
public class CommandParameterToken(int order, string value)
|
||||
{
|
||||
/// <summary>
|
||||
/// Parameter order.
|
||||
@@ -15,5 +15,5 @@ public class CommandParameterInput(int order, string value)
|
||||
/// </summary>
|
||||
public string Value { get; } = value;
|
||||
|
||||
internal string GetFormattedIdentifier() => $"<{Value}>";
|
||||
internal string FormattedIdentifier { get; } = $"<{value}>";
|
||||
}
|
||||
@@ -66,7 +66,7 @@ public abstract class CommandInputSchema(
|
||||
}
|
||||
}
|
||||
|
||||
internal void Activate(ICommand instance, IReadOnlyList<string?> rawArguments)
|
||||
internal void Activate(ICommand instance, IReadOnlyList<string?> rawValues)
|
||||
{
|
||||
var formatProvider = CultureInfo.InvariantCulture;
|
||||
|
||||
@@ -75,31 +75,29 @@ public abstract class CommandInputSchema(
|
||||
// Multiple values expected, single or multiple values provided
|
||||
if (IsSequence)
|
||||
{
|
||||
var value = rawArguments
|
||||
.Select(v => Converter.Convert(v, formatProvider))
|
||||
.ToArray();
|
||||
var values = rawValues.Select(v => Converter.Convert(v, formatProvider)).ToArray();
|
||||
|
||||
// TODO: cast array to the proper type
|
||||
|
||||
Validate(value);
|
||||
Validate(values);
|
||||
|
||||
Property.SetValue(instance, value);
|
||||
Property.Set(instance, values);
|
||||
}
|
||||
// Single value expected, single value provided
|
||||
else if (rawArguments.Count <= 1)
|
||||
else if (rawValues.Count <= 1)
|
||||
{
|
||||
var value = Converter.Convert(rawArguments.SingleOrDefault(), formatProvider);
|
||||
var value = Converter.Convert(rawValues.SingleOrDefault(), formatProvider);
|
||||
Validate(value);
|
||||
|
||||
Property.SetValue(instance, value);
|
||||
Property.Set(instance, value);
|
||||
}
|
||||
// Single value expected, multiple values provided
|
||||
else
|
||||
{
|
||||
throw CliFxException.UserError(
|
||||
$"""
|
||||
{Kind} {FormattedIdentifier} expects a single argument, but provided with multiple:
|
||||
{rawArguments.Select(v => '<' + v + '>').JoinToString(" ")}
|
||||
{Kind} {FormattedIdentifier} expects a single value, but provided with multiple:
|
||||
{rawValues.Select(v => '<' + v + '>').JoinToString(" ")}
|
||||
"""
|
||||
);
|
||||
}
|
||||
@@ -108,8 +106,8 @@ public abstract class CommandInputSchema(
|
||||
{
|
||||
throw CliFxException.UserError(
|
||||
$"""
|
||||
{Kind} {FormattedIdentifier} cannot be set from the provided argument(s):
|
||||
{rawArguments.Select(v => '<' + v + '>').JoinToString(" ")}
|
||||
{Kind} {FormattedIdentifier} cannot be set from the provided value(s):
|
||||
{rawValues.Select(v => '<' + v + '>').JoinToString(" ")}
|
||||
Error: {ex.Message}
|
||||
""",
|
||||
ex
|
||||
|
||||
@@ -27,7 +27,7 @@ public class CommandParameterSchema(
|
||||
public int Order { get; } = order;
|
||||
|
||||
/// <summary>
|
||||
/// Parameter name.
|
||||
/// Parameter name, used in the help text.
|
||||
/// </summary>
|
||||
public string Name { get; } = name;
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using CliFx.Exceptions;
|
||||
using CliFx.Input;
|
||||
using CliFx.Parsing;
|
||||
using CliFx.Utils.Extensions;
|
||||
|
||||
namespace CliFx.Schema;
|
||||
@@ -65,74 +66,74 @@ public class CommandSchema(
|
||||
{
|
||||
var result = new Dictionary<CommandInputSchema, object?>();
|
||||
|
||||
foreach (var parameterSchema in Parameters)
|
||||
foreach (var parameter in Parameters)
|
||||
{
|
||||
var value = parameterSchema.Property.GetValue(instance);
|
||||
result[parameterSchema] = value;
|
||||
var value = parameter.Property.Get(instance);
|
||||
result[parameter] = value;
|
||||
}
|
||||
|
||||
foreach (var optionSchema in Options)
|
||||
foreach (var option in Options)
|
||||
{
|
||||
var value = optionSchema.Property.GetValue(instance);
|
||||
result[optionSchema] = value;
|
||||
var value = option.Property.Get(instance);
|
||||
result[option] = value;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void ActivateParameters(ICommand instance, CommandInput input)
|
||||
private void ActivateParameters(ICommand instance, CommandArguments arguments)
|
||||
{
|
||||
// Ensure there are no unexpected parameters and that all parameters are provided
|
||||
var remainingParameterInputs = input.Parameters.ToList();
|
||||
var remainingRequiredParameterSchemas = Parameters.Where(p => p.IsRequired).ToList();
|
||||
var remainingParameterTokens = arguments.Parameters.ToList();
|
||||
var remainingRequiredParameters = Parameters.Where(p => p.IsRequired).ToList();
|
||||
|
||||
var position = 0;
|
||||
|
||||
foreach (var parameterSchema in Parameters.OrderBy(p => p.Order))
|
||||
foreach (var parameter in Parameters.OrderBy(p => p.Order))
|
||||
{
|
||||
// Break when there are no remaining inputs
|
||||
if (position >= input.Parameters.Count)
|
||||
if (position >= arguments.Parameters.Count)
|
||||
break;
|
||||
|
||||
// Non-sequence: take one input at the current position
|
||||
if (!parameterSchema.IsSequence)
|
||||
{
|
||||
var parameterInput = input.Parameters[position];
|
||||
parameterSchema.Activate(instance, [parameterInput.Value]);
|
||||
|
||||
position++;
|
||||
remainingParameterInputs.Remove(parameterInput);
|
||||
}
|
||||
// Sequence: take all remaining inputs starting from the current position
|
||||
if (parameter.IsSequence)
|
||||
{
|
||||
var parameterTokens = arguments.Parameters.Skip(position).ToArray();
|
||||
|
||||
parameter.Activate(instance, parameterTokens.Select(p => p.Value).ToArray());
|
||||
|
||||
position += parameterTokens.Length;
|
||||
remainingParameterTokens.RemoveRange(parameterTokens);
|
||||
}
|
||||
// Non-sequence: take one input at the current position
|
||||
else
|
||||
{
|
||||
var parameterInputs = input.Parameters.Skip(position).ToArray();
|
||||
var parameterToken = arguments.Parameters[position];
|
||||
parameter.Activate(instance, [parameterToken.Value]);
|
||||
|
||||
parameterSchema.Activate(instance, parameterInputs.Select(p => p.Value).ToArray());
|
||||
|
||||
position += parameterInputs.Length;
|
||||
remainingParameterInputs.RemoveRange(parameterInputs);
|
||||
position++;
|
||||
remainingParameterTokens.Remove(parameterToken);
|
||||
}
|
||||
|
||||
remainingRequiredParameterSchemas.Remove(parameterSchema);
|
||||
remainingRequiredParameters.Remove(parameter);
|
||||
}
|
||||
|
||||
if (remainingParameterInputs.Any())
|
||||
if (remainingParameterTokens.Any())
|
||||
{
|
||||
throw CliFxException.UserError(
|
||||
$"""
|
||||
Unexpected parameter(s):
|
||||
{remainingParameterInputs.Select(p => p.GetFormattedIdentifier()).JoinToString(" ")}
|
||||
{remainingParameterTokens.Select(p => p.FormattedIdentifier).JoinToString(" ")}
|
||||
"""
|
||||
);
|
||||
}
|
||||
|
||||
if (remainingRequiredParameterSchemas.Any())
|
||||
if (remainingRequiredParameters.Any())
|
||||
{
|
||||
throw CliFxException.UserError(
|
||||
$"""
|
||||
Missing required parameter(s):
|
||||
{remainingRequiredParameterSchemas
|
||||
Missing equired parameter(s):
|
||||
{remainingRequiredParameters
|
||||
.Select(p => p.FormattedIdentifier)
|
||||
.JoinToString(" ")}
|
||||
"""
|
||||
@@ -140,45 +141,52 @@ public class CommandSchema(
|
||||
}
|
||||
}
|
||||
|
||||
private void ActivateOptions(ICommand instance, CommandInput input)
|
||||
private void ActivateOptions(
|
||||
ICommand instance,
|
||||
CommandArguments arguments,
|
||||
IReadOnlyDictionary<string, string?> environmentVariables
|
||||
)
|
||||
{
|
||||
// Ensure there are no unrecognized options and that all required options are set
|
||||
var remainingOptionInputs = input.Options.ToList();
|
||||
var remainingRequiredOptionSchemas = Options.Where(o => o.IsRequired).ToList();
|
||||
var remainingOptionTokens = arguments.Options.ToList();
|
||||
var remainingRequiredOptions = Options.Where(o => o.IsRequired).ToList();
|
||||
|
||||
foreach (var optionSchema in Options)
|
||||
foreach (var option in Options)
|
||||
{
|
||||
var optionInputs = input
|
||||
.Options.Where(o => optionSchema.MatchesIdentifier(o.Identifier))
|
||||
var optionToken = arguments
|
||||
.Options.Where(o => option.MatchesIdentifier(o.Identifier))
|
||||
.ToArray();
|
||||
|
||||
var environmentVariableInput = input.EnvironmentVariables.FirstOrDefault(e =>
|
||||
optionSchema.MatchesEnvironmentVariable(e.Name)
|
||||
var environmentVariable = environmentVariables.FirstOrDefault(v =>
|
||||
option.MatchesEnvironmentVariable(v.Key)
|
||||
);
|
||||
|
||||
// Direct input
|
||||
if (optionInputs.Any())
|
||||
if (optionToken.Any())
|
||||
{
|
||||
var rawValues = optionInputs.SelectMany(o => o.Values).ToArray();
|
||||
var rawValues = optionToken.SelectMany(o => o.Values).ToArray();
|
||||
|
||||
optionSchema.Activate(instance, rawValues);
|
||||
option.Activate(instance, rawValues);
|
||||
|
||||
// Required options need at least one value to be set
|
||||
if (rawValues.Any())
|
||||
remainingRequiredOptionSchemas.Remove(optionSchema);
|
||||
remainingRequiredOptions.Remove(option);
|
||||
}
|
||||
// Environment variable
|
||||
else if (environmentVariableInput is not null)
|
||||
else if (!string.IsNullOrEmpty(environmentVariable.Value))
|
||||
{
|
||||
var rawValues = !optionSchema.IsSequence
|
||||
? [environmentVariableInput.Value]
|
||||
: environmentVariableInput.SplitValues();
|
||||
var rawValues = !option.IsSequence
|
||||
? [environmentVariable.Value]
|
||||
: environmentVariable.Value.Split(
|
||||
Path.PathSeparator,
|
||||
StringSplitOptions.RemoveEmptyEntries
|
||||
);
|
||||
|
||||
optionSchema.Activate(instance, rawValues);
|
||||
option.Activate(instance, rawValues);
|
||||
|
||||
// Required options need at least one value to be set
|
||||
if (rawValues.Any())
|
||||
remainingRequiredOptionSchemas.Remove(optionSchema);
|
||||
remainingRequiredOptions.Remove(option);
|
||||
}
|
||||
// No input, skip
|
||||
else
|
||||
@@ -186,25 +194,25 @@ public class CommandSchema(
|
||||
continue;
|
||||
}
|
||||
|
||||
remainingOptionInputs.RemoveRange(optionInputs);
|
||||
remainingOptionTokens.RemoveRange(optionToken);
|
||||
}
|
||||
|
||||
if (remainingOptionInputs.Any())
|
||||
if (remainingOptionTokens.Any())
|
||||
{
|
||||
throw CliFxException.UserError(
|
||||
$"""
|
||||
Unrecognized option(s):
|
||||
{remainingOptionInputs.Select(o => o.FormattedIdentifier).JoinToString(", ")}
|
||||
{remainingOptionTokens.Select(o => o.FormattedIdentifier).JoinToString(", ")}
|
||||
"""
|
||||
);
|
||||
}
|
||||
|
||||
if (remainingRequiredOptionSchemas.Any())
|
||||
if (remainingRequiredOptions.Any())
|
||||
{
|
||||
throw CliFxException.UserError(
|
||||
$"""
|
||||
Missing required option(s):
|
||||
{remainingRequiredOptionSchemas
|
||||
{remainingRequiredOptions
|
||||
.Select(o => o.FormattedIdentifier)
|
||||
.JoinToString(", ")}
|
||||
"""
|
||||
@@ -212,15 +220,19 @@ public class CommandSchema(
|
||||
}
|
||||
}
|
||||
|
||||
internal void Activate(ICommand instance, CommandInput input)
|
||||
internal void Activate(
|
||||
ICommand instance,
|
||||
CommandArguments arguments,
|
||||
IReadOnlyDictionary<string, string?> environmentVariables
|
||||
)
|
||||
{
|
||||
ActivateParameters(instance, input);
|
||||
ActivateOptions(instance, input);
|
||||
ActivateParameters(instance, arguments);
|
||||
ActivateOptions(instance, arguments, environmentVariables);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[ExcludeFromCodeCoverage]
|
||||
public override string ToString() => Name ?? "<default>";
|
||||
public override string ToString() => Name ?? "{default}";
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="CommandSchema" />
|
||||
|
||||
@@ -13,8 +13,8 @@ public class PropertyBinding(
|
||||
DynamicallyAccessedMemberTypes.Interfaces | DynamicallyAccessedMemberTypes.PublicMethods
|
||||
)]
|
||||
Type type,
|
||||
Func<object, object?> getValue,
|
||||
Action<object, object?> setValue
|
||||
Func<object, object?> get,
|
||||
Action<object, object?> set
|
||||
)
|
||||
{
|
||||
/// <summary>
|
||||
@@ -28,12 +28,12 @@ public class PropertyBinding(
|
||||
/// <summary>
|
||||
/// Gets the current value of the property on the specified instance.
|
||||
/// </summary>
|
||||
public object? GetValue(object instance) => getValue(instance);
|
||||
public object? Get(object instance) => get(instance);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the current value of the property on the specified instance.
|
||||
/// </summary>
|
||||
public void SetValue(object instance, object? value) => setValue(instance, value);
|
||||
public void Set(object instance, object? value) => set(instance, value);
|
||||
|
||||
internal IReadOnlyList<object?>? TryGetValidValues()
|
||||
{
|
||||
@@ -67,9 +67,9 @@ public class PropertyBinding<
|
||||
DynamicallyAccessedMemberTypes.Interfaces | DynamicallyAccessedMemberTypes.PublicMethods
|
||||
)]
|
||||
TProperty
|
||||
>(Func<TObject, TProperty?> getValue, Action<TObject, TProperty?> setValue)
|
||||
>(Func<TObject, TProperty?> get, Action<TObject, TProperty?> set)
|
||||
: PropertyBinding(
|
||||
typeof(TProperty),
|
||||
o => getValue((TObject)o),
|
||||
(o, v) => setValue((TObject)o, (TProperty?)v)
|
||||
o => get((TObject)o),
|
||||
(o, v) => set((TObject)o, (TProperty?)v)
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user