From a62ce71424e28e810b898d1e991a96d94e93bed6 Mon Sep 17 00:00:00 2001
From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com>
Date: Tue, 3 Sep 2024 02:03:50 +0300
Subject: [PATCH] asd
---
CliFx.Demo/Domain/LibraryJsonContext.cs | 2 +-
CliFx.Demo/Domain/LibraryProvider.cs | 3 +-
CliFx/Attributes/CommandAttribute.cs | 7 +-
.../Attributes/CommandHelpOptionAttribute.cs | 2 +-
CliFx/Attributes/CommandOptionAttribute.cs | 6 +-
CliFx/Attributes/CommandParameterAttribute.cs | 5 +-
.../CommandVersionOptionAttribute.cs | 2 +-
CliFx/Exceptions/CliFxException.cs | 2 +-
CliFx/Extensibility/BoolBindingConverter.cs | 2 +-
.../ConvertibleBindingConverter.cs | 2 +-
.../DateTimeOffsetBindingConverter.cs | 2 +-
.../Extensibility/DelegateBindingConverter.cs | 2 +-
CliFx/Extensibility/EnumBindingConverter.cs | 2 +-
CliFx/Extensibility/NoopBindingConverter.cs | 2 +-
.../Extensibility/NullableBindingConverter.cs | 2 +-
.../Extensibility/TimeSpanBindingConverter.cs | 2 +-
CliFx/Formatting/HelpConsoleFormatter.cs | 47 ++-----------
CliFx/Infrastructure/ConsoleReader.cs | 2 +-
CliFx/Infrastructure/ConsoleWriter.cs | 2 +-
CliFx/Infrastructure/DefaultTypeActivator.cs | 2 +-
CliFx/Infrastructure/DelegateTypeActivator.cs | 2 +-
CliFx/Infrastructure/ITypeActivator.cs | 2 +-
CliFx/Schema/CommandInputSchema.cs | 70 +++++++++++++------
CliFx/Schema/CommandOptionSchema.cs | 36 ++--------
CliFx/Schema/CommandParameterSchema.cs | 22 ++++--
CliFx/Schema/CommandSchema.cs | 45 ++++--------
CliFx/Schema/PropertyBinding.cs | 14 ++--
CliFx/Utils/Extensions/PropertyExtensions.cs | 20 ------
CliFx/Utils/Extensions/TypeExtensions.cs | 23 ------
29 files changed, 124 insertions(+), 208 deletions(-)
delete mode 100644 CliFx/Utils/Extensions/PropertyExtensions.cs
diff --git a/CliFx.Demo/Domain/LibraryJsonContext.cs b/CliFx.Demo/Domain/LibraryJsonContext.cs
index 833e67e..b64e1c7 100644
--- a/CliFx.Demo/Domain/LibraryJsonContext.cs
+++ b/CliFx.Demo/Domain/LibraryJsonContext.cs
@@ -3,4 +3,4 @@
namespace CliFx.Demo.Domain;
[JsonSerializable(typeof(Library))]
-public partial class LibraryJsonContext : JsonSerializerContext;
\ No newline at end of file
+public partial class LibraryJsonContext : JsonSerializerContext;
diff --git a/CliFx.Demo/Domain/LibraryProvider.cs b/CliFx.Demo/Domain/LibraryProvider.cs
index 9db7f8d..edd8b91 100644
--- a/CliFx.Demo/Domain/LibraryProvider.cs
+++ b/CliFx.Demo/Domain/LibraryProvider.cs
@@ -22,7 +22,8 @@ public class LibraryProvider
var data = File.ReadAllText(StorageFilePath);
- return JsonSerializer.Deserialize(data, LibraryJsonContext.Default.Library) ?? Library.Empty;
+ return JsonSerializer.Deserialize(data, LibraryJsonContext.Default.Library)
+ ?? Library.Empty;
}
public Book? TryGetBook(string title) =>
diff --git a/CliFx/Attributes/CommandAttribute.cs b/CliFx/Attributes/CommandAttribute.cs
index 16024fe..d08a4be 100644
--- a/CliFx/Attributes/CommandAttribute.cs
+++ b/CliFx/Attributes/CommandAttribute.cs
@@ -4,9 +4,10 @@ namespace CliFx.Attributes;
///
/// Annotates a type that defines a command.
-/// If a command is named, then the user must provide its name through the command-line arguments in order to execute it.
-/// If a command is not named, then it is treated as the application's default command and is executed when no other
-/// command is specified.
+/// If the command is named, then the user must provide its name through the
+/// command-line arguments in order to execute it.
+/// If the command is not named, then it is treated as the application's
+/// default command and is executed whenever the user does not provide a command name.
///
///
/// Only one default command is allowed per application.
diff --git a/CliFx/Attributes/CommandHelpOptionAttribute.cs b/CliFx/Attributes/CommandHelpOptionAttribute.cs
index fa0fd71..d8a024a 100644
--- a/CliFx/Attributes/CommandHelpOptionAttribute.cs
+++ b/CliFx/Attributes/CommandHelpOptionAttribute.cs
@@ -4,7 +4,7 @@
/// Binds a property to the help option of a command.
///
///
-/// This attribute is applied automatically by the framework and should not be used explicitly.
+/// This attribute is applied automatically by the framework and should not need to be used explicitly.
///
public class CommandHelpOptionAttribute : CommandOptionAttribute
{
diff --git a/CliFx/Attributes/CommandOptionAttribute.cs b/CliFx/Attributes/CommandOptionAttribute.cs
index 6dd90a7..c114c8b 100644
--- a/CliFx/Attributes/CommandOptionAttribute.cs
+++ b/CliFx/Attributes/CommandOptionAttribute.cs
@@ -1,5 +1,4 @@
using System;
-using CliFx.Extensibility;
namespace CliFx.Attributes;
@@ -7,7 +6,8 @@ namespace CliFx.Attributes;
/// Binds a property to a command option — a command-line input that is identified by a name and/or a short name.
///
///
-/// All options in a command must have unique names (comparison IS NOT case-sensitive) and short names (comparison IS case-sensitive).
+/// All options in a command must have unique names (comparison IS NOT case-sensitive)
+/// and short names (comparison IS case-sensitive).
///
[AttributeUsage(AttributeTargets.Property)]
public class CommandOptionAttribute : CommandInputAttribute
@@ -51,7 +51,7 @@ public class CommandOptionAttribute : CommandInputAttribute
///
/// Whether this option is required (default: false).
- /// If an option is required, the user will get an error if they don't set it.
+ /// If an option is required, the user will get an error when they don't set it.
///
///
/// You can use the required keyword on the property (introduced in C# 11) to implicitly
diff --git a/CliFx/Attributes/CommandParameterAttribute.cs b/CliFx/Attributes/CommandParameterAttribute.cs
index 9cc1f5e..7a8790b 100644
--- a/CliFx/Attributes/CommandParameterAttribute.cs
+++ b/CliFx/Attributes/CommandParameterAttribute.cs
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
-using CliFx.Extensibility;
namespace CliFx.Attributes;
@@ -10,7 +9,7 @@ namespace CliFx.Attributes;
///
///
/// All parameters in a command must have unique order values.
-/// If a parameter is bound to a property whose type is a sequence (e.g. Array, ; except ),
+/// If a parameter is bound to a property whose type is a sequence (i.e. implements ; except ),
/// then it must have the highest order in the command.
/// Only one sequential parameter is allowed per command.
///
@@ -24,7 +23,7 @@ public class CommandParameterAttribute(int order) : CommandInputAttribute
///
/// Whether this parameter is required (default: true).
- /// If a parameter is required, the user will get an error if they don't set it.
+ /// If a parameter is required, the user will get an error when they don't set it.
///
///
/// Parameter marked as non-required must have the highest order in the command.
diff --git a/CliFx/Attributes/CommandVersionOptionAttribute.cs b/CliFx/Attributes/CommandVersionOptionAttribute.cs
index 0169d28..072a1c9 100644
--- a/CliFx/Attributes/CommandVersionOptionAttribute.cs
+++ b/CliFx/Attributes/CommandVersionOptionAttribute.cs
@@ -4,7 +4,7 @@
/// Binds a property to the version option of a command.
///
///
-/// This attribute is applied automatically by the framework and should not be used explicitly.
+/// This attribute is applied automatically by the framework and should not need to be used explicitly.
///
public class CommandVersionOptionAttribute : CommandOptionAttribute
{
diff --git a/CliFx/Exceptions/CliFxException.cs b/CliFx/Exceptions/CliFxException.cs
index 5924188..76f053a 100644
--- a/CliFx/Exceptions/CliFxException.cs
+++ b/CliFx/Exceptions/CliFxException.cs
@@ -3,7 +3,7 @@
namespace CliFx.Exceptions;
///
-/// Exception thrown when there is an error during application execution.
+/// Exception thrown within .
///
public partial class CliFxException(
string message,
diff --git a/CliFx/Extensibility/BoolBindingConverter.cs b/CliFx/Extensibility/BoolBindingConverter.cs
index 400973a..493d557 100644
--- a/CliFx/Extensibility/BoolBindingConverter.cs
+++ b/CliFx/Extensibility/BoolBindingConverter.cs
@@ -3,7 +3,7 @@
namespace CliFx.Extensibility;
///
-/// Converter for binding command inputs to properties of type .
+/// Converter for activating command inputs bound to properties of type .
///
public class BoolBindingConverter : BindingConverter
{
diff --git a/CliFx/Extensibility/ConvertibleBindingConverter.cs b/CliFx/Extensibility/ConvertibleBindingConverter.cs
index 073367a..bb23f59 100644
--- a/CliFx/Extensibility/ConvertibleBindingConverter.cs
+++ b/CliFx/Extensibility/ConvertibleBindingConverter.cs
@@ -3,7 +3,7 @@
namespace CliFx.Extensibility;
///
-/// Converter for binding command inputs to properties whose types implement .
+/// Converter for activating command inputs bound to properties whose types implement .
///
public class ConvertibleBindingConverter : BindingConverter
where T : IConvertible
diff --git a/CliFx/Extensibility/DateTimeOffsetBindingConverter.cs b/CliFx/Extensibility/DateTimeOffsetBindingConverter.cs
index 546ed40..5c9aedc 100644
--- a/CliFx/Extensibility/DateTimeOffsetBindingConverter.cs
+++ b/CliFx/Extensibility/DateTimeOffsetBindingConverter.cs
@@ -3,7 +3,7 @@
namespace CliFx.Extensibility;
///
-/// Converter for binding command inputs to properties of type .
+/// Converter for activating command inputs bound to properties of type .
///
public class DateTimeOffsetBindingConverter : BindingConverter
{
diff --git a/CliFx/Extensibility/DelegateBindingConverter.cs b/CliFx/Extensibility/DelegateBindingConverter.cs
index db334bf..6383072 100644
--- a/CliFx/Extensibility/DelegateBindingConverter.cs
+++ b/CliFx/Extensibility/DelegateBindingConverter.cs
@@ -3,7 +3,7 @@
namespace CliFx.Extensibility;
///
-/// Converter for binding command inputs to properties using a custom delegate.
+/// Converter for activating command inputs bound to properties using a custom delegate.
///
public class DelegateBindingConverter(Func convert)
: BindingConverter
diff --git a/CliFx/Extensibility/EnumBindingConverter.cs b/CliFx/Extensibility/EnumBindingConverter.cs
index 02a62ac..a73d10c 100644
--- a/CliFx/Extensibility/EnumBindingConverter.cs
+++ b/CliFx/Extensibility/EnumBindingConverter.cs
@@ -3,7 +3,7 @@
namespace CliFx.Extensibility;
///
-/// Converter for binding command inputs to properties of type .
+/// Converter for activating command inputs bound to properties of type .
///
public class EnumBindingConverter : BindingConverter
where T : struct, Enum
diff --git a/CliFx/Extensibility/NoopBindingConverter.cs b/CliFx/Extensibility/NoopBindingConverter.cs
index 92537cd..cd86a4c 100644
--- a/CliFx/Extensibility/NoopBindingConverter.cs
+++ b/CliFx/Extensibility/NoopBindingConverter.cs
@@ -3,7 +3,7 @@
namespace CliFx.Extensibility;
///
-/// Converter for binding command inputs to properties without any conversion.
+/// Converter for activating command inputs bound to properties without performing any conversion.
///
public class NoopBindingConverter : IBindingConverter
{
diff --git a/CliFx/Extensibility/NullableBindingConverter.cs b/CliFx/Extensibility/NullableBindingConverter.cs
index 3150e2b..ea367e4 100644
--- a/CliFx/Extensibility/NullableBindingConverter.cs
+++ b/CliFx/Extensibility/NullableBindingConverter.cs
@@ -3,7 +3,7 @@
namespace CliFx.Extensibility;
///
-/// Converter for binding command inputs to properties of type .
+/// Converter for activating command inputs bound to properties of type .
///
public class NullableBindingConverter(BindingConverter innerConverter) : BindingConverter
where T : struct
diff --git a/CliFx/Extensibility/TimeSpanBindingConverter.cs b/CliFx/Extensibility/TimeSpanBindingConverter.cs
index f9fd4f2..adb8248 100644
--- a/CliFx/Extensibility/TimeSpanBindingConverter.cs
+++ b/CliFx/Extensibility/TimeSpanBindingConverter.cs
@@ -3,7 +3,7 @@
namespace CliFx.Extensibility;
///
-/// Converter for binding command inputs to properties of type .
+/// Converter for activating command inputs bound to properties of type .
///
public class TimeSpanBindingConverter : BindingConverter
{
diff --git a/CliFx/Formatting/HelpConsoleFormatter.cs b/CliFx/Formatting/HelpConsoleFormatter.cs
index 2a8cce4..c240e36 100644
--- a/CliFx/Formatting/HelpConsoleFormatter.cs
+++ b/CliFx/Formatting/HelpConsoleFormatter.cs
@@ -1,5 +1,4 @@
using System;
-using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
@@ -307,48 +306,14 @@ internal class HelpConsoleFormatter(ConsoleWriter consoleWriter, HelpContext con
if (defaultValue is null)
return;
- // Non-Scalar
- if (defaultValue is not string && defaultValue is IEnumerable defaultValues)
+ if (schema.Property.Type.IsToStringOverriden())
{
- var elementType =
- schema.Property.Type.TryGetEnumerableUnderlyingType() ?? typeof(object);
+ Write(ConsoleColor.White, "Default: ");
- if (elementType.IsToStringOverriden())
- {
- Write(ConsoleColor.White, "Default: ");
-
- var isFirst = true;
-
- foreach (var element in defaultValues)
- {
- if (isFirst)
- {
- isFirst = false;
- }
- else
- {
- Write(", ");
- }
-
- Write('"');
- Write(element.ToString(CultureInfo.InvariantCulture));
- Write('"');
- }
-
- Write('.');
- }
- }
- else
- {
- if (schema.Property.Type.IsToStringOverriden())
- {
- Write(ConsoleColor.White, "Default: ");
-
- Write('"');
- Write(defaultValue.ToString(CultureInfo.InvariantCulture));
- Write('"');
- Write('.');
- }
+ Write('"');
+ Write(defaultValue.ToString(CultureInfo.InvariantCulture));
+ Write('"');
+ Write('.');
}
}
diff --git a/CliFx/Infrastructure/ConsoleReader.cs b/CliFx/Infrastructure/ConsoleReader.cs
index c0f0f28..ad1f293 100644
--- a/CliFx/Infrastructure/ConsoleReader.cs
+++ b/CliFx/Infrastructure/ConsoleReader.cs
@@ -7,7 +7,7 @@ using System.Threading.Tasks;
namespace CliFx.Infrastructure;
///
-/// Implements a for reading characters or binary data from a console stream.
+/// Implements a for reading characters or binary data from a console stream.
///
// Both the underlying stream AND the stream reader must be synchronized!
// https://github.com/Tyrrrz/CliFx/issues/123
diff --git a/CliFx/Infrastructure/ConsoleWriter.cs b/CliFx/Infrastructure/ConsoleWriter.cs
index 516945e..bed047b 100644
--- a/CliFx/Infrastructure/ConsoleWriter.cs
+++ b/CliFx/Infrastructure/ConsoleWriter.cs
@@ -8,7 +8,7 @@ using CliFx.Utils;
namespace CliFx.Infrastructure;
///
-/// Implements a for writing characters or binary data to a console stream.
+/// Implements a for writing characters or binary data to a console stream.
///
// Both the underlying stream AND the stream writer must be synchronized!
// https://github.com/Tyrrrz/CliFx/issues/123
diff --git a/CliFx/Infrastructure/DefaultTypeActivator.cs b/CliFx/Infrastructure/DefaultTypeActivator.cs
index b2ddab3..09917b3 100644
--- a/CliFx/Infrastructure/DefaultTypeActivator.cs
+++ b/CliFx/Infrastructure/DefaultTypeActivator.cs
@@ -5,7 +5,7 @@ using CliFx.Exceptions;
namespace CliFx.Infrastructure;
///
-/// Implementation of that instantiates an object by using its parameterless constructor.
+/// Implementation of that instantiates a type by using its parameterless constructor.
///
public class DefaultTypeActivator : ITypeActivator
{
diff --git a/CliFx/Infrastructure/DelegateTypeActivator.cs b/CliFx/Infrastructure/DelegateTypeActivator.cs
index 772a460..dfcd288 100644
--- a/CliFx/Infrastructure/DelegateTypeActivator.cs
+++ b/CliFx/Infrastructure/DelegateTypeActivator.cs
@@ -5,7 +5,7 @@ using CliFx.Exceptions;
namespace CliFx.Infrastructure;
///
-/// Implementation of that instantiates an object by using a predefined delegate.
+/// Implementation of that instantiates a type by using a predefined delegate.
///
public class DelegateTypeActivator(Func createInstance) : ITypeActivator
{
diff --git a/CliFx/Infrastructure/ITypeActivator.cs b/CliFx/Infrastructure/ITypeActivator.cs
index d01d65f..a5faf5e 100644
--- a/CliFx/Infrastructure/ITypeActivator.cs
+++ b/CliFx/Infrastructure/ITypeActivator.cs
@@ -5,7 +5,7 @@ using CliFx.Exceptions;
namespace CliFx.Infrastructure;
///
-/// Abstraction for a service that can instantiate objects at run-time.
+/// Abstraction for a service that can instantiate types at run-time.
///
public interface ITypeActivator
{
diff --git a/CliFx/Schema/CommandInputSchema.cs b/CliFx/Schema/CommandInputSchema.cs
index 93b45ec..0e59c20 100644
--- a/CliFx/Schema/CommandInputSchema.cs
+++ b/CliFx/Schema/CommandInputSchema.cs
@@ -14,23 +14,21 @@ namespace CliFx.Schema;
///
public abstract class CommandInputSchema(
PropertyBinding property,
+ bool isSequence,
string? description,
IBindingConverter converter,
IReadOnlyList validators
)
{
- internal abstract string Kind { get; }
-
- internal abstract string FormattedIdentifier { get; }
-
///
/// CLR property to which this input is bound.
///
public PropertyBinding Property { get; } = property;
- internal bool IsSequence { get; } =
- property.Type != typeof(string)
- && property.Type.TryGetEnumerableUnderlyingType() is not null;
+ ///
+ /// Whether this input is a sequence (i.e. multiple values can be provided).
+ ///
+ public bool IsSequence { get; } = isSequence;
///
/// Input description, used in the help text.
@@ -51,14 +49,14 @@ public abstract class CommandInputSchema(
{
var errors = Validators
.Select(validator => validator.Validate(value))
- .OfType()
+ .WhereNotNull()
.ToArray();
if (errors.Any())
{
throw CliFxException.UserError(
$"""
- {Kind} {FormattedIdentifier} has been provided with an invalid value.
+ {this.GetKind()} {this.GetFormattedIdentifier()} has been provided with an invalid value.
Error(s):
{errors.Select(e => "- " + e.Message).JoinToString(Environment.NewLine)}
"""
@@ -72,7 +70,7 @@ public abstract class CommandInputSchema(
try
{
- // Multiple values expected, single or multiple values provided
+ // Sequential input; zero or more values provided
if (IsSequence)
{
var values = rawValues.Select(v => Converter.Convert(v, formatProvider)).ToArray();
@@ -80,23 +78,22 @@ public abstract class CommandInputSchema(
// TODO: cast array to the proper type
Validate(values);
-
- Property.Set(instance, values);
+ Property.SetValue(instance, values);
}
- // Single value expected, single value provided
+ // Non-sequential input; zero or one value provided
else if (rawValues.Count <= 1)
{
var value = Converter.Convert(rawValues.SingleOrDefault(), formatProvider);
- Validate(value);
- Property.Set(instance, value);
+ Validate(value);
+ Property.SetValue(instance, value);
}
- // Single value expected, multiple values provided
+ // Non-sequential input; more than one value provided
else
{
throw CliFxException.UserError(
$"""
- {Kind} {FormattedIdentifier} expects a single value, but provided with multiple:
+ {this.GetKind()} {this.GetFormattedIdentifier()} expects a single value, but provided with multiple:
{rawValues.Select(v => '<' + v + '>').JoinToString(" ")}
"""
);
@@ -106,7 +103,7 @@ public abstract class CommandInputSchema(
{
throw CliFxException.UserError(
$"""
- {Kind} {FormattedIdentifier} cannot be set from the provided value(s):
+ {this.GetKind()} {this.GetFormattedIdentifier()} cannot be set from the provided value(s):
{rawValues.Select(v => '<' + v + '>').JoinToString(" ")}
Error: {ex.Message}
""",
@@ -117,7 +114,7 @@ public abstract class CommandInputSchema(
///
[ExcludeFromCodeCoverage]
- public override string ToString() => FormattedIdentifier;
+ public override string ToString() => this.GetFormattedIdentifier();
}
///
@@ -134,8 +131,41 @@ public abstract class CommandInputSchema<
TProperty
>(
PropertyBinding property,
+ bool isSequence,
string? description,
BindingConverter converter,
IReadOnlyList> validators
-) : CommandInputSchema(property, description, converter, validators)
+) : CommandInputSchema(property, isSequence, description, converter, validators)
where TCommand : ICommand;
+
+// Define these as extension methods to avoid exposing them as protected members (i.e. essentially public API)
+internal static class CommandInputSchemaExtensions
+{
+ public static string GetKind(this CommandInputSchema schema) =>
+ schema switch
+ {
+ CommandParameterSchema => "Parameter",
+ CommandOptionSchema => "Option",
+ _ => throw new InvalidOperationException("Unknown input schema type.")
+ };
+
+ public static string GetFormattedIdentifier(this CommandInputSchema schema) =>
+ schema switch
+ {
+ CommandParameterSchema parameter
+ => parameter.IsSequence ? $"<{parameter.Name}>" : $"<{parameter.Name}...>",
+ CommandOptionSchema option
+ => option switch
+ {
+ { Name: not null, ShortName: not null }
+ => $"-{option.ShortName}|--{option.Name}",
+ { Name: not null } => $"--{option.Name}",
+ { ShortName: not null } => $"-{option.ShortName}",
+ _
+ => throw new InvalidOperationException(
+ "Option must have a name or a short name."
+ )
+ },
+ _ => throw new ArgumentOutOfRangeException(nameof(schema))
+ };
+}
diff --git a/CliFx/Schema/CommandOptionSchema.cs b/CliFx/Schema/CommandOptionSchema.cs
index 025c509..16f7faa 100644
--- a/CliFx/Schema/CommandOptionSchema.cs
+++ b/CliFx/Schema/CommandOptionSchema.cs
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
-using System.Text;
using CliFx.Extensibility;
namespace CliFx.Schema;
@@ -11,6 +10,7 @@ namespace CliFx.Schema;
///
public class CommandOptionSchema(
PropertyBinding property,
+ bool isSequence,
string? name,
char? shortName,
string? environmentVariable,
@@ -18,38 +18,8 @@ public class CommandOptionSchema(
string? description,
IBindingConverter converter,
IReadOnlyList validators
-) : CommandInputSchema(property, description, converter, validators)
+) : CommandInputSchema(property, isSequence, description, converter, validators)
{
- internal override string Kind => "Option";
-
- internal override string FormattedIdentifier
- {
- get
- {
- var buffer = new StringBuilder();
-
- // Short name
- if (ShortName is not null)
- {
- buffer.Append('-').Append(ShortName);
- }
-
- // Separator
- if (!string.IsNullOrWhiteSpace(Name) && ShortName is not null)
- {
- buffer.Append('|');
- }
-
- // Name
- if (!string.IsNullOrWhiteSpace(Name))
- {
- buffer.Append("--").Append(Name);
- }
-
- return buffer.ToString();
- }
- }
-
///
/// Option name.
///
@@ -99,6 +69,7 @@ public class CommandOptionSchema<
TProperty
>(
PropertyBinding property,
+ bool isSequence,
string? name,
char? shortName,
string? environmentVariable,
@@ -109,6 +80,7 @@ public class CommandOptionSchema<
)
: CommandOptionSchema(
property,
+ isSequence,
name,
shortName,
environmentVariable,
diff --git a/CliFx/Schema/CommandParameterSchema.cs b/CliFx/Schema/CommandParameterSchema.cs
index 9fd9c27..5daf9c4 100644
--- a/CliFx/Schema/CommandParameterSchema.cs
+++ b/CliFx/Schema/CommandParameterSchema.cs
@@ -9,20 +9,17 @@ namespace CliFx.Schema;
///
public class CommandParameterSchema(
PropertyBinding property,
+ bool isSequence,
int order,
string name,
bool isRequired,
string? description,
IBindingConverter converter,
IReadOnlyList validators
-) : CommandInputSchema(property, description, converter, validators)
+) : CommandInputSchema(property, isSequence, description, converter, validators)
{
- internal override string Kind => "Parameter";
-
- internal override string FormattedIdentifier => IsSequence ? $"<{Name}>" : $"<{Name}...>";
-
///
- /// Order, in which the parameter is bound from the command-line arguments.
+ /// Order, in which the parameter is activated from the command-line arguments.
///
public int Order { get; } = order;
@@ -51,11 +48,22 @@ public class CommandParameterSchema<
TProperty
>(
PropertyBinding property,
+ bool isSequence,
int order,
string name,
bool isRequired,
string? description,
BindingConverter converter,
IReadOnlyList> validators
-) : CommandParameterSchema(property, order, name, isRequired, description, converter, validators)
+)
+ : CommandParameterSchema(
+ property,
+ isSequence,
+ order,
+ name,
+ isRequired,
+ description,
+ converter,
+ validators
+ )
where TCommand : ICommand;
diff --git a/CliFx/Schema/CommandSchema.cs b/CliFx/Schema/CommandSchema.cs
index 7d8b56e..5ced0aa 100644
--- a/CliFx/Schema/CommandSchema.cs
+++ b/CliFx/Schema/CommandSchema.cs
@@ -62,24 +62,8 @@ public class CommandSchema(
? string.Equals(name, Name, StringComparison.OrdinalIgnoreCase)
: string.IsNullOrWhiteSpace(name);
- internal IReadOnlyDictionary GetValues(ICommand instance)
- {
- var result = new Dictionary();
-
- foreach (var parameter in Parameters)
- {
- var value = parameter.Property.Get(instance);
- result[parameter] = value;
- }
-
- foreach (var option in Options)
- {
- var value = option.Property.Get(instance);
- result[option] = value;
- }
-
- return result;
- }
+ internal IReadOnlyDictionary GetValues(ICommand instance) =>
+ Inputs.ToDictionary(input => input, input => input.Property.GetValue(instance));
private void ActivateParameters(ICommand instance, CommandArguments arguments)
{
@@ -91,21 +75,20 @@ public class CommandSchema(
foreach (var parameter in Parameters.OrderBy(p => p.Order))
{
- // Break when there are no remaining inputs
+ // Break when there are no remaining tokens
if (position >= arguments.Parameters.Count)
break;
- // Sequence: take all remaining inputs starting from the current position
+ // Sequential: take all remaining tokens starting from the current position
if (parameter.IsSequence)
{
var parameterTokens = arguments.Parameters.Skip(position).ToArray();
-
parameter.Activate(instance, parameterTokens.Select(p => p.Value).ToArray());
position += parameterTokens.Length;
remainingParameterTokens.RemoveRange(parameterTokens);
}
- // Non-sequence: take one input at the current position
+ // Non-sequential: take one token at the current position
else
{
var parameterToken = arguments.Parameters[position];
@@ -132,9 +115,9 @@ public class CommandSchema(
{
throw CliFxException.UserError(
$"""
- Missing equired parameter(s):
+ Missing required parameter(s):
{remainingRequiredParameters
- .Select(p => p.FormattedIdentifier)
+ .Select(p => p.GetFormattedIdentifier())
.JoinToString(" ")}
"""
);
@@ -153,7 +136,7 @@ public class CommandSchema(
foreach (var option in Options)
{
- var optionToken = arguments
+ var optionTokens = arguments
.Options.Where(o => option.MatchesIdentifier(o.Identifier))
.ToArray();
@@ -161,10 +144,10 @@ public class CommandSchema(
option.MatchesEnvironmentVariable(v.Key)
);
- // Direct input
- if (optionToken.Any())
+ // From arguments
+ if (optionTokens.Any())
{
- var rawValues = optionToken.SelectMany(o => o.Values).ToArray();
+ var rawValues = optionTokens.SelectMany(o => o.Values).ToArray();
option.Activate(instance, rawValues);
@@ -172,7 +155,7 @@ public class CommandSchema(
if (rawValues.Any())
remainingRequiredOptions.Remove(option);
}
- // Environment variable
+ // From environment
else if (!string.IsNullOrEmpty(environmentVariable.Value))
{
var rawValues = !option.IsSequence
@@ -194,7 +177,7 @@ public class CommandSchema(
continue;
}
- remainingOptionTokens.RemoveRange(optionToken);
+ remainingOptionTokens.RemoveRange(optionTokens);
}
if (remainingOptionTokens.Any())
@@ -213,7 +196,7 @@ public class CommandSchema(
$"""
Missing required option(s):
{remainingRequiredOptions
- .Select(o => o.FormattedIdentifier)
+ .Select(o => o.GetFormattedIdentifier())
.JoinToString(", ")}
"""
);
diff --git a/CliFx/Schema/PropertyBinding.cs b/CliFx/Schema/PropertyBinding.cs
index 3c9a945..36130a8 100644
--- a/CliFx/Schema/PropertyBinding.cs
+++ b/CliFx/Schema/PropertyBinding.cs
@@ -13,8 +13,8 @@ public class PropertyBinding(
DynamicallyAccessedMemberTypes.Interfaces | DynamicallyAccessedMemberTypes.PublicMethods
)]
Type type,
- Func