ReflectiveArgMapping 2.0.0
See the version list below for details.
dotnet add package ReflectiveArgMapping --version 2.0.0
NuGet\Install-Package ReflectiveArgMapping -Version 2.0.0
<PackageReference Include="ReflectiveArgMapping" Version="2.0.0" />
<PackageVersion Include="ReflectiveArgMapping" Version="2.0.0" />
<PackageReference Include="ReflectiveArgMapping" />
paket add ReflectiveArgMapping --version 2.0.0
#r "nuget: ReflectiveArgMapping, 2.0.0"
#:package ReflectiveArgMapping@2.0.0
#addin nuget:?package=ReflectiveArgMapping&version=2.0.0
#tool nuget:?package=ReflectiveArgMapping&version=2.0.0
Reflective Commandline Argument Mapping
An over-complicated system binding commandline arguments to runtime states.
Features & Motivation
The framework is motivated by the following requirements:
Automatic property binding
Automatically assign values to command handlers, so the framework handles work such as type checking and name mapping. It's implemented by using attributes to map properties to names, then use one of the following ways to get a way to map strings to the value:
- user-provided transform function: add a small lambda explaining how this value should be parsed in the attribute
- IParsable<T> interface: the built-in way of declaring default deserialization logic for data types
State propagation
Subcommands of the same super command should be able to share the logic in initializing and disposing their common super command. For example, a super command may include logic to initiate and terminate connection to a network database. Subcommands of this command, naturally, might have something to do with the database, and it'll make the code a lot simpler if the common super command logic is inherited in the subcommands.
As the above text suggests, this is implemented by making subcommands derived types of their super commands.
Limitations
Current implementation has the following limitations:
Performance: I only wrote this library for IO-bound programs' start up logic. Performance of reflection and frequent unboxing might not suffice on a hot path.
Root command: while it's kind of weird, the concept of "root command" is not really "the common ancestor of every command". It's just a command that's registered when all other commands can't be matched. Further, none of the commands added to the executor are required to have a common root. Each command is treated completely independently, each with its own root. Rather than a limitation this is more like a linguistical quirk: it's actually more work, nothing more secure, and less flexibility to make things otherwise.
Sample usage
private class RootCommand : CommandHandler<int>
{
public override void Initialize()
{
Console.WriteLine($"Initialize state: {State}");
}
public override int Execute()
{
Console.WriteLine($"Hello world from {State}");
return 0;
}
[CommandLineOption("--state")] public string State { get; set; } = string.Empty;
protected override void DisposeCore()
{
Console.WriteLine($"Disposed state: {State}");
}
}
[NamedCommand("yell")]
private class YellCommand : RootCommand
{
[CommandLineOption("--context")]
public string Context { get; set; } = "n/a";
public override int Execute()
{
Console.WriteLine($"({Context}) HELLO!!!!");
return 1;
}
}
[NamedCommand("mult")]
private class YellMultCommand : YellCommand
{
[CommandLineOption("--time")]
public int Time { get; set; } = 1;
public override int Execute()
{
for (int i = 0; i < Time; i++)
{
base.Execute();
}
return 2;
}
}
Assert.That(ReflectiveCommand
.WithRoot<RootCommand, int>()
.WithCommand<YellCommand>()
.WithCommand<YellMultCommand>()
.Execute(["yell", "--context", "in hurry", "--state", "db-connect"]), Is.EqualTo(1));
Assert.That(ReflectiveCommand
.WithRoot<RootCommand, int>()
.WithCommand<YellCommand>()
.WithCommand<YellMultCommand>()
.Execute(["yell", "mult", "--context", "in hurry", "--state", "db-connect", "--time", "10"]), Is.EqualTo(2));
private class RootCommand : AsyncCommandHandler<int>
{
[CommandLineOption("--state")] public string State { get; set; } = string.Empty;
public override Task Initialize()
{
Console.WriteLine($"Initialize state: {State}");
return Task.CompletedTask;
}
public override async Task<int> ExecuteAsync()
{
await Task.Delay(10).ConfigureAwait(false);
Console.WriteLine($"Hello world from {State}");
return 0;
}
protected override ValueTask DisposeAsyncCore()
{
Console.WriteLine($"Dispose state: {State}");
return ValueTask.CompletedTask;
}
}
[NamedCommand("yell")]
private class YellCommand : RootCommand
{
[CommandLineOption("--context")]
public string Context { get; set; } = "n/a";
public override async Task<int> ExecuteAsync()
{
await Task.Delay(10);
Console.WriteLine($"({Context}) HELLO!!!!");
return 1;
}
}
[NamedCommand("mult")]
private class YellMultCommand : YellCommand
{
[CommandLineOption("--time")]
public int Time { get; set; } = 1;
public override async Task<int> ExecuteAsync()
{
await Task.Delay(10);
for (int i = 0; i < Time; i++)
{
await base.ExecuteAsync().ConfigureAwait(false);
}
return 2;
}
}
Assert.That(await ReflectiveCommand
.WithAsyncRoot<RootCommand, int>()
.WithCommand<YellCommand>()
.WithCommand<YellMultCommand>()
.ExecuteAsync(["yell", "--context", "in hurry", "--state", "db-connect"]).ConfigureAwait(false), Is.EqualTo(1));
Assert.That(await ReflectiveCommand
.WithAsyncRoot<RootCommand, int>()
.WithCommand<YellCommand>()
.WithCommand<YellMultCommand>()
.ExecuteAsync(["yell", "mult", "--context", "in hurry", "--state", "db-connect", "--time", "10"]).ConfigureAwait(false), Is.EqualTo(2));
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Added asynchronous support.
Replaced ICommandHandler interface with abstract classes.
Added automatic handling of required vs optional options.