Skip to content

Commit 6b9b8c8

Browse files
authored
Move directives to CliRootCommand (#2224)
* some test refactoring * move directives from CliConfiguration to CliRootCommand * skip two tests added to track #2223 * remove a FIX comment
1 parent 8f2ada7 commit 6b9b8c8

29 files changed

+482
-451
lines changed

src/System.CommandLine.ApiCompatibility.Tests/ApiCompatibilityApprovalTests.System_CommandLine_Hosting_api_is_not_changed.approved.txt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
System.CommandLine.Hosting
2-
public static class DirectiveConfigurationExtensions
3-
public static Microsoft.Extensions.Configuration.IConfigurationBuilder AddCommandLineDirectives(this Microsoft.Extensions.Configuration.IConfigurationBuilder config, System.CommandLine.ParseResult commandline, System.CommandLine.CliDirective directive)
42
public static class HostingExtensions
53
public static OptionsBuilder<TOptions> BindCommandLine<TOptions>(this OptionsBuilder<TOptions> optionsBuilder)
64
public static Microsoft.Extensions.Hosting.IHost GetHost(this System.CommandLine.ParseResult parseResult)
75
public static System.CommandLine.ParseResult GetParseResult(this Microsoft.Extensions.Hosting.IHostBuilder hostBuilder)
86
public static System.CommandLine.ParseResult GetParseResult(this Microsoft.Extensions.Hosting.HostBuilderContext context)
97
public static System.CommandLine.CliCommand UseCommandHandler<THandler>(this System.CommandLine.CliCommand command)
10-
public static System.CommandLine.CliConfiguration UseHost(this System.CommandLine.CliConfiguration builder, System.Action<Microsoft.Extensions.Hosting.IHostBuilder> configureHost = null)
11-
public static System.CommandLine.CliConfiguration UseHost(this System.CommandLine.CliConfiguration builder, System.Func<System.String[],Microsoft.Extensions.Hosting.IHostBuilder> hostBuilderFactory, System.Action<Microsoft.Extensions.Hosting.IHostBuilder> configureHost = null)
8+
public static System.CommandLine.CliConfiguration UseHost(this System.CommandLine.CliConfiguration config, System.Action<Microsoft.Extensions.Hosting.IHostBuilder> configureHost = null)
9+
public static System.CommandLine.CliConfiguration UseHost(this System.CommandLine.CliConfiguration config, System.Func<System.String[],Microsoft.Extensions.Hosting.IHostBuilder> hostBuilderFactory, System.Action<Microsoft.Extensions.Hosting.IHostBuilder> configureHost = null)
1210
public static Microsoft.Extensions.Hosting.IHostBuilder UseInvocationLifetime(this Microsoft.Extensions.Hosting.IHostBuilder host, System.Action<InvocationLifetimeOptions> configureOptions = null)
1311
public class InvocationLifetime, Microsoft.Extensions.Hosting.IHostLifetime
1412
.ctor(Microsoft.Extensions.Options.IOptions<InvocationLifetimeOptions> options, Microsoft.Extensions.Hosting.IHostEnvironment environment, Microsoft.Extensions.Hosting.IHostApplicationLifetime applicationLifetime, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory = null)

src/System.CommandLine.ApiCompatibility.Tests/ApiCompatibilityApprovalTests.System_CommandLine_api_is_not_changed.approved.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ System.CommandLine
5858
public System.Void SetAction(System.Func<ParseResult,System.Threading.CancellationToken,System.Threading.Tasks.Task<System.Int32>> action)
5959
public class CliConfiguration
6060
.ctor(CliCommand rootCommand)
61-
public System.Collections.Generic.List<CliDirective> Directives { get; }
6261
public System.Boolean EnableDefaultExceptionHandler { get; set; }
6362
public System.Boolean EnablePosixBundling { get; set; }
6463
public System.IO.TextWriter Error { get; set; }
@@ -104,6 +103,8 @@ System.CommandLine
104103
public static System.String ExecutableName { get; }
105104
public static System.String ExecutablePath { get; }
106105
.ctor(System.String description = )
106+
public System.Collections.Generic.IList<CliDirective> Directives { get; }
107+
public System.Void Add(CliDirective directive)
107108
public abstract class CliSymbol
108109
public System.String Description { get; set; }
109110
public System.Boolean Hidden { get; set; }

src/System.CommandLine.Hosting.Tests/HostingHandlerTest.cs

Lines changed: 41 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
using Microsoft.Extensions.Hosting;
77
using Xunit;
88

9-
109
namespace System.CommandLine.Hosting.Tests
1110
{
1211
public static class HostingHandlerTest
@@ -17,24 +16,24 @@ public static async Task Constructor_Injection_Injects_Service()
1716
var service = new MyService();
1817

1918
var config = new CliConfiguration(
20-
new MyCommand().UseCommandHandler<MyCommand.MyHandler>()
19+
new MyRootCommand().UseCommandHandler<MyHandler>()
2120
)
22-
.UseHost((builder) => {
21+
.UseHost(builder => {
2322
builder.ConfigureServices(services =>
2423
{
25-
services.AddTransient(x => service);
24+
services.AddTransient(_ => service);
2625
});
2726
});
2827

29-
var result = await config.InvokeAsync(new string[] { "--int-option", "54"});
28+
await config.InvokeAsync(new [] { "--int-option", "54"});
3029

3130
service.Value.Should().Be(54);
3231
}
3332

3433
[Fact]
3534
public static async Task Parameter_is_available_in_property()
3635
{
37-
var config = new CliConfiguration(new MyCommand().UseCommandHandler<MyCommand.MyHandler>())
36+
var config = new CliConfiguration(new MyRootCommand().UseCommandHandler<MyHandler>())
3837
.UseHost(host =>
3938
{
4039
host.ConfigureServices(services =>
@@ -43,7 +42,7 @@ public static async Task Parameter_is_available_in_property()
4342
});
4443
});
4544

46-
var result = await config.InvokeAsync(new string[] { "--int-option", "54"});
45+
var result = await config.InvokeAsync(new [] { "--int-option", "54"});
4746

4847
result.Should().Be(54);
4948
}
@@ -53,7 +52,7 @@ public static async Task Can_have_different_handlers_based_on_command()
5352
{
5453
var root = new CliRootCommand();
5554

56-
root.Subcommands.Add(new MyCommand().UseCommandHandler<MyCommand.MyHandler>());
55+
root.Subcommands.Add(new MyCommand().UseCommandHandler<MyHandler>());
5756
root.Subcommands.Add(new MyOtherCommand().UseCommandHandler<MyOtherCommand.MyHandler>());
5857
var config = new CliConfiguration(root)
5958
.UseHost(host =>
@@ -102,7 +101,7 @@ public static async Task Invokes_DerivedClass()
102101
var service = new MyService();
103102

104103
var cmd = new CliRootCommand();
105-
cmd.Subcommands.Add(new MyCommand().UseCommandHandler<MyCommand.MyDerivedCliAction>());
104+
cmd.Subcommands.Add(new MyCommand().UseCommandHandler<MyDerivedCliAction>());
106105
cmd.Subcommands.Add(new MyOtherCommand().UseCommandHandler<MyOtherCommand.MyDerivedCliAction>());
107106
var config = new CliConfiguration(cmd)
108107
.UseHost((builder) => {
@@ -131,45 +130,53 @@ public override Task<int> InvokeAsync(ParseResult context, CancellationToken can
131130
protected abstract int Act();
132131
}
133132

133+
public class MyRootCommand : CliRootCommand
134+
{
135+
public MyRootCommand()
136+
{
137+
Options.Add(new CliOption<int>("--int-option")); // or nameof(Handler.IntOption).ToKebabCase() if you don't like the string literal
138+
}
139+
}
140+
134141
public class MyCommand : CliCommand
135142
{
136143
public MyCommand() : base(name: "mycommand")
137144
{
138145
Options.Add(new CliOption<int>("--int-option")); // or nameof(Handler.IntOption).ToKebabCase() if you don't like the string literal
139146
}
147+
}
140148

141-
public class MyHandler : AsynchronousCliAction
142-
{
143-
private readonly MyService service;
149+
public class MyDerivedCliAction : MyBaseCliAction
150+
{
151+
private readonly MyService service;
144152

145-
public MyHandler(MyService service)
146-
{
147-
this.service = service;
148-
}
153+
public MyDerivedCliAction(MyService service)
154+
{
155+
this.service = service;
156+
}
149157

150-
public int IntOption { get; set; } // bound from option
151-
152-
public override Task<int> InvokeAsync(ParseResult context, CancellationToken cancellationToken)
153-
{
154-
service.Value = IntOption;
155-
return Task.FromResult(IntOption);
156-
}
158+
protected override int Act()
159+
{
160+
service.Value = IntOption;
161+
return IntOption;
157162
}
163+
}
158164

159-
public class MyDerivedCliAction : MyBaseCliAction
165+
public class MyHandler : AsynchronousCliAction
166+
{
167+
private readonly MyService service;
168+
169+
public MyHandler(MyService service)
160170
{
161-
private readonly MyService service;
171+
this.service = service;
172+
}
162173

163-
public MyDerivedCliAction(MyService service)
164-
{
165-
this.service = service;
166-
}
174+
public int IntOption { get; set; } // bound from option
167175

168-
protected override int Act()
169-
{
170-
service.Value = IntOption;
171-
return IntOption;
172-
}
176+
public override Task<int> InvokeAsync(ParseResult context, CancellationToken cancellationToken)
177+
{
178+
service.Value = IntOption;
179+
return Task.FromResult(IntOption);
173180
}
174181
}
175182

src/System.CommandLine.Hosting.Tests/HostingTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ void Execute(IHost host)
150150
}
151151

152152
[Fact]
153-
public async static Task UseHost_flows_config_directives_to_HostConfiguration()
153+
public static async Task UseHost_flows_config_directives_to_HostConfiguration()
154154
{
155155
const string testKey = "Test";
156156
const string testValue = "Value";

src/System.CommandLine.Hosting/DirectiveConfigurationExtensions.cs

Lines changed: 0 additions & 36 deletions
This file was deleted.

src/System.CommandLine.Hosting/HostingAction.cs

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
using System.CommandLine.Binding;
1+
using System.Collections.Generic;
2+
using System.CommandLine.Binding;
23
using System.CommandLine.Invocation;
34
using System.CommandLine.NamingConventionBinder;
45
using System.Linq;
56
using System.Threading;
67
using System.Threading.Tasks;
8+
using Microsoft.Extensions.Configuration;
79
using Microsoft.Extensions.DependencyInjection;
810
using Microsoft.Extensions.Hosting;
911

@@ -12,6 +14,8 @@ namespace System.CommandLine.Hosting
1214
// It's a wrapper, that configures the host, starts it and then runs the actual action.
1315
internal sealed class HostingAction : BindingHandler
1416
{
17+
internal const string HostingDirectiveName = "config";
18+
1519
private readonly Func<string[], IHostBuilder> _hostBuilderFactory;
1620
private readonly Action<IHostBuilder> _configureHost;
1721
private readonly AsynchronousCliAction _actualAction;
@@ -46,8 +50,26 @@ public override async Task<int> InvokeAsync(ParseResult parseResult, Cancellatio
4650
?? new HostBuilder();
4751
hostBuilder.Properties[typeof(ParseResult)] = parseResult;
4852

49-
CliDirective configurationDirective = parseResult.Configuration.Directives.Single(d => d.Name == "config");
50-
hostBuilder.ConfigureHostConfiguration(config => { config.AddCommandLineDirectives(parseResult, configurationDirective); });
53+
if (parseResult.Configuration.RootCommand is CliRootCommand root &&
54+
root.Directives.SingleOrDefault(d => d.Name == HostingDirectiveName) is { } directive)
55+
{
56+
if (parseResult.GetResult(directive) is { } directiveResult)
57+
{
58+
hostBuilder.ConfigureHostConfiguration(config =>
59+
{
60+
var kvpSeparator = new[] { '=' };
61+
62+
config.AddInMemoryCollection(directiveResult.Values.Select(s =>
63+
{
64+
var parts = s.Split(kvpSeparator, count: 2);
65+
var key = parts[0];
66+
var value = parts.Length > 1 ? parts[1] : null;
67+
return new KeyValuePair<string, string>(key, value);
68+
}).ToList());
69+
});
70+
}
71+
}
72+
5173
var bindingContext = GetBindingContext(parseResult);
5274
int registeredBefore = 0;
5375
hostBuilder.UseInvocationLifetime();

src/System.CommandLine.Hosting/HostingExtensions.cs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,25 @@ namespace System.CommandLine.Hosting
1111
{
1212
public static class HostingExtensions
1313
{
14-
public static CliConfiguration UseHost(this CliConfiguration builder,
14+
public static CliConfiguration UseHost(
15+
this CliConfiguration config,
1516
Func<string[], IHostBuilder> hostBuilderFactory,
1617
Action<IHostBuilder> configureHost = null)
1718
{
18-
builder.Directives.Add(new CliDirective("config"));
19+
if (config.RootCommand is CliRootCommand root)
20+
{
21+
root.Add(new CliDirective(HostingAction.HostingDirectiveName));
22+
}
1923

20-
HostingAction.SetHandlers(builder.RootCommand, hostBuilderFactory, configureHost);
24+
HostingAction.SetHandlers(config.RootCommand, hostBuilderFactory, configureHost);
2125

22-
return builder;
26+
return config;
2327
}
2428

25-
public static CliConfiguration UseHost(this CliConfiguration builder,
29+
public static CliConfiguration UseHost(
30+
this CliConfiguration config,
2631
Action<IHostBuilder> configureHost = null
27-
) => UseHost(builder, null, configureHost);
32+
) => UseHost(config, null, configureHost);
2833

2934
public static IHostBuilder UseInvocationLifetime(this IHostBuilder host, Action<InvocationLifetimeOptions> configureOptions = null)
3035
{

src/System.CommandLine.Suggest/SuggestionDispatcher.cs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -71,14 +71,7 @@ public SuggestionDispatcher(ISuggestionRegistration suggestionRegistration, ISug
7171
CompleteScriptCommand,
7272
};
7373
root.TreatUnmatchedTokensAsErrors = false;
74-
Configuration = new CliConfiguration(root)
75-
{
76-
Directives =
77-
{
78-
new DiagramDirective(),
79-
new SuggestDirective(),
80-
}
81-
};
74+
Configuration = new CliConfiguration(root);
8275
}
8376

8477
private CliCommand CompleteScriptCommand { get; }

src/System.CommandLine.Tests/Binding/TypeConversionTests.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,19 @@
88
using System.IO;
99
using System.Linq;
1010
using System.Net;
11-
using System.Threading;
1211
using Xunit;
1312

1413
namespace System.CommandLine.Tests.Binding
1514
{
1615
public class TypeConversionTests
1716
{
18-
protected virtual T GetValue<T>(CliOption<T> option, string commandLine)
17+
protected T GetValue<T>(CliOption<T> option, string commandLine)
1918
{
2019
var result = new CliRootCommand { option }.Parse(commandLine);
2120
return result.GetValue(option);
2221
}
2322

24-
protected virtual T GetValue<T>(CliArgument<T> argument, string commandLine)
23+
protected T GetValue<T>(CliArgument<T> argument, string commandLine)
2524
{
2625
var result = new CliRootCommand { argument }.Parse(commandLine);
2726
return result.GetValue(argument);

src/System.CommandLine.Tests/CommandTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) .NET Foundation and contributors. All rights reserved.
1+
// Copyright (c) .NET Foundation and contributors. All rights reserved.
22
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
33

44
using FluentAssertions;

0 commit comments

Comments
 (0)