Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public class GreetCommand : ICommand
{
public Task<int> ExecuteAsync(CommandContext context, CancellationToken cancellationToken = default)
{
var name = context.Arguments.GetOptionValue("name") ?? "World";
var name = context.GetOptionValue("name") ?? "World";
Console.WriteLine($"Hello, {name}!");
return Task.FromResult(0);
}
Expand Down Expand Up @@ -221,23 +221,23 @@ Access parsed arguments in your commands:
public Task<int> ExecuteAsync(CommandContext context, CancellationToken cancellationToken)
{
// Check if option exists
bool verbose = context.Arguments.HasOption("verbose") || context.Arguments.HasOption("v");
bool verbose = context.HasOption("verbose") || context.HasOption("v");

// Get option value
string? output = context.Arguments.GetOptionValue("output");
string? output = context.GetOptionValue("output");

// Get typed value
if (context.Arguments.TryGetValue<int>("port", out var port))
if (context.TryGetOption<int>("port", out var port))
{
Console.WriteLine($"Using port: {port}");
}

// Get positional arguments
var file1 = context.Arguments.GetPositional(0);
var file2 = context.Arguments.GetPositional(1);
var file1 = context.GetPositional(0);
var file2 = context.GetPositional(1);

// Get all positional arguments
foreach (var file in context.Arguments.Positional)
foreach (var file in context.Positional)
{
Console.WriteLine($"Processing: {file}");
}
Expand Down
53 changes: 29 additions & 24 deletions docs/TypedArguments.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ public class GreetCommand : ICommand
{
public Task<int> ExecuteAsync(CommandContext context, CancellationToken cancellationToken)
{
// Type-safe access with IntelliSense support
var name = context.Arguments.GetArgument<string>(0, "World");
var greeting = context.Arguments.GetOption<string>("greeting", "Hello");
var formal = context.Arguments.GetOption<bool>("formal");
var repeat = context.Arguments.GetOption<int>("repeat", 1);
// Direct access from context - clean and simple!
var name = context.GetArgument<string>("name", "World");
var greeting = context.GetOption<string>("greeting", "Hello");
var formal = context.GetOption<bool>("formal");
var repeat = context.GetOption<int>("repeat", 1);

var message = formal ? $"Good day, {name}!" : $"{greeting}, {name}!";

Expand Down Expand Up @@ -73,12 +73,12 @@ app build --watch --output ./dist

```csharp
// Get with default value
var port = context.Arguments.GetOption<int>("port", 8080);
var name = context.Arguments.GetOption<string>("name", "default");
var verbose = context.Arguments.GetOption<bool>("verbose"); // defaults to false
var port = context.GetOption<int>("port", 8080);
var name = context.GetOption<string>("name", "default");
var verbose = context.GetOption<bool>("verbose"); // defaults to false

// Try get with error handling
if (context.Arguments.TryGetValue<int>("port", out var port))
if (context.TryGetOption<int>("port", out var port))
{
Console.WriteLine($"Using port: {port}");
}
Expand All @@ -91,12 +91,15 @@ else
### Getting Arguments (Positional)

```csharp
// Get typed positional argument
var count = context.Arguments.GetArgument<int>(0);
var name = context.Arguments.GetArgument<string>(1, "default");
// Get named argument - safer than positional access
var environment = context.GetArgument<string>("environment");
var version = context.GetArgument<string>("version", "latest");

// Still available: Get by position (but not recommended)
var firstArg = context.GetArgument<int>(0);

// With null safety
var value = context.Arguments.GetArgument<double?>(0);
var value = context.GetArgument<double?>("temperature");
if (value.HasValue)
{
Console.WriteLine($"Value: {value.Value}");
Expand All @@ -111,7 +114,7 @@ cli.AddCommand<ProcessCommand>("process")
.AddOption<string>("file", 'f', "Files to process");

// Get all values
var files = context.Arguments.GetOptionValues<string>("file");
var files = context.GetOptionValues<string>("file");
foreach (var file in files)
{
Console.WriteLine($"Processing: {file}");
Expand All @@ -137,9 +140,10 @@ public class ConvertCommand : ICommand
{
public Task<int> ExecuteAsync(CommandContext context, CancellationToken cancellationToken)
{
var value = context.Arguments.GetArgument<double>(0);
var from = context.Arguments.GetOption<string>("from")!.ToUpper();
var to = context.Arguments.GetOption<string>("to")!.ToUpper();
// Access arguments by name - clear and safe!
var value = context.GetArgument<double>("value");
var from = context.GetOption<string>("from")!.ToUpper();
var to = context.GetOption<string>("to")!.ToUpper();

// Conversion logic...
Console.WriteLine($"{value}°{from} = {result:F2}°{to}");
Expand Down Expand Up @@ -169,9 +173,10 @@ public class CalcCommand : ICommand
{
public Task<int> ExecuteAsync(CommandContext context, CancellationToken cancellationToken)
{
var a = context.Arguments.GetArgument<double>(0);
var b = context.Arguments.GetArgument<double>(1);
var op = context.Arguments.GetOption<string>("operation", "+");
// Named arguments make code self-documenting
var a = context.GetArgument<double>("a");
var b = context.GetArgument<double>("b");
var op = context.GetOption<string>("operation", "+");

var result = op switch
{
Expand Down Expand Up @@ -211,17 +216,17 @@ Options:

**Before:**
```csharp
var name = context.Arguments.GetOptionValue("name") ?? "default";
if (int.TryParse(context.Arguments.GetOptionValue("port"), out var port))
var name = context.GetOptionValue("name") ?? "default";
if (int.TryParse(context.GetOptionValue("port"), out var port))
{
// use port
}
```

**After:**
```csharp
var name = context.Arguments.GetOption<string>("name", "default");
var port = context.Arguments.GetOption<int>("port", 8080);
var name = context.GetOption<string>("name", "default");
var port = context.GetOption<int>("port", 8080);
```

## Benefits
Expand Down
31 changes: 17 additions & 14 deletions samples/CliCoreKit.Sample/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,16 @@ public class GreetCommand : ICommand
{
public Task<int> ExecuteAsync(CommandContext context, CancellationToken cancellationToken = default)
{
var name = context.Arguments.GetArgument<string>(0, "World");
var greeting = context.Arguments.GetOption<string>("greeting", "Hello");
var formal = context.Arguments.GetOption<bool>("formal");
var repeat = context.Arguments.GetOption<int>("repeat", 1);
// Clean API - default values come from definition!
var name = context.GetArgument<string>("name");
var greeting = context.GetOption<string>("greeting");
var formal = context.GetOption<bool>("formal");
var repeat = context.GetOption<int?>("repeat");

var message = formal ? $"Good day, {name}!" : $"{greeting}, {name}!";
var message = formal == true ? $"Good day, {name}!" : $"{greeting}, {name}!";

for (int i = 0; i < repeat; i++)
int repeatCount = repeat ?? 1;
for (int i = 0; i < repeatCount; i++)
{
Console.WriteLine(message);
}
Expand All @@ -69,11 +71,12 @@ public class AddCommand : ICommand
{
public Task<int> ExecuteAsync(CommandContext context, CancellationToken cancellationToken = default)
{
var a = context.Arguments.GetArgument<int>(0);
var b = context.Arguments.GetArgument<int>(1);
var verbose = context.Arguments.GetOption<bool>("verbose");
// Access by name - more readable and less error-prone
var a = context.GetArgument<int>("number1");
var b = context.GetArgument<int>("number2");
var verbose = context.GetOption<bool>("verbose");

if (a == 0 && b == 0 && context.Arguments.Positional.Count < 2)
if (a == 0 && b == 0 && context.Positional.Count < 2)
{
Console.Error.WriteLine("Error: Two numbers are required.");
Console.Error.WriteLine("Use 'add --help' for usage information.");
Expand All @@ -100,10 +103,10 @@ public class ListFilesCommand : ICommand
{
public Task<int> ExecuteAsync(CommandContext context, CancellationToken cancellationToken = default)
{
var path = context.Arguments.GetOption<string>("path", ".")!;
var pattern = context.Arguments.GetOption<string>("pattern", "*.*")!;
var invert = context.Arguments.GetOption<bool>("invert");
var recursive = context.Arguments.GetOption<bool>("recursive");
var path = context.GetOption<string>("path") ?? ".";
var pattern = context.GetOption<string>("pattern") ?? "*.*";
var invert = context.GetOption<bool>("invert");
var recursive = context.GetOption<bool>("recursive");

try
{
Expand Down
19 changes: 13 additions & 6 deletions src/CliCoreKit.Core/CliApplication.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
private readonly MiddlewarePipeline _pipeline;
private readonly Func<Type, ICommand>? _commandFactory;

public CliApplication(

Check warning on line 15 in src/CliCoreKit.Core/CliApplication.cs

View workflow job for this annotation

GitHub Actions / build

Missing XML comment for publicly visible type or member 'CliApplication.CliApplication(CommandRegistry, CommandRouter?, MiddlewarePipeline?, Func<Type, ICommand>?)'

Check warning on line 15 in src/CliCoreKit.Core/CliApplication.cs

View workflow job for this annotation

GitHub Actions / build

Missing XML comment for publicly visible type or member 'CliApplication.CliApplication(CommandRegistry, CommandRouter?, MiddlewarePipeline?, Func<Type, ICommand>?)'
CommandRegistry registry,
CommandRouter? router = null,
MiddlewarePipeline? pipeline = null,
Expand Down Expand Up @@ -48,6 +48,13 @@

var parsedArgs = _router.ParseArguments(route.RemainingArgs);

// Map positional arguments to their names based on command definition
var orderedArgs = route.CommandDefinition.Arguments.OrderBy(a => a.Position).ToList();
for (int i = 0; i < Math.Min(orderedArgs.Count, parsedArgs.Positional.Count); i++)
{
parsedArgs.AddNamedArgument(orderedArgs[i].Name, parsedArgs.Positional[i]);
}

// Check if help is requested
var helpRequested = parsedArgs.HasOption("help") || parsedArgs.HasOption("h");

Expand All @@ -58,12 +65,12 @@
return 0;
}

var context = new CommandContext
{
Arguments = parsedArgs,
RawArgs = args,
CommandName = string.Join(" ", route.CommandPath)
};
var context = new CommandContext(
parsedArgs,
args,
string.Join(" ", route.CommandPath),
route.CommandDefinition
);

// Add command definition to context for middleware
context.Data["CommandDefinition"] = route.CommandDefinition;
Expand Down
2 changes: 1 addition & 1 deletion src/CliCoreKit.Core/CliCoreKit.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

<!-- Package Information -->
<PackageId>Monbsoft.CliCoreKit.Core</PackageId>
<Version>1.1.0</Version>
<Version>1.2.0</Version>
<Authors>Monbsoft</Authors>
<Company>Monbsoft</Company>
<Product>CliCoreKit</Product>
Expand Down
Loading
Loading