Skip to content

Commit

Permalink
Expose raw arguments on the command context
Browse files Browse the repository at this point in the history
  • Loading branch information
patriksvensson committed Apr 20, 2024
1 parent de04619 commit 95bff47
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 11 deletions.
13 changes: 12 additions & 1 deletion src/Spectre.Console.Cli/CommandContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ public sealed class CommandContext
/// </value>
public IRemainingArguments Remaining { get; }

/// <summary>
/// Gets all the arguments that were passed to the applicaton.
/// </summary>
public IReadOnlyList<string> Arguments { get; }

/// <summary>
/// Gets the name of the command.
/// </summary>
Expand All @@ -32,11 +37,17 @@ public sealed class CommandContext
/// <summary>
/// Initializes a new instance of the <see cref="CommandContext"/> class.
/// </summary>
/// <param name="arguments">All arguments that were passed to the application.</param>
/// <param name="remaining">The remaining arguments.</param>
/// <param name="name">The command name.</param>
/// <param name="data">The command data.</param>
public CommandContext(IRemainingArguments remaining, string name, object? data)
public CommandContext(
IEnumerable<string> arguments,
IRemainingArguments remaining,
string name,
object? data)
{
Arguments = arguments.ToSafeReadOnlyList();
Remaining = remaining ?? throw new System.ArgumentNullException(nameof(remaining));
Name = name ?? throw new System.ArgumentNullException(nameof(name));
Data = data;
Expand Down
1 change: 1 addition & 0 deletions src/Spectre.Console.Cli/IRemainingArguments.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public interface IRemainingArguments

/// <summary>
/// Gets the raw, non-parsed remaining arguments.
/// This is normally everything after the `--` delimiter.
/// </summary>
IReadOnlyList<string> Raw { get; }
}
20 changes: 11 additions & 9 deletions src/Spectre.Console.Cli/Internal/CommandExecutor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public async Task<int> Execute(IConfiguration configuration, IEnumerable<string>
throw new ArgumentNullException(nameof(configuration));
}

args ??= new List<string>();
var arguments = args.ToSafeReadOnlyList();

_registrar.RegisterInstance(typeof(IConfiguration), configuration);
_registrar.RegisterLazy(typeof(IAnsiConsole), () => configuration.Settings.Console.GetConsole());
Expand All @@ -31,7 +31,7 @@ public async Task<int> Execute(IConfiguration configuration, IEnumerable<string>
if (model.DefaultCommand == null)
{
// Got at least one argument?
var firstArgument = args.FirstOrDefault();
var firstArgument = arguments.FirstOrDefault();
if (firstArgument != null)
{
// Asking for version? Kind of a hack, but it's alright.
Expand All @@ -47,7 +47,7 @@ public async Task<int> Execute(IConfiguration configuration, IEnumerable<string>
}

// Parse and map the model against the arguments.
var parsedResult = ParseCommandLineArguments(model, configuration.Settings, args);
var parsedResult = ParseCommandLineArguments(model, configuration.Settings, arguments);

// Register the arguments with the container.
_registrar.RegisterInstance(typeof(CommandTreeParserResult), parsedResult);
Expand Down Expand Up @@ -79,31 +79,34 @@ public async Task<int> Execute(IConfiguration configuration, IEnumerable<string>
}

// Is this the default and is it called without arguments when there are required arguments?
if (leaf.Command.IsDefaultCommand && args.Count() == 0 && leaf.Command.Parameters.Any(p => p.Required))
if (leaf.Command.IsDefaultCommand && arguments.Count == 0 && leaf.Command.Parameters.Any(p => p.Required))
{
// Display help for default command.
configuration.Settings.Console.SafeRender(helpProvider.Write(model, leaf.Command));
return 1;
}

// Create the content.
var context = new CommandContext(parsedResult.Remaining, leaf.Command.Name, leaf.Command.Data);
var context = new CommandContext(
arguments,
parsedResult.Remaining,
leaf.Command.Name,
leaf.Command.Data);

// Execute the command tree.
return await Execute(leaf, parsedResult.Tree, context, resolver, configuration).ConfigureAwait(false);
}
}

#pragma warning disable CS8603 // Possible null reference return.
private CommandTreeParserResult ParseCommandLineArguments(CommandModel model, CommandAppSettings settings, IEnumerable<string> args)
private CommandTreeParserResult ParseCommandLineArguments(CommandModel model, CommandAppSettings settings, IReadOnlyList<string> args)
{
var parser = new CommandTreeParser(model, settings.CaseSensitivity, settings.ParsingMode, settings.ConvertFlagsToRemainingArguments);

var parserContext = new CommandTreeParserContext(args, settings.ParsingMode);
var tokenizerResult = CommandTreeTokenizer.Tokenize(args);
var parsedResult = parser.Parse(parserContext, tokenizerResult);

var lastParsedLeaf = parsedResult?.Tree?.GetLeafCommand();
var lastParsedLeaf = parsedResult.Tree?.GetLeafCommand();
var lastParsedCommand = lastParsedLeaf?.Command;
if (lastParsedLeaf != null && lastParsedCommand != null &&
lastParsedCommand.IsBranch && !lastParsedLeaf.ShowHelp &&
Expand All @@ -122,7 +125,6 @@ private CommandTreeParserResult ParseCommandLineArguments(CommandModel model, Co

return parsedResult;
}
#pragma warning restore CS8603 // Possible null reference return.

private static string ResolveApplicationVersion(IConfiguration configuration)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace Spectre.Console.Cli;

internal static class EnumerableExtensions
{
public static IReadOnlyList<T> ToSafeReadOnlyList<T>(this IEnumerable<T> source)
{
return source switch
{
null => new List<T>(),
IReadOnlyList<T> list => list,
_ => source.ToList(),
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.3" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.4" />
</ItemGroup>

<ItemGroup>
Expand Down
23 changes: 23 additions & 0 deletions test/Spectre.Console.Cli.Tests/Unit/CommandAppTests.Context.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
namespace Spectre.Console.Tests.Unit.Cli;

public sealed partial class CommandAppTests
{
[Fact]
[Expectation("Should_Expose_Raw_Arguments")]
public void Should_Return_Correct_Text_When_Command_Is_Unknown()
{
// Given
var app = new CommandAppTester();
app.Configure(config =>
{
config.AddCommand<EmptyCommand>("test");
});

// When
var result = app.Run("test", "--foo", "32", "--lol");

// Then
result.Context.ShouldNotBeNull();
result.Context.Arguments.ShouldBe(new[] { "test", "--foo", "32", "--lol" });
}
}

0 comments on commit 95bff47

Please sign in to comment.