Skip to content

Commit 2733298

Browse files
committed
ConsoleAppContext.GlobalOptions
1 parent e929244 commit 2733298

File tree

5 files changed

+245
-69
lines changed

5 files changed

+245
-69
lines changed

sandbox/GeneratorSandbox/Program.cs

Lines changed: 100 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
using ConsoleAppFramework;
2+
using GeneratorSandbox;
23
using Microsoft.Extensions.DependencyInjection;
34
using Microsoft.Extensions.Logging;
5+
using System.ComponentModel.DataAnnotations;
46
using System.Diagnostics.CodeAnalysis;
57
using System.Runtime.CompilerServices;
68

@@ -12,7 +14,7 @@
1214
//var app = ConsoleApp.Create();
1315

1416

15-
//args = ["--x", "10", "--y", "20", "-f", "Orange", "-v", "--prefix-output", "takoyakix"];
17+
args = ["--x", "10", "--y", "20", "-v", "--prefix-output", "takoyakix"];
1618

1719

1820
//// Enum.TryParse<Fruit>("", true,
@@ -62,7 +64,7 @@
6264

6365
app.ConfigureGlobalOptions((ref builder) =>
6466
{
65-
var verbose = builder.AddGlobalOption<bool>($"takoyaki", "", true);
67+
var verbose = builder.AddGlobalOption<bool>($"-v", "", true);
6668
var noColor = builder.AddGlobalOption<bool>("--no-color", "Don't colorize output.");
6769
var dryRun = builder.AddGlobalOption<bool>("--dry-run");
6870
var prefixOutput = builder.AddRequiredGlobalOption<string>("--prefix-output|-pp|-po", "Prefix output with level.");
@@ -71,18 +73,21 @@
7173
});
7274

7375

74-
app.Add("", (int x, int y, ConsoleAppContext context) =>
76+
app.Add("", async (int x, int y, ConsoleAppContext context, CancellationToken cancellationToken) =>
7577
{
76-
Console.WriteLine(context.CommandName);
78+
Console.WriteLine("OK");
79+
await Task.Delay(TimeSpan.FromSeconds(1));
80+
Console.WriteLine(context.CommandName + ":" + (x, y));
7781
});
7882

7983
app.Add("tako", (int x, int y, ConsoleAppContext context) =>
8084
{
8185
Console.WriteLine(context.CommandName);
8286
});
8387

88+
app.UseFilter<NopFilter>();
8489

85-
app.Run(args);
90+
await app.RunAsync(args);
8691

8792

8893

@@ -150,7 +155,97 @@ internal static partial class ConsoleApp
150155
internal partial class ConsoleAppBuilder
151156
{
152157

158+
159+
160+
161+
162+
163+
164+
private void RunCommand0_2(string[] args, int commandDepth, int escapeIndex, Action<int, int, global::ConsoleAppFramework.ConsoleAppContext> command, CancellationToken __ExternalCancellationToken__)
165+
{
166+
var commandArgs = (escapeIndex == -1) ? args.AsSpan(commandDepth) : args.AsSpan(commandDepth, escapeIndex - commandDepth);
167+
if (TryShowHelpOrVersion(commandArgs, 2, 0)) return;
168+
169+
ConsoleAppContext context = default!;
170+
//if (configureGlobalOptions == null)
171+
//{
172+
// context = new ConsoleAppContext("", args, null, null, commandDepth, escapeIndex);
173+
//}
174+
//else
175+
//{
176+
// var builder = new GlobalOptionsBuilder(commandArgs);
177+
// var globalOptions = configureGlobalOptions(ref builder);
178+
// context = new ConsoleAppContext("", args, null, globalOptions, commandDepth, escapeIndex);
179+
// commandArgs = builder.RemainingArgs;
180+
//}
181+
//BuildAndSetServiceProvider(context);
182+
183+
var arg0 = default(int);
184+
var arg0Parsed = false;
185+
var arg1 = default(int);
186+
var arg1Parsed = false;
187+
var arg2 = context;
188+
189+
try
190+
{
191+
for (int i = 0; i < commandArgs.Length; i++)
192+
{
193+
var name = commandArgs[i];
194+
195+
switch (name)
196+
{
197+
case "--x":
198+
{
199+
if (!TryIncrementIndex(ref i, commandArgs.Length) || !int.TryParse(commandArgs[i], out arg0)) { ThrowArgumentParseFailed("x", commandArgs[i]); }
200+
arg0Parsed = true;
201+
continue;
202+
}
203+
case "--y":
204+
{
205+
if (!TryIncrementIndex(ref i, commandArgs.Length) || !int.TryParse(commandArgs[i], out arg1)) { ThrowArgumentParseFailed("y", commandArgs[i]); }
206+
arg1Parsed = true;
207+
continue;
208+
}
209+
default:
210+
if (string.Equals(name, "--x", StringComparison.OrdinalIgnoreCase))
211+
{
212+
if (!TryIncrementIndex(ref i, commandArgs.Length) || !int.TryParse(commandArgs[i], out arg0)) { ThrowArgumentParseFailed("x", commandArgs[i]); }
213+
arg0Parsed = true;
214+
continue;
215+
}
216+
if (string.Equals(name, "--y", StringComparison.OrdinalIgnoreCase))
217+
{
218+
if (!TryIncrementIndex(ref i, commandArgs.Length) || !int.TryParse(commandArgs[i], out arg1)) { ThrowArgumentParseFailed("y", commandArgs[i]); }
219+
arg1Parsed = true;
220+
continue;
221+
}
222+
ThrowArgumentNameNotFound(name);
223+
break;
224+
}
225+
}
226+
if (!arg0Parsed) ThrowRequiredArgumentNotParsed("x");
227+
if (!arg1Parsed) ThrowRequiredArgumentNotParsed("y");
228+
229+
command(arg0!, arg1!, arg2!);
230+
}
231+
catch (Exception ex)
232+
{
233+
Environment.ExitCode = 1;
234+
if (ex is ValidationException or ArgumentParseFailedException)
235+
{
236+
LogError(ex.Message);
237+
}
238+
else
239+
{
240+
LogError(ex.ToString());
241+
}
242+
}
243+
}
244+
245+
153246
}
154247

248+
249+
155250
}
156251
}

src/ConsoleAppFramework.Abstractions/ConsoleApp.Abstractions.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,17 @@ public record ConsoleAppContext
1212
public string CommandName { get; init; }
1313
public string[] Arguments { get; init; }
1414
public object? State { get; init; }
15+
public object? GlobalOptions { get; init; }
1516

1617
[EditorBrowsable(EditorBrowsableState.Never)]
1718
public int CommandDepth { get; }
1819

1920
[EditorBrowsable(EditorBrowsableState.Never)]
2021
public int EscapeIndex { get; }
2122

23+
[EditorBrowsable(EditorBrowsableState.Never)]
24+
public string[] InternalCommandArgs { get; }
25+
2226
public ReadOnlySpan<string> CommandArguments
2327
{
2428
get => (EscapeIndex == -1)
@@ -33,11 +37,13 @@ public ReadOnlySpan<string> EscapedArguments
3337
: Arguments.AsSpan(EscapeIndex + 1);
3438
}
3539

36-
public ConsoleAppContext(string commandName, string[] arguments, object? state, int commandDepth, int escapeIndex)
40+
public ConsoleAppContext(string commandName, string[] arguments, string[] commandArgs, object? state, object? globalOptions, int commandDepth, int escapeIndex)
3741
{
3842
this.CommandName = commandName;
3943
this.Arguments = arguments;
44+
this.InternalCommandArgs = commandArgs;
4045
this.State = state;
46+
this.GlobalOptions = globalOptions;
4147
this.CommandDepth = commandDepth;
4248
this.EscapeIndex = escapeIndex;
4349
}

src/ConsoleAppFramework/ConsoleAppBaseCode.cs

Lines changed: 64 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,10 @@ internal record ConsoleAppContext
5050
public string CommandName { get; init; }
5151
public string[] Arguments { get; init; }
5252
public object? State { get; init; }
53+
public object? GlobalOptions { get; init; }
5354
internal int CommandDepth { get; }
5455
internal int EscapeIndex { get; }
56+
internal string[] InternalCommandArgs { get; }
5557
5658
public ReadOnlySpan<string> CommandArguments
5759
{
@@ -67,11 +69,13 @@ public ReadOnlySpan<string> EscapedArguments
6769
: Arguments.AsSpan(EscapeIndex + 1);
6870
}
6971
70-
public ConsoleAppContext(string commandName, string[] arguments, object? state, int commandDepth, int escapeIndex)
72+
public ConsoleAppContext(string commandName, string[] arguments, string[] commandArgs, object? state, object? globalOptions, int commandDepth, int escapeIndex)
7173
{
7274
this.CommandName = commandName;
7375
this.Arguments = arguments;
76+
this.InternalCommandArgs = commandArgs;
7477
this.State = state;
78+
this.GlobalOptions = globalOptions;
7579
this.CommandDepth = commandDepth;
7680
this.EscapeIndex = escapeIndex;
7781
}
@@ -373,33 +377,6 @@ static void ShowVersion()
373377
374378
static partial void ShowHelp(int helpId);
375379
376-
static async Task RunWithFilterAsync(string commandName, string[] args, int commandDepth, int escapeIndex, ConsoleAppFilter invoker, CancellationToken cancellationToken)
377-
{
378-
using var posixSignalHandler = PosixSignalHandler.Register(Timeout, cancellationToken);
379-
try
380-
{
381-
await Task.Run(() => invoker.InvokeAsync(new ConsoleAppContext(commandName, args, null, commandDepth, escapeIndex), posixSignalHandler.Token)).WaitAsync(posixSignalHandler.TimeoutToken);
382-
}
383-
catch (Exception ex)
384-
{
385-
if (ex is OperationCanceledException)
386-
{
387-
Environment.ExitCode = 130;
388-
return;
389-
}
390-
391-
Environment.ExitCode = 1;
392-
if (ex is ValidationException or ArgumentParseFailedException)
393-
{
394-
LogError(ex.Message);
395-
}
396-
else
397-
{
398-
LogError(ex.ToString());
399-
}
400-
}
401-
}
402-
403380
sealed class PosixSignalHandler : IDisposable
404381
{
405382
public CancellationToken Token => cancellationTokenSource.Token;
@@ -492,7 +469,9 @@ public void UseFilter<T>() where T : ConsoleAppFilter { }
492469
[MethodImpl(MethodImplOptions.AggressiveInlining)]
493470
partial void RunAsyncCore(string[] args, CancellationToken cancellationToken, ref Task result);
494471
495-
partial void BuildAndSetServiceProvider();
472+
partial void BuildAndSetServiceProvider(ConsoleAppContext context);
473+
474+
partial void StartHostAsyncIfNeeded(CancellationToken cancellationToken, ref Task task);
496475
497476
static partial void ShowHelp(int helpId);
498477
@@ -531,15 +510,62 @@ public ConsoleAppBuilder ConfigureGlobalOptions(FuncGlobalOptionsBuilderObject c
531510
this.configureGlobalOptions = configure;
532511
return this;
533512
}
513+
514+
async Task RunWithFilterAsync(string commandName, string[] args, int commandDepth, int escapeIndex, ConsoleAppFilter invoker, CancellationToken cancellationToken)
515+
{
516+
using var posixSignalHandler = PosixSignalHandler.Register(Timeout, cancellationToken);
517+
try
518+
{
519+
ConsoleAppContext context;
520+
if (configureGlobalOptions == null)
521+
{
522+
context = new ConsoleAppContext(commandName, args, args, null, null, commandDepth, escapeIndex);
523+
}
524+
else
525+
{
526+
var builder = new GlobalOptionsBuilder(args);
527+
var globalOptions = configureGlobalOptions(ref builder);
528+
context = new ConsoleAppContext(commandName, args, builder.RemainingArgs.ToArray(), null, globalOptions, commandDepth, escapeIndex);
529+
}
530+
BuildAndSetServiceProvider(context);
531+
532+
Task? startHostTask = null;
533+
StartHostAsyncIfNeeded(posixSignalHandler.Token, ref startHostTask);
534+
if (startHostTask != null)
535+
{
536+
await startHostTask;
537+
}
538+
539+
await Task.Run(() => invoker.InvokeAsync(context, posixSignalHandler.Token)).WaitAsync(posixSignalHandler.TimeoutToken);
540+
}
541+
catch (Exception ex)
542+
{
543+
if (ex is OperationCanceledException)
544+
{
545+
Environment.ExitCode = 130;
546+
return;
547+
}
548+
549+
Environment.ExitCode = 1;
550+
if (ex is ValidationException or ArgumentParseFailedException)
551+
{
552+
LogError(ex.Message);
553+
}
554+
else
555+
{
556+
LogError(ex.ToString());
557+
}
558+
}
559+
}
534560
}
535561
536562
internal ref struct GlobalOptionsBuilder
537563
{
538564
Span<string> args;
539565
540-
ReadOnlySpan<string> Args => args;
566+
public Span<string> RemainingArgs => args;
541567
542-
public GlobalOptionsBuilder(string[] args)
568+
public GlobalOptionsBuilder(Span<string> args)
543569
{
544570
this.args = args;
545571
}
@@ -931,7 +957,6 @@ internal partial class ConsoleAppBuilder
931957
932958
public void Run(string[] args, bool disposeServiceProvider, CancellationToken cancellationToken = default)
933959
{
934-
BuildAndSetServiceProvider();
935960
try
936961
{
937962
RunCore(args, cancellationToken);
@@ -953,7 +978,6 @@ public void Run(string[] args, bool disposeServiceProvider, CancellationToken ca
953978
954979
public async Task RunAsync(string[] args, bool disposeServiceProvider, CancellationToken cancellationToken = default)
955980
{
956-
BuildAndSetServiceProvider();
957981
try
958982
{
959983
Task? task = null;
@@ -1003,26 +1027,25 @@ internal static partial class ConsoleApp
10031027
{
10041028
internal partial class ConsoleAppBuilder
10051029
{
1030+
Microsoft.Extensions.Hosting.IHost? host;
1031+
bool startHost;
1032+
10061033
public void Run(string[] args) => Run(args, true, true, true);
10071034
public void Run(string[] args, CancellationToken cancellationToken) => Run(args, true, true, true, cancellationToken);
10081035
10091036
public void Run(string[] args, bool startHost, bool stopHost, bool disposeServiceProvider, CancellationToken cancellationToken = default)
10101037
{
1011-
BuildAndSetServiceProvider();
1012-
Microsoft.Extensions.Hosting.IHost? host = ConsoleApp.ServiceProvider?.GetService(typeof(Microsoft.Extensions.Hosting.IHost)) as Microsoft.Extensions.Hosting.IHost;
1038+
this.startHost = startHost;
10131039
try
10141040
{
1015-
if (startHost && host != null)
1016-
{
1017-
host.StartAsync(cancellationToken).GetAwaiter().GetResult();
1018-
}
10191041
RunCore(args, cancellationToken);
10201042
}
10211043
finally
10221044
{
10231045
if (stopHost && host != null)
10241046
{
10251047
host.StopAsync(cancellationToken).GetAwaiter().GetResult();
1048+
host = null;
10261049
}
10271050
if (disposeServiceProvider)
10281051
{
@@ -1039,14 +1062,9 @@ public void Run(string[] args, bool startHost, bool stopHost, bool disposeServic
10391062
10401063
public async Task RunAsync(string[] args, bool startHost, bool stopHost, bool disposeServiceProvider, CancellationToken cancellationToken = default)
10411064
{
1042-
BuildAndSetServiceProvider();
1043-
Microsoft.Extensions.Hosting.IHost? host = ConsoleApp.ServiceProvider?.GetService(typeof(Microsoft.Extensions.Hosting.IHost)) as Microsoft.Extensions.Hosting.IHost;
1065+
this.startHost = startHost;
10441066
try
10451067
{
1046-
if (startHost && host != null)
1047-
{
1048-
await host.StartAsync(cancellationToken);
1049-
}
10501068
Task? task = null;
10511069
RunAsyncCore(args, cancellationToken, ref task!);
10521070
if (task != null)
@@ -1059,6 +1077,7 @@ public async Task RunAsync(string[] args, bool startHost, bool stopHost, bool di
10591077
if (stopHost && host != null)
10601078
{
10611079
await host.StopAsync(cancellationToken);
1080+
host = null;
10621081
}
10631082
if (disposeServiceProvider)
10641083
{

0 commit comments

Comments
 (0)