Skip to content

Commit d96f12e

Browse files
authored
Some small enhancements to cli.hosting handling command and options validation results (#66)
* Add static factory methods and operator overloads to `CommandResult` and `OptionsValidationResult` - Introduce `Success` property and implicit conversion operator in `CommandResult`. - Add static `Valid` and `Invalid` methods in `OptionsValidationResult` for easier initialization. - Create comprehensive unit tests to validate the new changes. * Document CLI-related classes with XML comments for improved clarity and usability - Add comprehensive XML documentation for `CommandResult`, `OptionsValidationResult`, `ICliHost`, `ICliCommandContext`, and `ICliHostBuilder`. - Update affected files to align with enhanced documentation standards.
1 parent 04e2770 commit d96f12e

7 files changed

Lines changed: 199 additions & 0 deletions

File tree

source/Cli/CreativeCoders.Cli.Core/CommandResult.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,17 @@
22

33
namespace CreativeCoders.Cli.Core;
44

5+
/// <summary>
6+
/// Represents the result of executing a command.
7+
/// </summary>
58
[PublicAPI]
69
public class CommandResult
710
{
11+
/// <summary>
12+
/// Represents a successful result of a command execution with an implicit default exit code of 0.
13+
/// </summary>
14+
public static CommandResult Success { get; } = new CommandResult();
15+
816
public CommandResult() { }
917

1018
public CommandResult(int exitCode)
@@ -13,4 +21,9 @@ public CommandResult(int exitCode)
1321
}
1422

1523
public int ExitCode { get; init; }
24+
25+
public static implicit operator CommandResult(int exitCode)
26+
=> exitCode == 0
27+
? Success
28+
: new CommandResult(exitCode);
1629
}

source/Cli/CreativeCoders.Cli.Core/ICliCommandContext.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,21 @@
22

33
namespace CreativeCoders.Cli.Core;
44

5+
/// <summary>
6+
/// Represents the context for a CLI command, providing access to arguments passed to the command.
7+
/// </summary>
58
[PublicAPI]
69
public interface ICliCommandContext
710
{
11+
/// <summary>
12+
/// Gets or sets all arguments passed to the CLI app, including commands, options and positional arguments.
13+
/// This property provides full visibility of the input arguments passed to the CLI app for further processing.
14+
/// </summary>
815
public string[] AllArgs { get; set; }
916

17+
/// <summary>
18+
/// Gets or sets the arguments that represent options passed to the CLI command.
19+
/// This property contains only the arguments classified as options and excludes CLI inputs such as commands.
20+
/// </summary>
1021
public string[] OptionsArgs { get; set; }
1122
}

source/Cli/CreativeCoders.Cli.Core/OptionsValidationResult.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,27 @@ public class OptionsValidationResult(bool isValid, IEnumerable<string>? messages
1616
/// Gets the validation messages.
1717
/// </summary>
1818
public IEnumerable<string> Messages { get; } = messages ?? [];
19+
20+
/// <summary>
21+
/// Creates a predefined valid result for options validation where
22+
/// the validation is successful without any messages.
23+
/// </summary>
24+
/// <returns>
25+
/// An <see cref="OptionsValidationResult"/> instance representing a successful validation result
26+
/// with an empty collection of messages.
27+
/// </returns>
28+
public static OptionsValidationResult Valid() => new OptionsValidationResult(true);
29+
30+
/// <summary>
31+
/// Creates a predefined invalid result for options validation where
32+
/// the validation fails and may include associated validation messages.
33+
/// </summary>
34+
/// <param name="messages">An optional collection of validation messages explaining the failure.
35+
/// If null, the result will contain an empty collection of messages.</param>
36+
/// <returns>
37+
/// An <see cref="OptionsValidationResult"/> instance representing an unsuccessful validation result
38+
/// with the provided validation messages or an empty collection if none are specified.
39+
/// </returns>
40+
public static OptionsValidationResult Invalid(IEnumerable<string>? messages)
41+
=> new OptionsValidationResult(false, messages);
1942
}

source/Cli/CreativeCoders.Cli.Hosting/ICliHost.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,16 @@
11
namespace CreativeCoders.Cli.Hosting;
22

3+
/// <summary>
4+
/// Represents a Command Line Interface (CLI) host that provides functionality to execute
5+
/// commands and handle related tasks.
6+
/// </summary>
37
public interface ICliHost
48
{
9+
/// <summary>
10+
/// Executes the CLI host with the given arguments and returns the result of the operation.
11+
/// </summary>
12+
/// <param name="args">The command line arguments provided to the host.</param>
13+
/// <returns>A <see cref="CliResult"/> object containing the exit code of the executed command.</returns>
514
Task<CliResult> RunAsync(string[] args);
615

716
/// <summary>

source/Cli/CreativeCoders.Cli.Hosting/ICliHostBuilder.cs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,54 @@
66

77
namespace CreativeCoders.Cli.Hosting;
88

9+
/// <summary>
10+
/// Defines a builder for configuring and creating an instance of <see cref="ICliHost"/>.
11+
/// Provides methods to set up services, command context, assembly scanning, validation, and help functionality
12+
/// for a Command Line Interface (CLI) application.
13+
/// </summary>
914
[PublicAPI]
1015
public interface ICliHostBuilder
1116
{
17+
/// <summary>
18+
/// Configures a custom command context for the CLI application.
19+
/// </summary>
20+
/// <typeparam name="TContext">The type of the context to be used. Must implement <see cref="ICliCommandContext"/>.</typeparam>
21+
/// <param name="configure">
22+
/// An optional action to configure the context instance. The action receives the service provider
23+
/// and the instance of <typeparamref name="TContext"/> as parameters.
24+
/// </param>
25+
/// <returns>The same <see cref="ICliHostBuilder"/> instance.</returns>
26+
/// <exception cref="InvalidOperationException">Thrown if the context is already configured.</exception>
1227
ICliHostBuilder UseContext<TContext>(Action<IServiceProvider, TContext>? configure = null)
1328
where TContext : class, ICliCommandContext;
1429

30+
/// <summary>
31+
/// Configures additional services for the CLI application.
32+
/// </summary>
33+
/// <param name="configureServices">
34+
/// An action that receives an <see cref="IServiceCollection"/> to add or modify services for the application.
35+
/// </param>
36+
/// <returns>The same <see cref="ICliHostBuilder"/> instance, enabling method chaining.</returns>
37+
/// <exception cref="ArgumentNullException">Thrown if <paramref name="configureServices"/> is null.</exception>
1538
ICliHostBuilder ConfigureServices(Action<IServiceCollection> configureServices);
1639

40+
/// <summary>
41+
/// Registers specified assemblies for command scanning within the CLI application.
42+
/// </summary>
43+
/// <param name="assemblies">
44+
/// An array of <see cref="Assembly"/> instances to be scanned for commands.
45+
/// </param>
46+
/// <returns>The same <see cref="ICliHostBuilder"/> instance.</returns>
1747
ICliHostBuilder ScanAssemblies(params Assembly[] assemblies);
1848

49+
/// <summary>
50+
/// Enables help functionality for the CLI application by specifying the type of help commands to be supported.
51+
/// </summary>
52+
/// <param name="commandKind">
53+
/// Defines the type of help commands that can be used within the application.
54+
/// This can be a command-specific help, argument-specific help, or both, as specified by the values in <see cref="HelpCommandKind"/>.
55+
/// </param>
56+
/// <returns>The same <see cref="ICliHostBuilder"/> instance to allow for method chaining.</returns>
1957
ICliHostBuilder EnableHelp(HelpCommandKind commandKind);
2058

2159
/// <summary>
@@ -25,7 +63,21 @@ ICliHostBuilder UseContext<TContext>(Action<IServiceProvider, TContext>? configu
2563
/// <returns>The same <see cref="ICliHostBuilder"/> instance.</returns>
2664
ICliHostBuilder UseValidation(bool useValidation = true);
2765

66+
/// <summary>
67+
/// Specifies whether the entry assembly should be excluded from the scanning process
68+
/// when configuring the CLI application.
69+
/// </summary>
70+
/// <param name="skipScanEntryAssembly">
71+
/// A boolean value indicating whether to skip scanning the entry assembly. Defaults to <c>true</c>.
72+
/// If set to <c>false</c>, the entry assembly will be included in the assembly scanning process.
73+
/// </param>
74+
/// <returns>The same <see cref="ICliHostBuilder"/> instance.</returns>
2875
ICliHostBuilder SkipScanEntryAssembly(bool skipScanEntryAssembly = true);
2976

77+
/// <summary>
78+
/// Builds and creates an instance of <see cref="ICliHost"/> configured through the current builder.
79+
/// </summary>
80+
/// <returns>An instance of <see cref="ICliHost"/> that represents the configured Command Line Interface (CLI) application.</returns>
81+
/// <exception cref="InvalidOperationException">Thrown if the build process encounters an invalid state or configuration.</exception>
3082
ICliHost Build();
3183
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using AwesomeAssertions;
2+
using CreativeCoders.Cli.Core;
3+
using Xunit;
4+
5+
namespace CreativeCoders.Cli.Tests.Core;
6+
7+
public class CommandResultTests
8+
{
9+
[Theory]
10+
[InlineData(0)]
11+
[InlineData(1)]
12+
[InlineData(-1)]
13+
[InlineData(int.MaxValue)]
14+
[InlineData(int.MinValue)]
15+
public void ImplicitOperator_FromInt_CreatesCommandResultWithExitCode(int exitCode)
16+
{
17+
// Act
18+
var result = (CommandResult)exitCode;
19+
20+
// Assert
21+
result.ExitCode
22+
.Should().Be(exitCode);
23+
}
24+
25+
[Fact]
26+
public void Success_ReturnsResultWithExitCodeZero()
27+
{
28+
// Act
29+
var result = CommandResult.Success;
30+
31+
// Assert
32+
result.ExitCode
33+
.Should().Be(0);
34+
}
35+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
using AwesomeAssertions;
2+
using CreativeCoders.Cli.Core;
3+
using Xunit;
4+
5+
namespace CreativeCoders.Cli.Tests.Core;
6+
7+
public class OptionsValidationResultTests
8+
{
9+
[Fact]
10+
public void StaticValid_Call_ReturnsValidResult()
11+
{
12+
// Act
13+
var result = OptionsValidationResult.Valid();
14+
15+
// Assert
16+
result.IsValid
17+
.Should().BeTrue();
18+
19+
result.Messages
20+
.Should().BeEmpty();
21+
}
22+
23+
[Fact]
24+
public void StaticInvalid_Call_ReturnsInvalidResultWithMessages()
25+
{
26+
// Arrange
27+
const string message0 = "Test";
28+
const string message1 = "qwertz2";
29+
30+
var messages = new[] { message0, message1 };
31+
32+
// Act
33+
var result = OptionsValidationResult.Invalid(messages);
34+
35+
// Assert
36+
result.IsValid
37+
.Should().BeFalse();
38+
39+
result.Messages
40+
.Should().BeEquivalentTo(message0, message1);
41+
}
42+
43+
[Fact]
44+
public void StaticInvalid_CallWithMessagesNull_ReturnsInvalidResultWithEmptyMessages()
45+
{
46+
// Act
47+
var result = OptionsValidationResult.Invalid(null);
48+
49+
// Assert
50+
result.IsValid
51+
.Should().BeFalse();
52+
53+
result.Messages
54+
.Should().BeEmpty();
55+
}
56+
}

0 commit comments

Comments
 (0)