Fix formatting

This commit is contained in:
Tyrrrz
2023-08-22 21:36:05 +03:00
parent 21b601da66
commit aed53eb090
53 changed files with 435 additions and 391 deletions

View File

@@ -29,10 +29,11 @@ public class ApplicationConfiguration
public ApplicationConfiguration(
IReadOnlyList<Type> commandTypes,
bool isDebugModeAllowed,
bool isPreviewModeAllowed)
bool isPreviewModeAllowed
)
{
CommandTypes = commandTypes;
IsDebugModeAllowed = isDebugModeAllowed;
IsPreviewModeAllowed = isPreviewModeAllowed;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -35,7 +35,5 @@ public sealed class CommandAttribute : Attribute
/// <summary>
/// Initializes an instance of <see cref="CommandAttribute" />.
/// </summary>
public CommandAttribute()
{
}
}
public CommandAttribute() { }
}

View File

@@ -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) { }
}

View File

@@ -70,4 +70,4 @@ public sealed class CommandParameterAttribute : Attribute
{
Order = order;
}
}
}

View File

@@ -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()
);
}

View File

@@ -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();
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}

View File

@@ -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) { }
}

View File

@@ -17,4 +17,4 @@ public abstract class BindingConverter<T> : IBindingConverter
public abstract T Convert(string? rawValue);
object? IBindingConverter.Convert(string? rawValue) => Convert(rawValue);
}
}

View File

@@ -14,4 +14,4 @@ public class BindingValidationError
/// Initializes an instance of <see cref="BindingValidationError" />.
/// </summary>
public BindingValidationError(string message) => Message = message;
}
}

View File

@@ -32,4 +32,4 @@ public abstract class BindingValidator<T> : IBindingValidator
public abstract BindingValidationError? Validate(T? value);
BindingValidationError? IBindingValidator.Validate(object? value) => Validate((T?)value);
}
}

View File

@@ -17,4 +17,4 @@ internal class FallbackDefaultCommand : ICommand
// Never actually executed
[ExcludeFromCodeCoverage]
public ValueTask ExecuteAsync(IConsole console) => default;
}
}

View File

@@ -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);
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}
}

View File

@@ -16,4 +16,4 @@ public interface ICommand
/// <c>return default;</c>
/// </remarks>
ValueTask ExecuteAsync(IConsole console);
}
}

View File

@@ -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));
}

View File

@@ -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 };
}

View File

@@ -29,4 +29,4 @@ public class DefaultTypeActivator : ITypeActivator
);
}
}
}
}

View File

@@ -34,4 +34,4 @@ public class DelegateTypeActivator : ITypeActivator
return instance;
}
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}
}

View File

@@ -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)
);
}
}

View File

@@ -27,4 +27,4 @@ internal static class TypeActivatorExtensions
return (T)activator.CreateInstance(type);
}
}
}

View File

@@ -119,4 +119,4 @@ public class SystemConsole : IConsole, IDisposable
Output.Dispose();
Error.Dispose();
}
}
}

View File

@@ -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
);
}
}
}

View File

@@ -13,4 +13,4 @@ internal class DirectiveInput
string.Equals(Name, "preview", StringComparison.OrdinalIgnoreCase);
public DirectiveInput(string name) => Name = name;
}
}

View File

@@ -16,4 +16,4 @@ internal class EnvironmentVariableInput
}
public IReadOnlyList<string> SplitValues() => Value.Split(Path.PathSeparator);
}
}

View File

@@ -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
};
}

View File

@@ -7,4 +7,4 @@ internal class ParameterInput
public ParameterInput(string value) => Value = value;
public string GetFormattedIdentifier() => $"<{Value}>";
}
}

View File

@@ -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());
}

View File

@@ -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?>();
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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))
};
}

View File

@@ -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;
}

View File

@@ -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();
}
}

View File

@@ -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>()
);
}

View File

@@ -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
);
}
}
}

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}
}

View File

@@ -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
)
);
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}
}

View File

@@ -6,4 +6,4 @@ internal static class VersionExtensions
{
public static string ToSemanticString(this Version version) =>
version.Revision <= 0 ? version.ToString(3) : version.ToString();
}
}

View File

@@ -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;
}

View File

@@ -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));
}
}
}

View File

@@ -9,4 +9,4 @@ internal static class ProcessEx
using var process = Process.GetCurrentProcess();
return process.Id;
}
}
}

View File

@@ -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()
);
}
}
}