mirror of
https://github.com/Tyrrrz/CliFx.git
synced 2025-10-25 15:19:17 +00:00
Fix formatting
This commit is contained in:
@@ -29,10 +29,11 @@ public class ApplicationConfiguration
|
||||
public ApplicationConfiguration(
|
||||
IReadOnlyList<Type> commandTypes,
|
||||
bool isDebugModeAllowed,
|
||||
bool isPreviewModeAllowed)
|
||||
bool isPreviewModeAllowed
|
||||
)
|
||||
{
|
||||
CommandTypes = commandTypes;
|
||||
IsDebugModeAllowed = isDebugModeAllowed;
|
||||
IsPreviewModeAllowed = isPreviewModeAllowed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,11 +32,12 @@ public class ApplicationMetadata
|
||||
string title,
|
||||
string executableName,
|
||||
string version,
|
||||
string? description)
|
||||
string? description
|
||||
)
|
||||
{
|
||||
Title = title;
|
||||
ExecutableName = executableName;
|
||||
Version = version;
|
||||
Description = description;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,5 @@ public sealed class CommandAttribute : Attribute
|
||||
/// <summary>
|
||||
/// Initializes an instance of <see cref="CommandAttribute" />.
|
||||
/// </summary>
|
||||
public CommandAttribute()
|
||||
{
|
||||
}
|
||||
}
|
||||
public CommandAttribute() { }
|
||||
}
|
||||
|
||||
@@ -81,23 +81,17 @@ public sealed class CommandOptionAttribute : Attribute
|
||||
/// Initializes an instance of <see cref="CommandOptionAttribute" />.
|
||||
/// </summary>
|
||||
public CommandOptionAttribute(string name, char shortName)
|
||||
: this(name, (char?)shortName)
|
||||
{
|
||||
}
|
||||
: this(name, (char?)shortName) { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes an instance of <see cref="CommandOptionAttribute" />.
|
||||
/// </summary>
|
||||
public CommandOptionAttribute(string name)
|
||||
: this(name, null)
|
||||
{
|
||||
}
|
||||
: this(name, null) { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes an instance of <see cref="CommandOptionAttribute" />.
|
||||
/// </summary>
|
||||
public CommandOptionAttribute(char shortName)
|
||||
: this(null, (char?)shortName)
|
||||
{
|
||||
}
|
||||
}
|
||||
: this(null, (char?)shortName) { }
|
||||
}
|
||||
|
||||
@@ -70,4 +70,4 @@ public sealed class CommandParameterAttribute : Attribute
|
||||
{
|
||||
Order = order;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,8 @@ public class CliApplication
|
||||
ApplicationMetadata metadata,
|
||||
ApplicationConfiguration configuration,
|
||||
IConsole console,
|
||||
ITypeActivator typeActivator)
|
||||
ITypeActivator typeActivator
|
||||
)
|
||||
{
|
||||
Metadata = metadata;
|
||||
Configuration = configuration;
|
||||
@@ -58,9 +59,11 @@ public class CliApplication
|
||||
Configuration.IsPreviewModeAllowed && commandInput.IsPreviewDirectiveSpecified;
|
||||
|
||||
private bool ShouldShowHelpText(CommandSchema commandSchema, CommandInput commandInput) =>
|
||||
commandSchema.IsHelpOptionAvailable && commandInput.IsHelpOptionSpecified ||
|
||||
commandSchema.IsHelpOptionAvailable && commandInput.IsHelpOptionSpecified
|
||||
||
|
||||
// Show help text also if the fallback default command is executed without any arguments
|
||||
commandSchema == FallbackDefaultCommand.Schema && !commandInput.HasArguments;
|
||||
commandSchema == FallbackDefaultCommand.Schema
|
||||
&& !commandInput.HasArguments;
|
||||
|
||||
private bool ShouldShowVersionText(CommandSchema commandSchema, CommandInput commandInput) =>
|
||||
commandSchema.IsVersionOptionAvailable && commandInput.IsVersionOptionSpecified;
|
||||
@@ -83,7 +86,10 @@ public class CliApplication
|
||||
await Task.Delay(100);
|
||||
}
|
||||
|
||||
private async ValueTask<int> RunAsync(ApplicationSchema applicationSchema, CommandInput commandInput)
|
||||
private async ValueTask<int> RunAsync(
|
||||
ApplicationSchema applicationSchema,
|
||||
CommandInput commandInput
|
||||
)
|
||||
{
|
||||
// Console colors may have already been overridden by the parent process,
|
||||
// so we need to reset it to make sure that everything we write looks properly.
|
||||
@@ -104,21 +110,25 @@ public class CliApplication
|
||||
|
||||
// Try to get the command schema that matches the input
|
||||
var commandSchema =
|
||||
(!string.IsNullOrWhiteSpace(commandInput.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)
|
||||
// Otherwise, try to find the default command
|
||||
: applicationSchema.TryFindDefaultCommand()) ??
|
||||
(
|
||||
!string.IsNullOrWhiteSpace(commandInput.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)
|
||||
// Otherwise, try to find the default command
|
||||
: applicationSchema.TryFindDefaultCommand()
|
||||
)
|
||||
??
|
||||
// If a valid command was not found, use the fallback default command.
|
||||
// This is only used as a stub to show the help text.
|
||||
FallbackDefaultCommand.Schema;
|
||||
|
||||
// Initialize an instance of the command type
|
||||
var commandInstance = commandSchema == FallbackDefaultCommand.Schema
|
||||
? new FallbackDefaultCommand() // bypass the activator
|
||||
: _typeActivator.CreateInstance<ICommand>(commandSchema.Type);
|
||||
var commandInstance =
|
||||
commandSchema == FallbackDefaultCommand.Schema
|
||||
? new FallbackDefaultCommand() // bypass the activator
|
||||
: _typeActivator.CreateInstance<ICommand>(commandSchema.Type);
|
||||
|
||||
// Assemble the help context
|
||||
var helpContext = new HelpContext(
|
||||
@@ -178,7 +188,8 @@ public class CliApplication
|
||||
/// </remarks>
|
||||
public async ValueTask<int> RunAsync(
|
||||
IReadOnlyList<string> commandLineArguments,
|
||||
IReadOnlyDictionary<string, string> environmentVariables)
|
||||
IReadOnlyDictionary<string, string> environmentVariables
|
||||
)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -213,16 +224,17 @@ public class CliApplication
|
||||
/// When running WITHOUT the debugger attached (i.e. in production), this method swallows
|
||||
/// all exceptions and reports them to the console.
|
||||
/// </remarks>
|
||||
public async ValueTask<int> RunAsync(IReadOnlyList<string> commandLineArguments) => await RunAsync(
|
||||
commandLineArguments,
|
||||
Environment
|
||||
.GetEnvironmentVariables()
|
||||
.ToDictionary<string, string>(
|
||||
RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
|
||||
? StringComparer.OrdinalIgnoreCase
|
||||
: StringComparer.Ordinal
|
||||
)
|
||||
);
|
||||
public async ValueTask<int> RunAsync(IReadOnlyList<string> commandLineArguments) =>
|
||||
await RunAsync(
|
||||
commandLineArguments,
|
||||
Environment
|
||||
.GetEnvironmentVariables()
|
||||
.ToDictionary<string, string>(
|
||||
RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
|
||||
? StringComparer.OrdinalIgnoreCase
|
||||
: StringComparer.Ordinal
|
||||
)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Runs the application.
|
||||
@@ -233,9 +245,11 @@ public class CliApplication
|
||||
/// When running WITHOUT the debugger attached (i.e. in production), this method swallows
|
||||
/// all exceptions and reports them to the console.
|
||||
/// </remarks>
|
||||
public async ValueTask<int> RunAsync() => await RunAsync(
|
||||
Environment.GetCommandLineArgs()
|
||||
.Skip(1) // first element is the file path
|
||||
.ToArray()
|
||||
);
|
||||
}
|
||||
public async ValueTask<int> RunAsync() =>
|
||||
await RunAsync(
|
||||
Environment
|
||||
.GetCommandLineArgs()
|
||||
.Skip(1) // first element is the file path
|
||||
.ToArray()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -39,8 +39,8 @@ public partial class CliApplicationBuilder
|
||||
/// <summary>
|
||||
/// Adds a command to the application.
|
||||
/// </summary>
|
||||
public CliApplicationBuilder AddCommand<TCommand>() where TCommand : ICommand =>
|
||||
AddCommand(typeof(TCommand));
|
||||
public CliApplicationBuilder AddCommand<TCommand>()
|
||||
where TCommand : ICommand => AddCommand(typeof(TCommand));
|
||||
|
||||
/// <summary>
|
||||
/// Adds multiple commands to the application.
|
||||
@@ -62,7 +62,9 @@ public partial class CliApplicationBuilder
|
||||
/// </remarks>
|
||||
public CliApplicationBuilder AddCommandsFrom(Assembly commandAssembly)
|
||||
{
|
||||
foreach (var commandType in commandAssembly.ExportedTypes.Where(CommandSchema.IsCommandType))
|
||||
foreach (
|
||||
var commandType in commandAssembly.ExportedTypes.Where(CommandSchema.IsCommandType)
|
||||
)
|
||||
AddCommand(commandType);
|
||||
|
||||
return this;
|
||||
@@ -90,7 +92,8 @@ public partial class CliApplicationBuilder
|
||||
/// This method looks for public non-abstract classes that implement <see cref="ICommand" />
|
||||
/// and are annotated by <see cref="CommandAttribute" />.
|
||||
/// </remarks>
|
||||
public CliApplicationBuilder AddCommandsFromThisAssembly() => AddCommandsFrom(Assembly.GetCallingAssembly());
|
||||
public CliApplicationBuilder AddCommandsFromThisAssembly() =>
|
||||
AddCommandsFrom(Assembly.GetCallingAssembly());
|
||||
|
||||
/// <summary>
|
||||
/// Specifies whether debug mode (enabled with the [debug] directive) is allowed in the application.
|
||||
@@ -190,8 +193,9 @@ public partial class CliApplicationBuilder
|
||||
/// This method takes a delegate that receives the list of all added command types, so that you can
|
||||
/// easily register them with the service provider.
|
||||
/// </summary>
|
||||
public CliApplicationBuilder UseTypeActivator(Func<IReadOnlyList<Type>, IServiceProvider> getServiceProvider) =>
|
||||
UseTypeActivator(getServiceProvider(_commandTypes.ToArray()));
|
||||
public CliApplicationBuilder UseTypeActivator(
|
||||
Func<IReadOnlyList<Type>, IServiceProvider> getServiceProvider
|
||||
) => UseTypeActivator(getServiceProvider(_commandTypes.ToArray()));
|
||||
|
||||
/// <summary>
|
||||
/// Creates a configured instance of <see cref="CliApplication" />.
|
||||
@@ -228,8 +232,8 @@ public partial class CliApplicationBuilder
|
||||
if (string.IsNullOrWhiteSpace(entryAssemblyName))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Failed to infer the default application title. " +
|
||||
$"Please specify it explicitly using `{nameof(SetTitle)}()`."
|
||||
"Failed to infer the default application title. "
|
||||
+ $"Please specify it explicitly using `{nameof(SetTitle)}()`."
|
||||
);
|
||||
}
|
||||
|
||||
@@ -241,11 +245,14 @@ public partial class CliApplicationBuilder
|
||||
var entryAssemblyFilePath = EnvironmentEx.EntryAssembly?.Location;
|
||||
var processFilePath = EnvironmentEx.ProcessPath;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(entryAssemblyFilePath) || string.IsNullOrWhiteSpace(processFilePath))
|
||||
if (
|
||||
string.IsNullOrWhiteSpace(entryAssemblyFilePath)
|
||||
|| string.IsNullOrWhiteSpace(processFilePath)
|
||||
)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Failed to infer the default application executable name. " +
|
||||
$"Please specify it explicitly using `{nameof(SetExecutableName)}()`."
|
||||
"Failed to infer the default application executable name. "
|
||||
+ $"Please specify it explicitly using `{nameof(SetExecutableName)}()`."
|
||||
);
|
||||
}
|
||||
|
||||
@@ -258,8 +265,13 @@ public partial class CliApplicationBuilder
|
||||
|
||||
// If the process path has the same name and parent directory as the entry assembly path,
|
||||
// but different extension, it's a framework-dependent .NET Core app launched through the apphost.
|
||||
if (PathEx.AreEqual(Path.ChangeExtension(entryAssemblyFilePath, "exe"), processFilePath) ||
|
||||
PathEx.AreEqual(Path.GetFileNameWithoutExtension(entryAssemblyFilePath), processFilePath))
|
||||
if (
|
||||
PathEx.AreEqual(Path.ChangeExtension(entryAssemblyFilePath, "exe"), processFilePath)
|
||||
|| PathEx.AreEqual(
|
||||
Path.GetFileNameWithoutExtension(entryAssemblyFilePath),
|
||||
processFilePath
|
||||
)
|
||||
)
|
||||
{
|
||||
return Path.GetFileNameWithoutExtension(entryAssemblyFilePath);
|
||||
}
|
||||
@@ -274,11 +286,11 @@ public partial class CliApplicationBuilder
|
||||
if (entryAssemblyVersion is null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Failed to infer the default application version. " +
|
||||
$"Please specify it explicitly using `{nameof(SetVersion)}()`."
|
||||
"Failed to infer the default application version. "
|
||||
+ $"Please specify it explicitly using `{nameof(SetVersion)}()`."
|
||||
);
|
||||
}
|
||||
|
||||
return "v" + entryAssemblyVersion.ToSemanticString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,9 @@ internal class CommandBinder
|
||||
// Custom converter
|
||||
if (memberSchema.ConverterType is not null)
|
||||
{
|
||||
var converter = _typeActivator.CreateInstance<IBindingConverter>(memberSchema.ConverterType);
|
||||
var converter = _typeActivator.CreateInstance<IBindingConverter>(
|
||||
memberSchema.ConverterType
|
||||
);
|
||||
return converter.Convert(rawValue);
|
||||
}
|
||||
|
||||
@@ -88,7 +90,10 @@ internal class CommandBinder
|
||||
var parseMethodWithFormatProvider = targetType.TryGetStaticParseMethod(true);
|
||||
if (parseMethodWithFormatProvider is not null)
|
||||
{
|
||||
return parseMethodWithFormatProvider.Invoke(null, new object?[] { rawValue, _formatProvider });
|
||||
return parseMethodWithFormatProvider.Invoke(
|
||||
null,
|
||||
new object?[] { rawValue, _formatProvider }
|
||||
);
|
||||
}
|
||||
|
||||
// String-parseable (without IFormatProvider)
|
||||
@@ -111,7 +116,8 @@ internal class CommandBinder
|
||||
IMemberSchema memberSchema,
|
||||
IReadOnlyList<string> rawValues,
|
||||
Type targetEnumerableType,
|
||||
Type targetElementType)
|
||||
Type targetElementType
|
||||
)
|
||||
{
|
||||
var array = rawValues
|
||||
.Select(v => ConvertSingle(memberSchema, v, targetElementType))
|
||||
@@ -146,8 +152,11 @@ internal class CommandBinder
|
||||
try
|
||||
{
|
||||
// Non-scalar
|
||||
var enumerableUnderlyingType = memberSchema.Property.Type.TryGetEnumerableUnderlyingType();
|
||||
if (enumerableUnderlyingType is not null && memberSchema.Property.Type != typeof(string))
|
||||
var enumerableUnderlyingType =
|
||||
memberSchema.Property.Type.TryGetEnumerableUnderlyingType();
|
||||
if (
|
||||
enumerableUnderlyingType is not null && memberSchema.Property.Type != typeof(string)
|
||||
)
|
||||
{
|
||||
return ConvertMultiple(
|
||||
memberSchema,
|
||||
@@ -219,7 +228,11 @@ internal class CommandBinder
|
||||
}
|
||||
}
|
||||
|
||||
private void BindMember(IMemberSchema memberSchema, ICommand commandInstance, IReadOnlyList<string> rawValues)
|
||||
private void BindMember(
|
||||
IMemberSchema memberSchema,
|
||||
ICommand commandInstance,
|
||||
IReadOnlyList<string> rawValues
|
||||
)
|
||||
{
|
||||
var convertedValue = ConvertMember(memberSchema, rawValues);
|
||||
ValidateMember(memberSchema, convertedValue);
|
||||
@@ -227,11 +240,17 @@ internal class CommandBinder
|
||||
memberSchema.Property.SetValue(commandInstance, convertedValue);
|
||||
}
|
||||
|
||||
private void BindParameters(CommandInput commandInput, CommandSchema commandSchema, ICommand commandInstance)
|
||||
private void BindParameters(
|
||||
CommandInput commandInput,
|
||||
CommandSchema commandSchema,
|
||||
ICommand commandInstance
|
||||
)
|
||||
{
|
||||
// Ensure there are no unexpected parameters and that all parameters are provided
|
||||
var remainingParameterInputs = commandInput.Parameters.ToList();
|
||||
var remainingRequiredParameterSchemas = commandSchema.Parameters.Where(p => p.IsRequired).ToList();
|
||||
var remainingRequiredParameterSchemas = commandSchema.Parameters
|
||||
.Where(p => p.IsRequired)
|
||||
.ToList();
|
||||
|
||||
var position = 0;
|
||||
|
||||
@@ -290,22 +309,27 @@ internal class CommandBinder
|
||||
}
|
||||
}
|
||||
|
||||
private void BindOptions(CommandInput commandInput, CommandSchema commandSchema, ICommand commandInstance)
|
||||
private void BindOptions(
|
||||
CommandInput commandInput,
|
||||
CommandSchema commandSchema,
|
||||
ICommand commandInstance
|
||||
)
|
||||
{
|
||||
// Ensure there are no unrecognized options and that all required options are set
|
||||
var remainingOptionInputs = commandInput.Options.ToList();
|
||||
var remainingRequiredOptionSchemas = commandSchema.Options.Where(o => o.IsRequired).ToList();
|
||||
var remainingRequiredOptionSchemas = commandSchema.Options
|
||||
.Where(o => o.IsRequired)
|
||||
.ToList();
|
||||
|
||||
foreach (var optionSchema in commandSchema.Options)
|
||||
{
|
||||
var optionInputs = commandInput
|
||||
.Options
|
||||
var optionInputs = commandInput.Options
|
||||
.Where(o => optionSchema.MatchesIdentifier(o.Identifier))
|
||||
.ToArray();
|
||||
|
||||
var environmentVariableInput = commandInput
|
||||
.EnvironmentVariables
|
||||
.FirstOrDefault(e => optionSchema.MatchesEnvironmentVariable(e.Name));
|
||||
var environmentVariableInput = commandInput.EnvironmentVariables.FirstOrDefault(
|
||||
e => optionSchema.MatchesEnvironmentVariable(e.Name)
|
||||
);
|
||||
|
||||
// Direct input
|
||||
if (optionInputs.Any())
|
||||
@@ -361,9 +385,13 @@ internal class CommandBinder
|
||||
}
|
||||
}
|
||||
|
||||
public void Bind(CommandInput commandInput, CommandSchema commandSchema, ICommand commandInstance)
|
||||
public void Bind(
|
||||
CommandInput commandInput,
|
||||
CommandSchema commandSchema,
|
||||
ICommand commandInstance
|
||||
)
|
||||
{
|
||||
BindParameters(commandInput, commandSchema, commandInstance);
|
||||
BindOptions(commandInput, commandSchema, commandInstance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,8 @@ public partial class CliFxException : Exception
|
||||
string message,
|
||||
int exitCode = DefaultExitCode,
|
||||
bool showHelp = false,
|
||||
Exception? innerException = null)
|
||||
Exception? innerException = null
|
||||
)
|
||||
: base(message, innerException)
|
||||
{
|
||||
HasCustomMessage = !string.IsNullOrWhiteSpace(message);
|
||||
@@ -45,11 +46,13 @@ public partial class CliFxException
|
||||
{
|
||||
// Internal errors don't show help because they're meant for the developer and
|
||||
// not the end-user of the application.
|
||||
internal static CliFxException InternalError(string message, Exception? innerException = null) =>
|
||||
new(message, DefaultExitCode, false, innerException);
|
||||
internal static CliFxException InternalError(
|
||||
string message,
|
||||
Exception? innerException = null
|
||||
) => new(message, DefaultExitCode, false, innerException);
|
||||
|
||||
// User errors are typically caused by invalid input and they're meant for the end-user,
|
||||
// so we want to show help.
|
||||
internal static CliFxException UserError(string message, Exception? innerException = null) =>
|
||||
new(message, DefaultExitCode, true, innerException);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,7 @@ public class CommandException : CliFxException
|
||||
string message,
|
||||
int exitCode = DefaultExitCode,
|
||||
bool showHelp = false,
|
||||
Exception? innerException = null)
|
||||
: base(message, exitCode, showHelp, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
Exception? innerException = null
|
||||
)
|
||||
: base(message, exitCode, showHelp, innerException) { }
|
||||
}
|
||||
|
||||
@@ -17,4 +17,4 @@ public abstract class BindingConverter<T> : IBindingConverter
|
||||
public abstract T Convert(string? rawValue);
|
||||
|
||||
object? IBindingConverter.Convert(string? rawValue) => Convert(rawValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,4 +14,4 @@ public class BindingValidationError
|
||||
/// Initializes an instance of <see cref="BindingValidationError" />.
|
||||
/// </summary>
|
||||
public BindingValidationError(string message) => Message = message;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,4 +32,4 @@ public abstract class BindingValidator<T> : IBindingValidator
|
||||
public abstract BindingValidationError? Validate(T? value);
|
||||
|
||||
BindingValidationError? IBindingValidator.Validate(object? value) => Validate((T?)value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,4 +17,4 @@ internal class FallbackDefaultCommand : ICommand
|
||||
// Never actually executed
|
||||
[ExcludeFromCodeCoverage]
|
||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,9 +7,7 @@ namespace CliFx.Formatting;
|
||||
internal class CommandInputConsoleFormatter : ConsoleFormatter
|
||||
{
|
||||
public CommandInputConsoleFormatter(ConsoleWriter consoleWriter)
|
||||
: base(consoleWriter)
|
||||
{
|
||||
}
|
||||
: base(consoleWriter) { }
|
||||
|
||||
private void WriteCommandLineArguments(CommandInput commandInput)
|
||||
{
|
||||
@@ -92,6 +90,8 @@ internal class CommandInputConsoleFormatter : ConsoleFormatter
|
||||
|
||||
internal static class CommandInputConsoleFormatterExtensions
|
||||
{
|
||||
public static void WriteCommandInput(this ConsoleWriter consoleWriter, CommandInput commandInput) =>
|
||||
new CommandInputConsoleFormatter(consoleWriter).WriteCommandInput(commandInput);
|
||||
}
|
||||
public static void WriteCommandInput(
|
||||
this ConsoleWriter consoleWriter,
|
||||
CommandInput commandInput
|
||||
) => new CommandInputConsoleFormatter(consoleWriter).WriteCommandInput(commandInput);
|
||||
}
|
||||
|
||||
@@ -12,8 +12,7 @@ internal class ConsoleFormatter
|
||||
|
||||
public bool IsEmpty => _column == 0 && _row == 0;
|
||||
|
||||
public ConsoleFormatter(ConsoleWriter consoleWriter) =>
|
||||
_consoleWriter = consoleWriter;
|
||||
public ConsoleFormatter(ConsoleWriter consoleWriter) => _consoleWriter = consoleWriter;
|
||||
|
||||
public void Write(string? value)
|
||||
{
|
||||
@@ -71,4 +70,4 @@ internal class ConsoleFormatter
|
||||
else
|
||||
WriteHorizontalMargin(offsetSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,9 +10,7 @@ namespace CliFx.Formatting;
|
||||
internal class ExceptionConsoleFormatter : ConsoleFormatter
|
||||
{
|
||||
public ExceptionConsoleFormatter(ConsoleWriter consoleWriter)
|
||||
: base(consoleWriter)
|
||||
{
|
||||
}
|
||||
: base(consoleWriter) { }
|
||||
|
||||
private void WriteStackFrame(StackFrame stackFrame, int indentLevel)
|
||||
{
|
||||
@@ -126,4 +124,4 @@ internal static class ExceptionConsoleFormatterExtensions
|
||||
{
|
||||
public static void WriteException(this ConsoleWriter consoleWriter, Exception exception) =>
|
||||
new ExceptionConsoleFormatter(consoleWriter).WriteException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,9 +74,9 @@ internal class HelpConsoleFormatter : ConsoleFormatter
|
||||
// Parameters
|
||||
foreach (var parameter in _context.CommandSchema.Parameters.OrderBy(p => p.Order))
|
||||
{
|
||||
Write(ConsoleColor.DarkCyan, parameter.Property.IsScalar()
|
||||
? $"<{parameter.Name}>"
|
||||
: $"<{parameter.Name}...>"
|
||||
Write(
|
||||
ConsoleColor.DarkCyan,
|
||||
parameter.Property.IsScalar() ? $"<{parameter.Name}>" : $"<{parameter.Name}...>"
|
||||
);
|
||||
Write(' ');
|
||||
}
|
||||
@@ -84,16 +84,15 @@ internal class HelpConsoleFormatter : ConsoleFormatter
|
||||
// Required options
|
||||
foreach (var option in _context.CommandSchema.Options.Where(o => o.IsRequired))
|
||||
{
|
||||
Write(ConsoleColor.Yellow, !string.IsNullOrWhiteSpace(option.Name)
|
||||
? $"--{option.Name}"
|
||||
: $"-{option.ShortName}"
|
||||
Write(
|
||||
ConsoleColor.Yellow,
|
||||
!string.IsNullOrWhiteSpace(option.Name)
|
||||
? $"--{option.Name}"
|
||||
: $"-{option.ShortName}"
|
||||
);
|
||||
Write(' ');
|
||||
|
||||
Write(ConsoleColor.White, option.Property.IsScalar()
|
||||
? "<value>"
|
||||
: "<values...>"
|
||||
);
|
||||
Write(ConsoleColor.White, option.Property.IsScalar() ? "<value>" : "<values...>");
|
||||
Write(' ');
|
||||
}
|
||||
|
||||
@@ -107,9 +106,9 @@ internal class HelpConsoleFormatter : ConsoleFormatter
|
||||
}
|
||||
|
||||
// Child command usage
|
||||
var childCommandSchemas = _context
|
||||
.ApplicationSchema
|
||||
.GetChildCommands(_context.CommandSchema.Name);
|
||||
var childCommandSchemas = _context.ApplicationSchema.GetChildCommands(
|
||||
_context.CommandSchema.Name
|
||||
);
|
||||
|
||||
if (childCommandSchemas.Any())
|
||||
{
|
||||
@@ -224,7 +223,9 @@ internal class HelpConsoleFormatter : ConsoleFormatter
|
||||
|
||||
WriteHeader("Options");
|
||||
|
||||
foreach (var optionSchema in _context.CommandSchema.Options.OrderByDescending(o => o.IsRequired))
|
||||
foreach (
|
||||
var optionSchema in _context.CommandSchema.Options.OrderByDescending(o => o.IsRequired)
|
||||
)
|
||||
{
|
||||
if (optionSchema.IsRequired)
|
||||
{
|
||||
@@ -320,8 +321,7 @@ internal class HelpConsoleFormatter : ConsoleFormatter
|
||||
if (defaultValue is not string && defaultValue is IEnumerable defaultValues)
|
||||
{
|
||||
var elementType =
|
||||
defaultValues.GetType().TryGetEnumerableUnderlyingType() ??
|
||||
typeof(object);
|
||||
defaultValues.GetType().TryGetEnumerableUnderlyingType() ?? typeof(object);
|
||||
|
||||
if (elementType.IsToStringOverriden())
|
||||
{
|
||||
@@ -365,8 +365,7 @@ internal class HelpConsoleFormatter : ConsoleFormatter
|
||||
|
||||
private void WriteCommandChildren()
|
||||
{
|
||||
var childCommandSchemas = _context
|
||||
.ApplicationSchema
|
||||
var childCommandSchemas = _context.ApplicationSchema
|
||||
.GetChildCommands(_context.CommandSchema.Name)
|
||||
.OrderBy(a => a.Name, StringComparer.Ordinal)
|
||||
.ToArray();
|
||||
@@ -386,10 +385,7 @@ internal class HelpConsoleFormatter : ConsoleFormatter
|
||||
Write(
|
||||
ConsoleColor.Cyan,
|
||||
// Relative to current command
|
||||
childCommandSchema
|
||||
.Name?
|
||||
.Substring(_context.CommandSchema.Name?.Length ?? 0)
|
||||
.Trim()
|
||||
childCommandSchema.Name?.Substring(_context.CommandSchema.Name?.Length ?? 0).Trim()
|
||||
);
|
||||
|
||||
WriteColumnMargin();
|
||||
@@ -402,8 +398,7 @@ internal class HelpConsoleFormatter : ConsoleFormatter
|
||||
}
|
||||
|
||||
// Child commands of child command
|
||||
var grandChildCommandSchemas = _context
|
||||
.ApplicationSchema
|
||||
var grandChildCommandSchemas = _context.ApplicationSchema
|
||||
.GetChildCommands(childCommandSchema.Name)
|
||||
.OrderBy(c => c.Name, StringComparer.Ordinal)
|
||||
.ToArray();
|
||||
@@ -427,9 +422,8 @@ internal class HelpConsoleFormatter : ConsoleFormatter
|
||||
Write(
|
||||
ConsoleColor.Cyan,
|
||||
// Relative to current command (not the parent)
|
||||
grandChildCommandSchema
|
||||
.Name?
|
||||
.Substring(_context.CommandSchema.Name?.Length ?? 0)
|
||||
grandChildCommandSchema.Name
|
||||
?.Substring(_context.CommandSchema.Name?.Length ?? 0)
|
||||
.Trim()
|
||||
);
|
||||
}
|
||||
@@ -468,4 +462,4 @@ internal static class HelpConsoleFormatterExtensions
|
||||
{
|
||||
public static void WriteHelpText(this ConsoleWriter consoleWriter, HelpContext context) =>
|
||||
new HelpConsoleFormatter(consoleWriter, context).WriteHelpText();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,11 +17,12 @@ internal class HelpContext
|
||||
ApplicationMetadata applicationMetadata,
|
||||
ApplicationSchema applicationSchema,
|
||||
CommandSchema commandSchema,
|
||||
IReadOnlyDictionary<IMemberSchema, object?> commandDefaultValues)
|
||||
IReadOnlyDictionary<IMemberSchema, object?> commandDefaultValues
|
||||
)
|
||||
{
|
||||
ApplicationMetadata = applicationMetadata;
|
||||
ApplicationSchema = applicationSchema;
|
||||
CommandSchema = commandSchema;
|
||||
CommandDefaultValues = commandDefaultValues;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,4 +16,4 @@ public interface ICommand
|
||||
/// <c>return default;</c>
|
||||
/// </remarks>
|
||||
ValueTask ExecuteAsync(IConsole console);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,9 +31,7 @@ public partial class ConsoleReader : StreamReader
|
||||
/// Initializes an instance of <see cref="ConsoleReader" />.
|
||||
/// </summary>
|
||||
public ConsoleReader(IConsole console, Stream stream)
|
||||
: this(console, stream, System.Console.InputEncoding)
|
||||
{
|
||||
}
|
||||
: this(console, stream, System.Console.InputEncoding) { }
|
||||
|
||||
// The following overrides are required to establish thread-safe behavior
|
||||
// in methods deriving from StreamReader.
|
||||
@@ -99,8 +97,6 @@ public partial class ConsoleReader : StreamReader
|
||||
|
||||
public partial class ConsoleReader
|
||||
{
|
||||
internal static ConsoleReader Create(IConsole console, Stream stream) => new(
|
||||
console,
|
||||
Stream.Synchronized(stream)
|
||||
);
|
||||
}
|
||||
internal static ConsoleReader Create(IConsole console, Stream stream) =>
|
||||
new(console, Stream.Synchronized(stream));
|
||||
}
|
||||
|
||||
@@ -32,16 +32,15 @@ public partial class ConsoleWriter : StreamWriter
|
||||
/// Initializes an instance of <see cref="ConsoleWriter" />.
|
||||
/// </summary>
|
||||
public ConsoleWriter(IConsole console, Stream stream)
|
||||
: this(console, stream, System.Console.OutputEncoding)
|
||||
{
|
||||
}
|
||||
: this(console, stream, System.Console.OutputEncoding) { }
|
||||
|
||||
// The following overrides are required to establish thread-safe behavior
|
||||
// in methods deriving from StreamWriter.
|
||||
|
||||
/// <inheritdoc />
|
||||
[ExcludeFromCodeCoverage, MethodImpl(MethodImplOptions.Synchronized)]
|
||||
public override void Write(char[] buffer, int index, int count) => base.Write(buffer, index, count);
|
||||
public override void Write(char[] buffer, int index, int count) =>
|
||||
base.Write(buffer, index, count);
|
||||
|
||||
/// <inheritdoc />
|
||||
[ExcludeFromCodeCoverage, MethodImpl(MethodImplOptions.Synchronized)]
|
||||
@@ -272,8 +271,6 @@ public partial class ConsoleWriter : StreamWriter
|
||||
|
||||
public partial class ConsoleWriter
|
||||
{
|
||||
internal static ConsoleWriter Create(IConsole console, Stream stream) => new(
|
||||
console,
|
||||
Stream.Synchronized(stream)
|
||||
) {AutoFlush = true};
|
||||
}
|
||||
internal static ConsoleWriter Create(IConsole console, Stream stream) =>
|
||||
new(console, Stream.Synchronized(stream)) { AutoFlush = true };
|
||||
}
|
||||
|
||||
@@ -29,4 +29,4 @@ public class DefaultTypeActivator : ITypeActivator
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,4 +34,4 @@ public class DelegateTypeActivator : ITypeActivator
|
||||
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,8 +66,8 @@ public class FakeConsole : IConsole, IDisposable
|
||||
_keys.TryDequeue(out var key)
|
||||
? key
|
||||
: throw new InvalidOperationException(
|
||||
"Cannot read key because there are no key presses enqueued. " +
|
||||
$"Use the `{nameof(EnqueueKey)}(...)` method to simulate a key press."
|
||||
"Cannot read key because there are no key presses enqueued. "
|
||||
+ $"Use the `{nameof(EnqueueKey)}(...)` method to simulate a key press."
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
@@ -83,9 +83,7 @@ public class FakeConsole : IConsole, IDisposable
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Clear()
|
||||
{
|
||||
}
|
||||
public void Clear() { }
|
||||
|
||||
/// <inheritdoc />
|
||||
public CancellationToken RegisterCancellationHandler() => _cancellationTokenSource.Token;
|
||||
@@ -112,4 +110,4 @@ public class FakeConsole : IConsole, IDisposable
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void Dispose() => _cancellationTokenSource.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,9 +25,7 @@ public class FakeInMemoryConsole : FakeConsole
|
||||
/// Initializes an instance of <see cref="FakeInMemoryConsole" />.
|
||||
/// </summary>
|
||||
public FakeInMemoryConsole()
|
||||
: this(new MemoryStream(), new MemoryStream(), new MemoryStream())
|
||||
{
|
||||
}
|
||||
: this(new MemoryStream(), new MemoryStream(), new MemoryStream()) { }
|
||||
|
||||
/// <summary>
|
||||
/// Writes data to the input stream.
|
||||
@@ -51,9 +49,7 @@ public class FakeInMemoryConsole : FakeConsole
|
||||
/// <summary>
|
||||
/// Writes data to the input stream.
|
||||
/// </summary>
|
||||
public void WriteInput(string data) => WriteInput(
|
||||
Input.CurrentEncoding.GetBytes(data)
|
||||
);
|
||||
public void WriteInput(string data) => WriteInput(Input.CurrentEncoding.GetBytes(data));
|
||||
|
||||
/// <summary>
|
||||
/// Reads the data written to the output stream.
|
||||
@@ -98,4 +94,4 @@ public class FakeInMemoryConsole : FakeConsole
|
||||
|
||||
base.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,7 +112,10 @@ public static class ConsoleExtensions
|
||||
/// Sets the specified foreground color and returns an <see cref="IDisposable" />
|
||||
/// that will reset the color back to its previous value upon disposal.
|
||||
/// </summary>
|
||||
public static IDisposable WithForegroundColor(this IConsole console, ConsoleColor foregroundColor)
|
||||
public static IDisposable WithForegroundColor(
|
||||
this IConsole console,
|
||||
ConsoleColor foregroundColor
|
||||
)
|
||||
{
|
||||
var lastColor = console.ForegroundColor;
|
||||
console.ForegroundColor = foregroundColor;
|
||||
@@ -124,7 +127,10 @@ public static class ConsoleExtensions
|
||||
/// Sets the specified background color and returns an <see cref="IDisposable" />
|
||||
/// that will reset the color back to its previous value upon disposal.
|
||||
/// </summary>
|
||||
public static IDisposable WithBackgroundColor(this IConsole console, ConsoleColor backgroundColor)
|
||||
public static IDisposable WithBackgroundColor(
|
||||
this IConsole console,
|
||||
ConsoleColor backgroundColor
|
||||
)
|
||||
{
|
||||
var lastColor = console.BackgroundColor;
|
||||
console.BackgroundColor = backgroundColor;
|
||||
@@ -139,9 +145,10 @@ public static class ConsoleExtensions
|
||||
public static IDisposable WithColors(
|
||||
this IConsole console,
|
||||
ConsoleColor foregroundColor,
|
||||
ConsoleColor backgroundColor) =>
|
||||
ConsoleColor backgroundColor
|
||||
) =>
|
||||
Disposable.Merge(
|
||||
console.WithForegroundColor(foregroundColor),
|
||||
console.WithBackgroundColor(backgroundColor)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,4 +27,4 @@ internal static class TypeActivatorExtensions
|
||||
|
||||
return (T)activator.CreateInstance(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,4 +119,4 @@ public class SystemConsole : IConsole, IDisposable
|
||||
Output.Dispose();
|
||||
Error.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,10 +18,10 @@ internal partial class CommandInput
|
||||
public IReadOnlyList<EnvironmentVariableInput> EnvironmentVariables { get; }
|
||||
|
||||
public bool HasArguments =>
|
||||
!string.IsNullOrWhiteSpace(CommandName) ||
|
||||
Directives.Any() ||
|
||||
Parameters.Any() ||
|
||||
Options.Any();
|
||||
!string.IsNullOrWhiteSpace(CommandName)
|
||||
|| Directives.Any()
|
||||
|| Parameters.Any()
|
||||
|| Options.Any();
|
||||
|
||||
public bool IsDebugDirectiveSpecified => Directives.Any(d => d.IsDebugDirective);
|
||||
|
||||
@@ -36,7 +36,8 @@ internal partial class CommandInput
|
||||
IReadOnlyList<DirectiveInput> directives,
|
||||
IReadOnlyList<ParameterInput> parameters,
|
||||
IReadOnlyList<OptionInput> options,
|
||||
IReadOnlyList<EnvironmentVariableInput> environmentVariables)
|
||||
IReadOnlyList<EnvironmentVariableInput> environmentVariables
|
||||
)
|
||||
{
|
||||
CommandName = commandName;
|
||||
Directives = directives;
|
||||
@@ -50,7 +51,8 @@ internal partial class CommandInput
|
||||
{
|
||||
private static IReadOnlyList<DirectiveInput> ParseDirectives(
|
||||
IReadOnlyList<string> commandLineArguments,
|
||||
ref int index)
|
||||
ref int index
|
||||
)
|
||||
{
|
||||
var result = new List<DirectiveInput>();
|
||||
|
||||
@@ -73,7 +75,8 @@ internal partial class CommandInput
|
||||
private static string? ParseCommandName(
|
||||
IReadOnlyList<string> commandLineArguments,
|
||||
ISet<string> commandNames,
|
||||
ref int index)
|
||||
ref int index
|
||||
)
|
||||
{
|
||||
var potentialCommandNameComponents = new List<string>();
|
||||
var commandName = default(string?);
|
||||
@@ -107,7 +110,8 @@ internal partial class CommandInput
|
||||
|
||||
private static IReadOnlyList<ParameterInput> ParseParameters(
|
||||
IReadOnlyList<string> commandLineArguments,
|
||||
ref int index)
|
||||
ref int index
|
||||
)
|
||||
{
|
||||
var result = new List<ParameterInput>();
|
||||
|
||||
@@ -118,13 +122,14 @@ internal partial class CommandInput
|
||||
|
||||
var isOptionIdentifier =
|
||||
// Name
|
||||
argument.StartsWith("--", StringComparison.Ordinal) &&
|
||||
argument.Length > 2 &&
|
||||
char.IsLetter(argument[2]) ||
|
||||
argument.StartsWith("--", StringComparison.Ordinal)
|
||||
&& argument.Length > 2
|
||||
&& char.IsLetter(argument[2])
|
||||
||
|
||||
// Short name
|
||||
argument.StartsWith('-') &&
|
||||
argument.Length > 1 &&
|
||||
char.IsLetter(argument[1]);
|
||||
argument.StartsWith('-')
|
||||
&& argument.Length > 1
|
||||
&& char.IsLetter(argument[1]);
|
||||
|
||||
// Break on the first option identifier
|
||||
if (isOptionIdentifier)
|
||||
@@ -138,7 +143,8 @@ internal partial class CommandInput
|
||||
|
||||
private static IReadOnlyList<OptionInput> ParseOptions(
|
||||
IReadOnlyList<string> commandLineArguments,
|
||||
ref int index)
|
||||
ref int index
|
||||
)
|
||||
{
|
||||
var result = new List<OptionInput>();
|
||||
|
||||
@@ -151,9 +157,11 @@ internal partial class CommandInput
|
||||
var argument = commandLineArguments[index];
|
||||
|
||||
// Name
|
||||
if (argument.StartsWith("--", StringComparison.Ordinal) &&
|
||||
argument.Length > 2 &&
|
||||
char.IsLetter(argument[2]))
|
||||
if (
|
||||
argument.StartsWith("--", StringComparison.Ordinal)
|
||||
&& argument.Length > 2
|
||||
&& char.IsLetter(argument[2])
|
||||
)
|
||||
{
|
||||
// Flush previous
|
||||
if (!string.IsNullOrWhiteSpace(lastOptionIdentifier))
|
||||
@@ -163,9 +171,7 @@ internal partial class CommandInput
|
||||
lastOptionValues = new List<string>();
|
||||
}
|
||||
// Short name
|
||||
else if (argument.StartsWith('-') &&
|
||||
argument.Length > 1 &&
|
||||
char.IsLetter(argument[1]))
|
||||
else if (argument.StartsWith('-') && argument.Length > 1 && char.IsLetter(argument[1]))
|
||||
{
|
||||
foreach (var identifier in argument[1..])
|
||||
{
|
||||
@@ -194,14 +200,12 @@ internal partial class CommandInput
|
||||
public static CommandInput Parse(
|
||||
IReadOnlyList<string> commandLineArguments,
|
||||
IReadOnlyDictionary<string, string> environmentVariables,
|
||||
IReadOnlyList<string> availableCommandNames)
|
||||
IReadOnlyList<string> availableCommandNames
|
||||
)
|
||||
{
|
||||
var index = 0;
|
||||
|
||||
var parsedDirectives = ParseDirectives(
|
||||
commandLineArguments,
|
||||
ref index
|
||||
);
|
||||
var parsedDirectives = ParseDirectives(commandLineArguments, ref index);
|
||||
|
||||
var parsedCommandName = ParseCommandName(
|
||||
commandLineArguments,
|
||||
@@ -209,15 +213,9 @@ internal partial class CommandInput
|
||||
ref index
|
||||
);
|
||||
|
||||
var parsedParameters = ParseParameters(
|
||||
commandLineArguments,
|
||||
ref index
|
||||
);
|
||||
var parsedParameters = ParseParameters(commandLineArguments, ref index);
|
||||
|
||||
var parsedOptions = ParseOptions(
|
||||
commandLineArguments,
|
||||
ref index
|
||||
);
|
||||
var parsedOptions = ParseOptions(commandLineArguments, ref index);
|
||||
|
||||
var parsedEnvironmentVariables = environmentVariables
|
||||
.Select(kvp => new EnvironmentVariableInput(kvp.Key, kvp.Value))
|
||||
@@ -231,4 +229,4 @@ internal partial class CommandInput
|
||||
parsedEnvironmentVariables
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,4 +13,4 @@ internal class DirectiveInput
|
||||
string.Equals(Name, "preview", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
public DirectiveInput(string name) => Name = name;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,4 +16,4 @@ internal class EnvironmentVariableInput
|
||||
}
|
||||
|
||||
public IReadOnlyList<string> SplitValues() => Value.Split(Path.PathSeparator);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,11 +9,9 @@ internal class OptionInput
|
||||
|
||||
public IReadOnlyList<string> Values { get; }
|
||||
|
||||
public bool IsHelpOption =>
|
||||
OptionSchema.HelpOption.MatchesIdentifier(Identifier);
|
||||
public bool IsHelpOption => OptionSchema.HelpOption.MatchesIdentifier(Identifier);
|
||||
|
||||
public bool IsVersionOption =>
|
||||
OptionSchema.VersionOption.MatchesIdentifier(Identifier);
|
||||
public bool IsVersionOption => OptionSchema.VersionOption.MatchesIdentifier(Identifier);
|
||||
|
||||
public OptionInput(string identifier, IReadOnlyList<string> values)
|
||||
{
|
||||
@@ -21,9 +19,10 @@ internal class OptionInput
|
||||
Values = values;
|
||||
}
|
||||
|
||||
public string GetFormattedIdentifier() => Identifier switch
|
||||
{
|
||||
{Length: >= 2} => "--" + Identifier,
|
||||
_ => '-' + Identifier
|
||||
};
|
||||
}
|
||||
public string GetFormattedIdentifier() =>
|
||||
Identifier switch
|
||||
{
|
||||
{ Length: >= 2 } => "--" + Identifier,
|
||||
_ => '-' + Identifier
|
||||
};
|
||||
}
|
||||
|
||||
@@ -7,4 +7,4 @@ internal class ParameterInput
|
||||
public ParameterInput(string value) => Value = value;
|
||||
|
||||
public string GetFormattedIdentifier() => $"<{Value}>";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,20 +14,18 @@ internal partial class ApplicationSchema
|
||||
Commands = commands;
|
||||
}
|
||||
|
||||
public IReadOnlyList<string> GetCommandNames() => Commands
|
||||
.Select(c => c.Name)
|
||||
.WhereNotNullOrWhiteSpace()
|
||||
.ToArray();
|
||||
public IReadOnlyList<string> GetCommandNames() =>
|
||||
Commands.Select(c => c.Name).WhereNotNullOrWhiteSpace().ToArray();
|
||||
|
||||
public CommandSchema? TryFindDefaultCommand() =>
|
||||
Commands.FirstOrDefault(c => c.IsDefault);
|
||||
public CommandSchema? TryFindDefaultCommand() => Commands.FirstOrDefault(c => c.IsDefault);
|
||||
|
||||
public CommandSchema? TryFindCommand(string commandName) =>
|
||||
Commands.FirstOrDefault(c => c.MatchesName(commandName));
|
||||
|
||||
private IReadOnlyList<CommandSchema> GetDescendantCommands(
|
||||
IReadOnlyList<CommandSchema> potentialParentCommandSchemas,
|
||||
string? parentCommandName)
|
||||
string? parentCommandName
|
||||
)
|
||||
{
|
||||
var result = new List<CommandSchema>();
|
||||
|
||||
@@ -43,7 +41,8 @@ internal partial class ApplicationSchema
|
||||
|
||||
var isDescendant =
|
||||
// Every command is a descendant of the default command
|
||||
string.IsNullOrWhiteSpace(parentCommandName) ||
|
||||
string.IsNullOrWhiteSpace(parentCommandName)
|
||||
||
|
||||
// Otherwise a command is a descendant if it starts with the same name segments
|
||||
potentialParentCommandSchema.Name.StartsWith(
|
||||
parentCommandName + ' ',
|
||||
@@ -69,9 +68,7 @@ internal partial class ApplicationSchema
|
||||
// Filter out descendants of descendants, leave only direct children
|
||||
foreach (var descendant in descendants)
|
||||
{
|
||||
result.RemoveRange(
|
||||
GetDescendantCommands(descendants, descendant.Name)
|
||||
);
|
||||
result.RemoveRange(GetDescendantCommands(descendants, descendant.Name));
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -80,7 +77,6 @@ internal partial class ApplicationSchema
|
||||
|
||||
internal partial class ApplicationSchema
|
||||
{
|
||||
public static ApplicationSchema Resolve(IReadOnlyList<Type> commandTypes) => new(
|
||||
commandTypes.Select(CommandSchema.Resolve).ToArray()
|
||||
);
|
||||
}
|
||||
public static ApplicationSchema Resolve(IReadOnlyList<Type> commandTypes) =>
|
||||
new(commandTypes.Select(CommandSchema.Resolve).ToArray());
|
||||
}
|
||||
|
||||
@@ -13,8 +13,7 @@ internal class BindablePropertyDescriptor : IPropertyDescriptor
|
||||
|
||||
public BindablePropertyDescriptor(PropertyInfo property) => _property = property;
|
||||
|
||||
public object? GetValue(ICommand commandInstance) =>
|
||||
_property.GetValue(commandInstance);
|
||||
public object? GetValue(ICommand commandInstance) => _property.GetValue(commandInstance);
|
||||
|
||||
public void SetValue(ICommand commandInstance, object? value) =>
|
||||
_property.SetValue(commandInstance, value);
|
||||
@@ -42,4 +41,4 @@ internal class BindablePropertyDescriptor : IPropertyDescriptor
|
||||
|
||||
return Array.Empty<object?>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,8 @@ internal partial class CommandSchema
|
||||
string? name,
|
||||
string? description,
|
||||
IReadOnlyList<ParameterSchema> parameters,
|
||||
IReadOnlyList<OptionSchema> options)
|
||||
IReadOnlyList<OptionSchema> options
|
||||
)
|
||||
{
|
||||
Type = type;
|
||||
Name = name;
|
||||
@@ -68,9 +69,9 @@ internal partial class CommandSchema
|
||||
internal partial class CommandSchema
|
||||
{
|
||||
public static bool IsCommandType(Type type) =>
|
||||
type.Implements(typeof(ICommand)) &&
|
||||
type.IsDefined(typeof(CommandAttribute)) &&
|
||||
type is { IsAbstract: false, IsInterface: false };
|
||||
type.Implements(typeof(ICommand))
|
||||
&& type.IsDefined(typeof(CommandAttribute))
|
||||
&& type is { IsAbstract: false, IsInterface: false };
|
||||
|
||||
public static CommandSchema? TryResolve(Type type)
|
||||
{
|
||||
@@ -83,21 +84,22 @@ internal partial class CommandSchema
|
||||
var description = attribute?.Description?.Trim();
|
||||
|
||||
var implicitOptionSchemas = string.IsNullOrWhiteSpace(name)
|
||||
? new[] {OptionSchema.HelpOption, OptionSchema.VersionOption}
|
||||
: new[] {OptionSchema.HelpOption};
|
||||
? new[] { OptionSchema.HelpOption, OptionSchema.VersionOption }
|
||||
: new[] { OptionSchema.HelpOption };
|
||||
|
||||
var properties = type
|
||||
// Get properties directly on the command type
|
||||
.GetProperties()
|
||||
// Get properties directly on the command type
|
||||
.GetProperties()
|
||||
// Get non-abstract properties on interfaces (to support default interfaces members)
|
||||
.Union(type
|
||||
.GetInterfaces()
|
||||
// Only interfaces implementing ICommand for explicitness
|
||||
.Where(i => i != typeof(ICommand) && i.IsAssignableTo(typeof(ICommand)))
|
||||
.SelectMany(i => i
|
||||
.GetProperties()
|
||||
.Where(p => !p.GetMethod.IsAbstract && !p.SetMethod.IsAbstract)
|
||||
)
|
||||
.Union(
|
||||
type.GetInterfaces()
|
||||
// Only interfaces implementing ICommand for explicitness
|
||||
.Where(i => i != typeof(ICommand) && i.IsAssignableTo(typeof(ICommand)))
|
||||
.SelectMany(
|
||||
i =>
|
||||
i.GetProperties()
|
||||
.Where(p => !p.GetMethod.IsAbstract && !p.SetMethod.IsAbstract)
|
||||
)
|
||||
)
|
||||
.ToArray();
|
||||
|
||||
@@ -112,13 +114,7 @@ internal partial class CommandSchema
|
||||
.Concat(implicitOptionSchemas)
|
||||
.ToArray();
|
||||
|
||||
return new CommandSchema(
|
||||
type,
|
||||
name,
|
||||
description,
|
||||
parameterSchemas,
|
||||
optionSchemas
|
||||
);
|
||||
return new CommandSchema(type, name, description, parameterSchemas, optionSchemas);
|
||||
}
|
||||
|
||||
public static CommandSchema Resolve(Type type)
|
||||
@@ -139,4 +135,4 @@ internal partial class CommandSchema
|
||||
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,10 +16,11 @@ internal interface IMemberSchema
|
||||
|
||||
internal static class MemberSchemaExtensions
|
||||
{
|
||||
public static string GetKind(this IMemberSchema memberSchema) => memberSchema switch
|
||||
{
|
||||
ParameterSchema => "Parameter",
|
||||
OptionSchema => "Option",
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(memberSchema))
|
||||
};
|
||||
}
|
||||
public static string GetKind(this IMemberSchema memberSchema) =>
|
||||
memberSchema switch
|
||||
{
|
||||
ParameterSchema => "Parameter",
|
||||
OptionSchema => "Option",
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(memberSchema))
|
||||
};
|
||||
}
|
||||
|
||||
@@ -18,6 +18,6 @@ internal interface IPropertyDescriptor
|
||||
internal static class PropertyDescriptorExtensions
|
||||
{
|
||||
public static bool IsScalar(this IPropertyDescriptor propertyDescriptor) =>
|
||||
propertyDescriptor.Type == typeof(string) ||
|
||||
propertyDescriptor.Type.TryGetEnumerableUnderlyingType() is null;
|
||||
}
|
||||
propertyDescriptor.Type == typeof(string)
|
||||
|| propertyDescriptor.Type.TryGetEnumerableUnderlyingType() is null;
|
||||
}
|
||||
|
||||
@@ -9,9 +9,7 @@ internal partial class NullPropertyDescriptor : IPropertyDescriptor
|
||||
|
||||
public object? GetValue(ICommand commandInstance) => null;
|
||||
|
||||
public void SetValue(ICommand commandInstance, object? value)
|
||||
{
|
||||
}
|
||||
public void SetValue(ICommand commandInstance, object? value) { }
|
||||
|
||||
public IReadOnlyList<object?> GetValidValues() => Array.Empty<object?>();
|
||||
}
|
||||
@@ -19,4 +17,4 @@ internal partial class NullPropertyDescriptor : IPropertyDescriptor
|
||||
internal partial class NullPropertyDescriptor
|
||||
{
|
||||
public static NullPropertyDescriptor Instance { get; } = new();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,8 @@ internal partial class OptionSchema : IMemberSchema
|
||||
bool isRequired,
|
||||
string? description,
|
||||
Type? converterType,
|
||||
IReadOnlyList<Type> validatorTypes)
|
||||
IReadOnlyList<Type> validatorTypes
|
||||
)
|
||||
{
|
||||
Property = property;
|
||||
Name = name;
|
||||
@@ -46,20 +47,18 @@ internal partial class OptionSchema : IMemberSchema
|
||||
}
|
||||
|
||||
public bool MatchesName(string? name) =>
|
||||
!string.IsNullOrWhiteSpace(Name) &&
|
||||
string.Equals(Name, name, StringComparison.OrdinalIgnoreCase);
|
||||
!string.IsNullOrWhiteSpace(Name)
|
||||
&& string.Equals(Name, name, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
public bool MatchesShortName(char? shortName) =>
|
||||
ShortName is not null &&
|
||||
ShortName == shortName;
|
||||
ShortName is not null && ShortName == shortName;
|
||||
|
||||
public bool MatchesIdentifier(string identifier) =>
|
||||
MatchesName(identifier) ||
|
||||
identifier.Length == 1 && MatchesShortName(identifier[0]);
|
||||
MatchesName(identifier) || identifier.Length == 1 && MatchesShortName(identifier[0]);
|
||||
|
||||
public bool MatchesEnvironmentVariable(string environmentVariableName) =>
|
||||
!string.IsNullOrWhiteSpace(EnvironmentVariable) &&
|
||||
string.Equals(EnvironmentVariable, environmentVariableName, StringComparison.Ordinal);
|
||||
!string.IsNullOrWhiteSpace(EnvironmentVariable)
|
||||
&& string.Equals(EnvironmentVariable, environmentVariableName, StringComparison.Ordinal);
|
||||
|
||||
public string GetFormattedIdentifier()
|
||||
{
|
||||
@@ -68,9 +67,7 @@ internal partial class OptionSchema : IMemberSchema
|
||||
// Short name
|
||||
if (ShortName is not null)
|
||||
{
|
||||
buffer
|
||||
.Append('-')
|
||||
.Append(ShortName);
|
||||
buffer.Append('-').Append(ShortName);
|
||||
}
|
||||
|
||||
// Separator
|
||||
@@ -82,9 +79,7 @@ internal partial class OptionSchema : IMemberSchema
|
||||
// Name
|
||||
if (!string.IsNullOrWhiteSpace(Name))
|
||||
{
|
||||
buffer
|
||||
.Append("--")
|
||||
.Append(Name);
|
||||
buffer.Append("--").Append(Name);
|
||||
}
|
||||
|
||||
return buffer.ToString();
|
||||
@@ -120,25 +115,27 @@ internal partial class OptionSchema
|
||||
|
||||
internal partial class OptionSchema
|
||||
{
|
||||
public static OptionSchema HelpOption { get; } = new(
|
||||
NullPropertyDescriptor.Instance,
|
||||
"help",
|
||||
'h',
|
||||
null,
|
||||
false,
|
||||
"Shows help text.",
|
||||
null,
|
||||
Array.Empty<Type>()
|
||||
);
|
||||
public static OptionSchema HelpOption { get; } =
|
||||
new(
|
||||
NullPropertyDescriptor.Instance,
|
||||
"help",
|
||||
'h',
|
||||
null,
|
||||
false,
|
||||
"Shows help text.",
|
||||
null,
|
||||
Array.Empty<Type>()
|
||||
);
|
||||
|
||||
public static OptionSchema VersionOption { get; } = new(
|
||||
NullPropertyDescriptor.Instance,
|
||||
"version",
|
||||
null,
|
||||
null,
|
||||
false,
|
||||
"Shows version information.",
|
||||
null,
|
||||
Array.Empty<Type>()
|
||||
);
|
||||
}
|
||||
public static OptionSchema VersionOption { get; } =
|
||||
new(
|
||||
NullPropertyDescriptor.Instance,
|
||||
"version",
|
||||
null,
|
||||
null,
|
||||
false,
|
||||
"Shows version information.",
|
||||
null,
|
||||
Array.Empty<Type>()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -29,7 +29,8 @@ internal partial class ParameterSchema : IMemberSchema
|
||||
bool isRequired,
|
||||
string? description,
|
||||
Type? converterType,
|
||||
IReadOnlyList<Type> validatorTypes)
|
||||
IReadOnlyList<Type> validatorTypes
|
||||
)
|
||||
{
|
||||
Property = property;
|
||||
Order = order;
|
||||
@@ -40,9 +41,7 @@ internal partial class ParameterSchema : IMemberSchema
|
||||
ValidatorTypes = validatorTypes;
|
||||
}
|
||||
|
||||
public string GetFormattedIdentifier() => Property.IsScalar()
|
||||
? $"<{Name}>"
|
||||
: $"<{Name}...>";
|
||||
public string GetFormattedIdentifier() => Property.IsScalar() ? $"<{Name}>" : $"<{Name}...>";
|
||||
}
|
||||
|
||||
internal partial class ParameterSchema
|
||||
@@ -67,4 +66,4 @@ internal partial class ParameterSchema
|
||||
attribute.Validators
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,12 +16,13 @@ internal partial class Disposable
|
||||
{
|
||||
public static IDisposable Create(Action dispose) => new Disposable(dispose);
|
||||
|
||||
public static IDisposable Merge(IEnumerable<IDisposable> disposables) => Create(() =>
|
||||
{
|
||||
foreach (var disposable in disposables)
|
||||
disposable.Dispose();
|
||||
});
|
||||
public static IDisposable Merge(IEnumerable<IDisposable> disposables) =>
|
||||
Create(() =>
|
||||
{
|
||||
foreach (var disposable in disposables)
|
||||
disposable.Dispose();
|
||||
});
|
||||
|
||||
public static IDisposable Merge(params IDisposable[] disposables) =>
|
||||
Merge((IEnumerable<IDisposable>) disposables);
|
||||
}
|
||||
Merge((IEnumerable<IDisposable>)disposables);
|
||||
}
|
||||
|
||||
@@ -6,15 +6,16 @@ namespace CliFx.Utils;
|
||||
|
||||
internal static class EnvironmentEx
|
||||
{
|
||||
private static readonly Lazy<string?> ProcessPathLazy = new(() =>
|
||||
{
|
||||
using var process = Process.GetCurrentProcess();
|
||||
return process.MainModule?.FileName;
|
||||
});
|
||||
private static readonly Lazy<string?> ProcessPathLazy =
|
||||
new(() =>
|
||||
{
|
||||
using var process = Process.GetCurrentProcess();
|
||||
return process.MainModule?.FileName;
|
||||
});
|
||||
|
||||
public static string? ProcessPath => ProcessPathLazy.Value;
|
||||
|
||||
private static readonly Lazy<Assembly?> EntryAssemblyLazy = new(Assembly.GetEntryAssembly);
|
||||
|
||||
public static Assembly? EntryAssembly => EntryAssemblyLazy.Value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,10 +41,11 @@ internal static class CollectionExtensions
|
||||
|
||||
public static Dictionary<TKey, TValue> ToDictionary<TKey, TValue>(
|
||||
this IDictionary dictionary,
|
||||
IEqualityComparer<TKey> comparer) =>
|
||||
IEqualityComparer<TKey> comparer
|
||||
) =>
|
||||
dictionary
|
||||
.Cast<DictionaryEntry>()
|
||||
.ToDictionary(entry => (TKey) entry.Key, entry => (TValue) entry.Value, comparer);
|
||||
.ToDictionary(entry => (TKey)entry.Key, entry => (TValue)entry.Value, comparer);
|
||||
|
||||
public static Array ToNonGenericArray<T>(this IEnumerable<T> source, Type elementType)
|
||||
{
|
||||
@@ -55,4 +56,4 @@ internal static class CollectionExtensions
|
||||
|
||||
return array;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,11 +8,14 @@ internal static class PropertyExtensions
|
||||
{
|
||||
public static bool IsRequired(this PropertyInfo propertyInfo) =>
|
||||
// Match attribute by name to avoid depending on .NET 7.0+ and to allow polyfilling
|
||||
propertyInfo.GetCustomAttributes().Any(a =>
|
||||
string.Equals(
|
||||
a.GetType().FullName,
|
||||
"System.Runtime.CompilerServices.RequiredMemberAttribute",
|
||||
StringComparison.Ordinal
|
||||
)
|
||||
);
|
||||
}
|
||||
propertyInfo
|
||||
.GetCustomAttributes()
|
||||
.Any(
|
||||
a =>
|
||||
string.Equals(
|
||||
a.GetType().FullName,
|
||||
"System.Runtime.CompilerServices.RequiredMemberAttribute",
|
||||
StringComparison.Ordinal
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,9 +6,7 @@ namespace CliFx.Utils.Extensions;
|
||||
internal static class StringExtensions
|
||||
{
|
||||
public static string? NullIfWhiteSpace(this string str) =>
|
||||
!string.IsNullOrWhiteSpace(str)
|
||||
? str
|
||||
: null;
|
||||
!string.IsNullOrWhiteSpace(str) ? str : null;
|
||||
|
||||
public static string Repeat(this char c, int count) => new(c, count);
|
||||
|
||||
@@ -20,8 +18,9 @@ internal static class StringExtensions
|
||||
public static string ToString(
|
||||
this object obj,
|
||||
IFormatProvider? formatProvider = null,
|
||||
string? format = null) =>
|
||||
string? format = null
|
||||
) =>
|
||||
obj is IFormattable formattable
|
||||
? formattable.ToString(format, formatProvider)
|
||||
: obj.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,8 +25,7 @@ internal static class TypeExtensions
|
||||
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>))
|
||||
return type.GetGenericArguments().FirstOrDefault();
|
||||
|
||||
return type
|
||||
.GetInterfaces()
|
||||
return type.GetInterfaces()
|
||||
.Select(TryGetEnumerableUnderlyingType)
|
||||
.Where(t => t is not null)
|
||||
// Every IEnumerable<T> implements IEnumerable (which is essentially IEnumerable<object>),
|
||||
@@ -35,15 +34,21 @@ internal static class TypeExtensions
|
||||
.MaxBy(t => t != typeof(object));
|
||||
}
|
||||
|
||||
public static MethodInfo? TryGetStaticParseMethod(this Type type, bool withFormatProvider = false)
|
||||
public static MethodInfo? TryGetStaticParseMethod(
|
||||
this Type type,
|
||||
bool withFormatProvider = false
|
||||
)
|
||||
{
|
||||
var argumentTypes = withFormatProvider
|
||||
? new[] {typeof(string), typeof(IFormatProvider)}
|
||||
: new[] {typeof(string)};
|
||||
? new[] { typeof(string), typeof(IFormatProvider) }
|
||||
: new[] { typeof(string) };
|
||||
|
||||
return type.GetMethod("Parse",
|
||||
return type.GetMethod(
|
||||
"Parse",
|
||||
BindingFlags.Public | BindingFlags.Static,
|
||||
null, argumentTypes, null
|
||||
null,
|
||||
argumentTypes,
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
@@ -52,4 +57,4 @@ internal static class TypeExtensions
|
||||
var toStringMethod = type.GetMethod(nameof(ToString), Type.EmptyTypes);
|
||||
return toStringMethod?.GetBaseDefinition()?.DeclaringType != toStringMethod?.DeclaringType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,4 +6,4 @@ internal static class VersionExtensions
|
||||
{
|
||||
public static string ToSemanticString(this Version version) =>
|
||||
version.Revision <= 0 ? version.ToString(3) : version.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,8 +71,13 @@ internal class NoPreambleEncoding : Encoding
|
||||
public override int GetByteCount(string s) => _underlyingEncoding.GetByteCount(s);
|
||||
|
||||
[ExcludeFromCodeCoverage]
|
||||
public override int GetBytes(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex) =>
|
||||
_underlyingEncoding.GetBytes(chars, charIndex, charCount, bytes, byteIndex);
|
||||
public override int GetBytes(
|
||||
char[] chars,
|
||||
int charIndex,
|
||||
int charCount,
|
||||
byte[] bytes,
|
||||
int byteIndex
|
||||
) => _underlyingEncoding.GetBytes(chars, charIndex, charCount, bytes, byteIndex);
|
||||
|
||||
[ExcludeFromCodeCoverage]
|
||||
public override byte[] GetBytes(char[] chars, int index, int count) =>
|
||||
@@ -82,8 +87,13 @@ internal class NoPreambleEncoding : Encoding
|
||||
public override byte[] GetBytes(char[] chars) => _underlyingEncoding.GetBytes(chars);
|
||||
|
||||
[ExcludeFromCodeCoverage]
|
||||
public override int GetBytes(string s, int charIndex, int charCount, byte[] bytes, int byteIndex) =>
|
||||
_underlyingEncoding.GetBytes(s, charIndex, charCount, bytes, byteIndex);
|
||||
public override int GetBytes(
|
||||
string s,
|
||||
int charIndex,
|
||||
int charCount,
|
||||
byte[] bytes,
|
||||
int byteIndex
|
||||
) => _underlyingEncoding.GetBytes(s, charIndex, charCount, bytes, byteIndex);
|
||||
|
||||
[ExcludeFromCodeCoverage]
|
||||
public override byte[] GetBytes(string s) => _underlyingEncoding.GetBytes(s);
|
||||
@@ -96,8 +106,13 @@ internal class NoPreambleEncoding : Encoding
|
||||
public override int GetCharCount(byte[] bytes) => _underlyingEncoding.GetCharCount(bytes);
|
||||
|
||||
[ExcludeFromCodeCoverage]
|
||||
public override int GetChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex) =>
|
||||
_underlyingEncoding.GetChars(bytes, byteIndex, byteCount, chars, charIndex);
|
||||
public override int GetChars(
|
||||
byte[] bytes,
|
||||
int byteIndex,
|
||||
int byteCount,
|
||||
char[] chars,
|
||||
int charIndex
|
||||
) => _underlyingEncoding.GetChars(bytes, byteIndex, byteCount, chars, charIndex);
|
||||
|
||||
[ExcludeFromCodeCoverage]
|
||||
public override char[] GetChars(byte[] bytes) => _underlyingEncoding.GetChars(bytes);
|
||||
@@ -122,7 +137,8 @@ internal class NoPreambleEncoding : Encoding
|
||||
_underlyingEncoding.GetMaxCharCount(byteCount);
|
||||
|
||||
[ExcludeFromCodeCoverage]
|
||||
public override bool IsAlwaysNormalized(NormalizationForm form) => _underlyingEncoding.IsAlwaysNormalized(form);
|
||||
public override bool IsAlwaysNormalized(NormalizationForm form) =>
|
||||
_underlyingEncoding.IsAlwaysNormalized(form);
|
||||
|
||||
[ExcludeFromCodeCoverage]
|
||||
public override Encoder GetEncoder() => _underlyingEncoding.GetEncoder();
|
||||
@@ -131,13 +147,11 @@ internal class NoPreambleEncoding : Encoding
|
||||
public override Decoder GetDecoder() => _underlyingEncoding.GetDecoder();
|
||||
|
||||
[ExcludeFromCodeCoverage]
|
||||
public override object Clone() => new NoPreambleEncoding((Encoding) base.Clone());
|
||||
public override object Clone() => new NoPreambleEncoding((Encoding)base.Clone());
|
||||
}
|
||||
|
||||
internal static class NoPreambleEncodingExtensions
|
||||
{
|
||||
public static Encoding WithoutPreamble(this Encoding encoding) =>
|
||||
encoding.GetPreamble().Length > 0
|
||||
? new NoPreambleEncoding(encoding)
|
||||
: encoding;
|
||||
}
|
||||
encoding.GetPreamble().Length > 0 ? new NoPreambleEncoding(encoding) : encoding;
|
||||
}
|
||||
|
||||
@@ -13,10 +13,10 @@ internal static class PathEx
|
||||
|
||||
public static bool AreEqual(string path1, string path2)
|
||||
{
|
||||
static string Normalize(string path) => Path
|
||||
.GetFullPath(path)
|
||||
.Trim(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
|
||||
static string Normalize(string path) =>
|
||||
Path.GetFullPath(path)
|
||||
.Trim(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
|
||||
|
||||
return EqualityComparer.Equals(Normalize(path1), Normalize(path2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,4 +9,4 @@ internal static class ProcessEx
|
||||
using var process = Process.GetCurrentProcess();
|
||||
return process.Id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,8 @@ internal partial class StackFrame
|
||||
string methodName,
|
||||
IReadOnlyList<StackFrameParameter> parameters,
|
||||
string? filePath,
|
||||
string? lineNumber)
|
||||
string? lineNumber
|
||||
)
|
||||
{
|
||||
ParentType = parentType;
|
||||
MethodName = methodName;
|
||||
@@ -52,8 +53,9 @@ internal partial class StackFrame
|
||||
private const string NotSpace = @"[^\x20\t]";
|
||||
|
||||
// Taken from https://github.com/atifaziz/StackTraceParser
|
||||
private static readonly Regex Pattern = new(
|
||||
$$"""
|
||||
private static readonly Regex Pattern =
|
||||
new(
|
||||
$$"""
|
||||
^
|
||||
{{Space}}*
|
||||
\w+ {{Space}}+
|
||||
@@ -81,13 +83,13 @@ internal partial class StackFrame
|
||||
\s*
|
||||
$
|
||||
""",
|
||||
RegexOptions.IgnoreCase |
|
||||
RegexOptions.Multiline |
|
||||
RegexOptions.ExplicitCapture |
|
||||
RegexOptions.CultureInvariant |
|
||||
RegexOptions.IgnorePatternWhitespace,
|
||||
TimeSpan.FromSeconds(5)
|
||||
);
|
||||
RegexOptions.IgnoreCase
|
||||
| RegexOptions.Multiline
|
||||
| RegexOptions.ExplicitCapture
|
||||
| RegexOptions.CultureInvariant
|
||||
| RegexOptions.IgnorePatternWhitespace,
|
||||
TimeSpan.FromSeconds(5)
|
||||
);
|
||||
|
||||
public static IEnumerable<StackFrame> ParseTrace(string stackTrace)
|
||||
{
|
||||
@@ -106,8 +108,7 @@ internal partial class StackFrame
|
||||
}
|
||||
|
||||
return from m in matches
|
||||
select m.Groups
|
||||
into groups
|
||||
select m.Groups into groups
|
||||
let pt = groups["pt"].Captures
|
||||
let pn = groups["pn"].Captures
|
||||
select new StackFrame(
|
||||
@@ -121,4 +122,4 @@ internal partial class StackFrame
|
||||
groups["line"].Value.NullIfWhiteSpace()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user