Skip to content

Commit

Permalink
Add possibility to set description and/or data for the default command (
Browse files Browse the repository at this point in the history
  • Loading branch information
0xced authored Jan 12, 2023
1 parent f223f60 commit 7b9553d
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 31 deletions.
7 changes: 5 additions & 2 deletions src/Spectre.Console.Cli/CommandApp.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using Spectre.Console.Cli.Internal.Configuration;

namespace Spectre.Console.Cli;

/// <summary>
Expand Down Expand Up @@ -39,10 +41,11 @@ public void Configure(Action<IConfigurator> configuration)
/// Sets the default command.
/// </summary>
/// <typeparam name="TCommand">The command type.</typeparam>
public void SetDefaultCommand<TCommand>()
/// <returns>A <see cref="DefaultCommandConfigurator"/> that can be used to configure the default command.</returns>
public DefaultCommandConfigurator SetDefaultCommand<TCommand>()
where TCommand : class, ICommand
{
GetConfigurator().SetDefaultCommand<TCommand>();
return new DefaultCommandConfigurator(GetConfigurator().SetDefaultCommand<TCommand>());
}

/// <summary>
Expand Down
32 changes: 31 additions & 1 deletion src/Spectre.Console.Cli/CommandAppOfT.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using Spectre.Console.Cli.Internal.Configuration;

namespace Spectre.Console.Cli;

/// <summary>
Expand All @@ -8,6 +10,7 @@ public sealed class CommandApp<TDefaultCommand> : ICommandApp
where TDefaultCommand : class, ICommand
{
private readonly CommandApp _app;
private readonly DefaultCommandConfigurator _defaultCommandConfigurator;

/// <summary>
/// Initializes a new instance of the <see cref="CommandApp{TDefaultCommand}"/> class.
Expand All @@ -16,7 +19,7 @@ public sealed class CommandApp<TDefaultCommand> : ICommandApp
public CommandApp(ITypeRegistrar? registrar = null)
{
_app = new CommandApp(registrar);
_app.GetConfigurator().SetDefaultCommand<TDefaultCommand>();
_defaultCommandConfigurator = _app.SetDefaultCommand<TDefaultCommand>();
}

/// <summary>
Expand Down Expand Up @@ -46,5 +49,32 @@ public int Run(IEnumerable<string> args)
public Task<int> RunAsync(IEnumerable<string> args)
{
return _app.RunAsync(args);
}

internal Configurator GetConfigurator()
{
return _app.GetConfigurator();
}

/// <summary>
/// Sets the description of the default command.
/// </summary>
/// <param name="description">The default command description.</param>
/// <returns>The same <see cref="CommandApp{TDefaultCommand}"/> instance so that multiple calls can be chained.</returns>
public CommandApp<TDefaultCommand> WithDescription(string description)
{
_defaultCommandConfigurator.WithDescription(description);
return this;
}

/// <summary>
/// Sets data that will be passed to the command via the <see cref="CommandContext"/>.
/// </summary>
/// <param name="data">The data to pass to the default command.</param>
/// <returns>The same <see cref="CommandApp{TDefaultCommand}"/> instance so that multiple calls can be chained.</returns>
public CommandApp<TDefaultCommand> WithData(object data)
{
_defaultCommandConfigurator.WithData(data);
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,12 @@ public void AddExample(string[] args)
Examples.Add(args);
}

public void SetDefaultCommand<TDefaultCommand>()
public ConfiguredCommand SetDefaultCommand<TDefaultCommand>()
where TDefaultCommand : class, ICommand
{
DefaultCommand = ConfiguredCommand.FromType<TDefaultCommand>(
CliConstants.DefaultCommandName, isDefaultCommand: true);
return DefaultCommand;
}

public ICommandConfigurator AddCommand<TCommand>(string name)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
namespace Spectre.Console.Cli.Internal.Configuration;

/// <summary>
/// Fluent configurator for the default command.
/// </summary>
public sealed class DefaultCommandConfigurator
{
private readonly ConfiguredCommand _defaultCommand;

internal DefaultCommandConfigurator(ConfiguredCommand defaultCommand)
{
_defaultCommand = defaultCommand;
}

/// <summary>
/// Sets the description of the default command.
/// </summary>
/// <param name="description">The default command description.</param>
/// <returns>The same <see cref="DefaultCommandConfigurator"/> instance so that multiple calls can be chained.</returns>
public DefaultCommandConfigurator WithDescription(string description)
{
_defaultCommand.Description = description;
return this;
}

/// <summary>
/// Sets data that will be passed to the command via the <see cref="CommandContext"/>.
/// </summary>
/// <param name="data">The data to pass to the default command.</param>
/// <returns>The same <see cref="DefaultCommandConfigurator"/> instance so that multiple calls can be chained.</returns>
public DefaultCommandConfigurator WithData(object data)
{
_defaultCommand.Data = data;
return this;
}
}
18 changes: 16 additions & 2 deletions src/Spectre.Console.Testing/Cli/CommandAppTester.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,25 @@ public CommandAppTester(ITypeRegistrar? registrar = null)
/// <summary>
/// Sets the default command.
/// </summary>
/// <param name="description">The optional default command description.</param>
/// <param name="data">The optional default command data.</param>
/// <typeparam name="T">The default command type.</typeparam>
public void SetDefaultCommand<T>()
public void SetDefaultCommand<T>(string? description = null, object? data = null)
where T : class, ICommand
{
_appConfiguration = (app) => app.SetDefaultCommand<T>();
_appConfiguration = (app) =>
{
var defaultCommandBuilder = app.SetDefaultCommand<T>();
if (description != null)
{
defaultCommandBuilder.WithDescription(description);
}

if (data != null)
{
defaultCommandBuilder.WithData(data);
}
};
}

/// <summary>
Expand Down
86 changes: 61 additions & 25 deletions test/Spectre.Console.Cli.Tests/Unit/CommandAppTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -845,30 +845,6 @@ public void Should_Add_Unknown_Boolean_Option_To_Remaining_Arguments_In_Relaxed_
result.Context.ShouldHaveRemainingArgument("foo", values: new[] { (string)null });
}

[Fact]
public void Should_Be_Able_To_Set_The_Default_Command()
{
// Given
var app = new CommandAppTester();
app.SetDefaultCommand<DogCommand>();

// When
var result = app.Run(new[]
{
"4", "12", "--good-boy", "--name", "Rufus",
});

// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<DogSettings>().And(dog =>
{
dog.Legs.ShouldBe(4);
dog.Age.ShouldBe(12);
dog.GoodBoy.ShouldBe(true);
dog.Name.ShouldBe("Rufus");
});
}

[Fact]
public void Should_Set_Command_Name_In_Context()
{
Expand Down Expand Up @@ -917,6 +893,66 @@ public void Should_Pass_Command_Data_In_Context()
// Then
result.Context.ShouldNotBeNull();
result.Context.Data.ShouldBe(123);
}

public sealed class Default_Command
{
[Fact]
public void Should_Be_Able_To_Set_The_Default_Command()
{
// Given
var app = new CommandAppTester();
app.SetDefaultCommand<DogCommand>();

// When
var result = app.Run(new[]
{
"4", "12", "--good-boy", "--name", "Rufus",
});

// Then
result.ExitCode.ShouldBe(0);
result.Settings.ShouldBeOfType<DogSettings>().And(dog =>
{
dog.Legs.ShouldBe(4);
dog.Age.ShouldBe(12);
dog.GoodBoy.ShouldBe(true);
dog.Name.ShouldBe("Rufus");
});
}

[Fact]
public void Should_Set_The_Default_Command_Description_Data_CommandApp()
{
// Given
var app = new CommandApp();
app.SetDefaultCommand<DogCommand>()
.WithDescription("The default command")
.WithData(new string[] { "foo", "bar" });

// When

// Then
app.GetConfigurator().DefaultCommand.ShouldNotBeNull();
app.GetConfigurator().DefaultCommand.Description.ShouldBe("The default command");
app.GetConfigurator().DefaultCommand.Data.ShouldBe(new string[] { "foo", "bar" });
}

[Fact]
public void Should_Set_The_Default_Command_Description_Data_CommandAppOfT()
{
// Given
var app = new CommandApp<DogCommand>()
.WithDescription("The default command")
.WithData(new string[] { "foo", "bar" });

// When

// Then
app.GetConfigurator().DefaultCommand.ShouldNotBeNull();
app.GetConfigurator().DefaultCommand.Description.ShouldBe("The default command");
app.GetConfigurator().DefaultCommand.Data.ShouldBe(new string[] { "foo", "bar" });
}
}

public sealed class Delegate_Commands
Expand All @@ -930,7 +966,7 @@ public void Should_Execute_Delegate_Command_At_Root_Level()

var app = new CommandApp();
app.Configure(config =>
{
{
config.PropagateExceptions();
config.AddDelegate<DogSettings>(
"foo", (context, settings) =>
Expand Down

0 comments on commit 7b9553d

Please sign in to comment.