Skip to content

Commit 529928c

Browse files
Merge pull request #3 from EAVFW/tst/argument-attribute
feat: Add Arugment Attribute to add command arguments using attribute
2 parents e4ebadf + bd74707 commit 529928c

File tree

2 files changed

+100
-28
lines changed

2 files changed

+100
-28
lines changed

Directory.Build.props

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<Project>
2+
<Import Condition="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../')) != ''"
3+
Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))"/>
4+
<PropertyGroup>
5+
<LangVersion>10.0</LangVersion>
6+
<LocalExternalpath Condition="'$(LocalExternalpath)' == ''">$(MSBuildThisFileDirectory)/external</LocalExternalpath>
7+
</PropertyGroup>
8+
</Project>

src/EAVFW.Extensions.CommandLine/Extensions.cs

Lines changed: 92 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,11 @@
1-

2-
using Microsoft.Extensions.DependencyInjection;
1+
using Microsoft.Extensions.DependencyInjection;
32
using Microsoft.Extensions.Hosting;
4-
using System;
53
using System.Collections.Generic;
6-
using System.CommandLine;
74
using System.CommandLine.Invocation;
85
using System.CommandLine.NamingConventionBinder;
96
using System.CommandLine.Parsing;
107
using System.ComponentModel;
118
using System.ComponentModel.DataAnnotations;
12-
using System.IO;
13-
using System.IO.Compression;
149
using System.Linq;
1510
using System.Reflection;
1611
using System.Threading;
@@ -22,14 +17,62 @@ namespace System.CommandLine
2217
public class AliasAttribute : Attribute
2318
{
2419
public string Alias { get; }
20+
2521
public AliasAttribute(string alias)
2622
{
2723
Alias = alias;
2824
}
2925
}
3026

27+
[AttributeUsage(AttributeTargets.Property)]
28+
public class ArgumentAttribute : Attribute
29+
{
30+
}
31+
3132
public static class COmmandExtensions
3233
{
34+
public static Dictionary<string, Argument> AddArguments(this Command command)
35+
{
36+
var output = new Dictionary<string, Argument>();
37+
38+
foreach (var prop in command.GetType().GetProperties())
39+
{
40+
var val = prop.GetValue(command);
41+
if (val is Option option)
42+
{
43+
if (prop.GetCustomAttribute<RequiredAttribute>() is RequiredAttribute)
44+
{
45+
option.IsRequired = true;
46+
}
47+
48+
command.Add(option);
49+
}
50+
else if (prop.GetCustomAttributes<ArgumentAttribute>().Any())
51+
{
52+
var argumentAttribute = prop.GetCustomAttributes<ArgumentAttribute>().FirstOrDefault();
53+
54+
if (argumentAttribute == null)
55+
continue;
56+
57+
var argument =
58+
typeof(COmmandExtensions).GetMethod(nameof(CreateArgument), 1, new[] { typeof(string) })
59+
?.MakeGenericMethod(prop.PropertyType).Invoke(null,
60+
new object[]
61+
{
62+
prop.GetCustomAttribute<DescriptionAttribute>()?.Description
63+
});
64+
65+
66+
if (argument is not Argument a) continue;
67+
68+
output.Add(prop.Name, a);
69+
command.Add(a);
70+
}
71+
}
72+
73+
return output;
74+
}
75+
3376
public static Dictionary<string, Option> AddOptions(this Command command)
3477
{
3578
var o = new Dictionary<string, Option>();
@@ -43,15 +86,25 @@ public static Dictionary<string, Option> AddOptions(this Command command)
4386
{
4487
option.IsRequired = true;
4588
}
89+
4690
command.Add(option);
4791
}
4892
else if (prop.GetCustomAttributes<AliasAttribute>().Any())
4993
{
5094
var aliass = prop.GetCustomAttributes<AliasAttribute>();
51-
var op = typeof(COmmandExtensions).GetMethod(nameof(CreateOption), 1, new[] { typeof(string), typeof(string) })
52-
.MakeGenericMethod(prop.PropertyType).Invoke(null, new object[] { aliass.First().Alias, prop.GetCustomAttribute<DescriptionAttribute>().Description }) as Option;
95+
var op = typeof(COmmandExtensions)
96+
.GetMethod(nameof(CreateOption), 1, new[] { typeof(string), typeof(string) })
97+
.MakeGenericMethod(prop.PropertyType).Invoke(null,
98+
new object[]
99+
{
100+
aliass.First().Alias, prop.GetCustomAttribute<DescriptionAttribute>().Description
101+
}) as Option;
102+
53103
foreach (var a in aliass.Skip(1))
104+
{
54105
op.AddAlias(a.Alias);
106+
}
107+
55108
o[prop.Name] = op;
56109

57110
command.Add(op);
@@ -60,42 +113,51 @@ public static Dictionary<string, Option> AddOptions(this Command command)
60113

61114
return o;
62115
}
116+
63117
public static Option<T> CreateOption<T>(string alias, string description)
64118
{
65119
return new Option<T>(alias, description);
66120
}
67121

68-
public static ICommandHandler Create(Command cmd, IEnumerable<Command> commands, Func<ParseResult, IConsole, Task<int>> runner)
122+
public static Argument<T> CreateArgument<T>(string description)
123+
{
124+
return new Argument<T> { Description = description };
125+
}
126+
127+
public static ICommandHandler Create(Command cmd, IEnumerable<Command> commands,
128+
Func<ParseResult, IConsole, Task<int>> runner)
69129
{
70130
foreach (var command in commands)
71131
cmd.Add(command);
72132

73133
var options = cmd.AddOptions();
74-
134+
var arguments = cmd.AddArguments();
75135

76136
Task<int> Run(ParseResult parsed, IConsole console)
77137
{
78-
79138
foreach (var o in options)
80139
{
81140
cmd.GetType().GetProperty(o.Key).SetValue(cmd, parsed.GetValueForOption(o.Value));
82141
}
142+
foreach (var (key, argument) in arguments)
143+
{
144+
cmd.GetType().GetProperty(key)!.SetValue(cmd, parsed.GetValueForArgument(argument));
145+
}
83146

84147
return runner(parsed, console);
85148
}
86149

87150

88-
return CommandHandler.Create<ParseResult,IConsole>(Run);
89-
90-
151+
return CommandHandler.Create<ParseResult, IConsole>(Run);
91152
}
92-
93153
}
154+
94155
internal sealed class ConsoleHostedService<TApp> : IHostedService where TApp : RootCommand
95156
{
96157
private readonly IHostApplicationLifetime appLifetime;
97158
private readonly TApp app;
98159
public int Result = 0;
160+
99161
public ConsoleHostedService(
100162
IHostApplicationLifetime appLifetime,
101163
IServiceProvider serviceProvider,
@@ -109,46 +171,48 @@ public ConsoleHostedService(
109171
public Task StartAsync(CancellationToken cancellationToken)
110172
{
111173
return app.InvokeAsync(System.Environment.GetCommandLineArgs().Skip(1).ToArray())
112-
.ContinueWith(result =>
113-
{
114-
Result = result.Result;
115-
appLifetime.StopApplication();
116-
117-
});
174+
.ContinueWith(result =>
175+
{
176+
Result = result.Result;
177+
appLifetime.StopApplication();
178+
});
118179
}
119180

120181
public Task StopAsync(CancellationToken cancellationToken)
121182
{
122-
123183
return Task.CompletedTask;
124184
}
125185
}
186+
126187
public static class Extensions
127188
{
128189
public static T GetValue<T>(this Argument<T> argument, ParseResult parser)
129190
=> parser.GetValueForArgument(argument);
191+
130192
public static T GetValue<T>(this Option<T> argument, ParseResult parser)
131-
=> parser.GetValueForOption(argument);
132-
193+
=> parser.GetValueForOption(argument);
194+
133195
public static IServiceCollection AddCommand<TCommand>(this IServiceCollection services) where TCommand : Command
134196
{
135197
return services.AddSingleton<Command, TCommand>();
136198
}
137-
public static IServiceCollection AddConsoleApp<TCommand>(this IServiceCollection services) where TCommand : RootCommand
199+
200+
public static IServiceCollection AddConsoleApp<TCommand>(this IServiceCollection services)
201+
where TCommand : RootCommand
138202
{
139203
services.AddSingleton<ConsoleHostedService<TCommand>>();
140204
services.AddHostedService(sp => sp.GetRequiredService<ConsoleHostedService<TCommand>>());
141205
return services.AddSingleton<TCommand>();
142206
}
207+
143208
public static IHostBuilder AddConsoleApp<TCommand>(this IHostBuilder hostbuilder) where TCommand : RootCommand
144209
{
145-
hostbuilder.ConfigureServices((_,services) =>services.AddConsoleApp<TCommand>());
210+
hostbuilder.ConfigureServices((_, services) => services.AddConsoleApp<TCommand>());
146211

147212
return hostbuilder;
148-
149213
}
150214

151-
public static async Task<int> RunConsoleApp<TApp>(this IHost host ) where TApp : RootCommand
215+
public static async Task<int> RunConsoleApp<TApp>(this IHost host) where TApp : RootCommand
152216
{
153217
var app = host.Services.GetRequiredService<ConsoleHostedService<TApp>>();
154218
await host.RunAsync();

0 commit comments

Comments
 (0)