Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Revert "provide more flexible mechanism for directive handlers: possi…
…bility to pass next handler and invoke it or not"

This reverts commit fc987d4.
  • Loading branch information
adamsitnik committed Feb 24, 2023
commit ea619bf743a0d5a04c292a3281eb91b8dd853f6e
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,10 @@ System.CommandLine
public static System.Void Write(this IConsole console, System.String value)
public static System.Void WriteLine(this IConsole console, System.String value)
public class Directive : Symbol
.ctor(System.String name, System.String description = null, System.Action<System.CommandLine.Invocation.InvocationContext,ICommandHandler> syncHandler = null, System.Func<System.CommandLine.Invocation.InvocationContext,ICommandHandler,System.Threading.CancellationToken,System.Threading.Tasks.Task> asyncHandler = null)
.ctor(System.String name, System.String description = null, System.Action<System.CommandLine.Invocation.InvocationContext> syncHandler = null, System.Func<System.CommandLine.Invocation.InvocationContext,System.Threading.CancellationToken,System.Threading.Tasks.Task> asyncHandler = null)
public System.Collections.Generic.IEnumerable<System.CommandLine.Completions.CompletionItem> GetCompletions(System.CommandLine.Completions.CompletionContext context)
public System.Void SetAsynchronousHandler(System.Func<System.CommandLine.Invocation.InvocationContext,ICommandHandler,System.Threading.CancellationToken,System.Threading.Tasks.Task> handler)
public System.Void SetSynchronousHandler(System.Action<System.CommandLine.Invocation.InvocationContext,ICommandHandler> handler)
public System.Void SetAsynchronousHandler(System.Func<System.CommandLine.Invocation.InvocationContext,System.Threading.CancellationToken,System.Threading.Tasks.Task> handler)
public System.Void SetSynchronousHandler(System.Action<System.CommandLine.Invocation.InvocationContext> handler)
public class EnvironmentVariablesDirective : Directive
.ctor()
public static class Handler
Expand Down
88 changes: 0 additions & 88 deletions src/System.CommandLine.Tests/DirectiveTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using FluentAssertions;
using Xunit;

Expand Down Expand Up @@ -139,50 +137,6 @@ public void When_directives_are_not_enabled_they_are_treated_as_regular_tokens()
.BeEquivalentTo("[hello]");
}

[Theory]
[InlineData(false)]
[InlineData(true)]
public async Task Directive_can_restore_the_state_after_running_continuation(bool async)
{
const string plCulture = "pl-PL", enUsCulture = "en-US";
const string envVarName = "uniqueName", envVarValue = "just";

var before = CultureInfo.CurrentUICulture;

try
{
CultureInfo.CurrentUICulture = new(enUsCulture);

bool invoked = false;
Option<bool> option = new("-a");
RootCommand root = new() { option };
CommandLineBuilder builder = new(root);
builder.Directives.Add(new EnvironmentVariablesDirective());
builder.Directives.Add(new CultureDirective());
root.SetHandler(ctx =>
{
invoked = true;
CultureInfo.CurrentUICulture.Name.Should().Be(plCulture);
Environment.GetEnvironmentVariable(envVarName).Should().Be(envVarValue);
});

if (async)
{
await builder.Build().InvokeAsync($"[culture:{plCulture}] [env:{envVarName}={envVarValue}]");
}
else
{
builder.Build().Invoke($"[culture:{plCulture}] [env:{envVarName}={envVarValue}]");
}

invoked.Should().BeTrue();
}
finally
{
CultureInfo.CurrentUICulture = before;
}
}

private static ParseResult Parse(Option option, Directive directive, string commandLine)
{
RootCommand root = new() { option };
Expand All @@ -191,47 +145,5 @@ private static ParseResult Parse(Option option, Directive directive, string comm

return root.Parse(commandLine, builder.Build());
}

private sealed class CultureDirective : Directive
{
public CultureDirective() : base("culture")
{
SetSynchronousHandler((ctx, next) =>
{
CultureInfo cultureBefore = CultureInfo.CurrentUICulture;

try
{
string cultureName = ctx.ParseResult.FindResultFor(this).Values.Single();

CultureInfo.CurrentUICulture = new CultureInfo(cultureName);

next?.Invoke(ctx);
}
finally
{
CultureInfo.CurrentUICulture = cultureBefore;
}
});
SetAsynchronousHandler(async (ctx, next, ct) =>
{
CultureInfo cultureBefore = CultureInfo.CurrentUICulture;

try
{
string cultureName = ctx.ParseResult.FindResultFor(this).Values.Single();

CultureInfo.CurrentUICulture = new CultureInfo(cultureName);

await next?.InvokeAsync(ctx, ct);
}
finally
{
CultureInfo.CurrentUICulture = cultureBefore;
}
});
}
}

}
}
44 changes: 30 additions & 14 deletions src/System.CommandLine/Directive.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.CommandLine.Invocation;
using System.Threading.Tasks;
using System.Threading;
using System.CommandLine.Parsing;

namespace System.CommandLine
{
Expand All @@ -17,22 +18,17 @@ namespace System.CommandLine
/// </summary>
public class Directive : Symbol
{
internal Action<InvocationContext, ICommandHandler?>? SyncHandler;
internal Func<InvocationContext, ICommandHandler?, CancellationToken, Task>? AsyncHandler;

/// <summary>
/// Initializes a new instance of the Directive class.
/// </summary>
/// <param name="name">The name of the directive. It can't contain whitespaces.</param>
/// <param name="description">The description of the directive, shown in help.</param>
/// <param name="syncHandler">The synchronous action that is invoked when directive is parsed.</param>
/// <param name="asyncHandler">The asynchronous action that is invoked when directive is parsed.</param>
/// <remarks>The second argument of both handlers is next handler than can be invoked.
/// Example: a custom directive might just change current culture and run actual command afterwards.</remarks>
public Directive(string name,
string? description = null,
Action<InvocationContext, ICommandHandler?>? syncHandler = null,
Func<InvocationContext, ICommandHandler?, CancellationToken, Task>? asyncHandler = null)
Action<InvocationContext>? syncHandler = null,
Func<InvocationContext, CancellationToken, Task>? asyncHandler = null)
{
if (string.IsNullOrWhiteSpace(name))
{
Expand All @@ -50,17 +46,37 @@ public Directive(string name,
Name = name;
Description = description;

SyncHandler = syncHandler;
AsyncHandler = asyncHandler;
if (syncHandler is not null)
{
SetSynchronousHandler(syncHandler);
}
else if (asyncHandler is not null)
{
SetAsynchronousHandler(asyncHandler);
}
}

internal bool HasHandler => SyncHandler != null || AsyncHandler != null;
public void SetAsynchronousHandler(Func<InvocationContext, CancellationToken, Task> handler)
{
if (handler is null)
{
throw new ArgumentNullException(nameof(handler));
}

public void SetAsynchronousHandler(Func<InvocationContext, ICommandHandler?, CancellationToken, Task> handler)
=> AsyncHandler = handler ?? throw new ArgumentNullException(nameof(handler));
Handler = new AnonymousCommandHandler(handler);
}

public void SetSynchronousHandler(Action<InvocationContext> handler)
{
if (handler is null)
{
throw new ArgumentNullException(nameof(handler));
}

Handler = new AnonymousCommandHandler(handler);
}

public void SetSynchronousHandler(Action<InvocationContext, ICommandHandler?> handler)
=> SyncHandler = handler ?? throw new ArgumentNullException(nameof(handler));
internal ICommandHandler? Handler { get; private set; }

private protected override string DefaultName => throw new NotImplementedException();

Expand Down
29 changes: 8 additions & 21 deletions src/System.CommandLine/EnvironmentVariablesDirective.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using System.CommandLine.Parsing;
using System.Threading.Tasks;
using System.CommandLine.Invocation;
using System.CommandLine.Parsing;

namespace System.CommandLine
{
Expand All @@ -10,28 +10,12 @@ public sealed class EnvironmentVariablesDirective : Directive
{
public EnvironmentVariablesDirective() : base("env")
{
SetSynchronousHandler((context, next) =>
{
SetEnvVars(context.ParseResult);

next?.Invoke(context);
});
SetAsynchronousHandler((context, next, cancellationToken) =>
{
if (cancellationToken.IsCancellationRequested)
{
return Task.FromCanceled(cancellationToken);
}

SetEnvVars(context.ParseResult);

return next?.InvokeAsync(context, cancellationToken) ?? Task.CompletedTask;
});
SetSynchronousHandler(SyncHandler);
}

private void SetEnvVars(ParseResult parseResult)
private void SyncHandler(InvocationContext context)
{
DirectiveResult directiveResult = parseResult.FindResultFor(this)!;
DirectiveResult directiveResult = context.ParseResult.FindResultFor(this)!;

for (int i = 0; i < directiveResult.Values.Count; i++)
{
Expand All @@ -51,6 +35,9 @@ private void SetEnvVars(ParseResult parseResult)
}
}
}

// we need a cleaner, more flexible and intuitive way of continuing the execution
context.ParseResult.CommandResult.Command.Handler?.Invoke(context);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jonsequitur I've reverted the ability to handle continuations and hardcoded it for now as we have agreed

fc987d4

}
}
}
81 changes: 0 additions & 81 deletions src/System.CommandLine/Invocation/CombinedCommandHandler.cs

This file was deleted.

20 changes: 2 additions & 18 deletions src/System.CommandLine/ParseDirective.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System.CommandLine.Invocation;
using System.CommandLine.IO;
using System.CommandLine.Parsing;
using System.Threading.Tasks;

namespace System.CommandLine
{
Expand All @@ -13,32 +12,17 @@ public sealed class ParseDirective : Directive
/// <param name="errorExitCode">If the parse result contains errors, this exit code will be used when the process exits.</param>
public ParseDirective(int errorExitCode = 1) : base("parse")
{
SetSynchronousHandler(SyncHandler);
ErrorExitCode = errorExitCode;

SetSynchronousHandler(PrintDiagramAndQuit);
SetAsynchronousHandler((context, next, cancellationToken) =>
{
if (cancellationToken.IsCancellationRequested)
{
return Task.FromCanceled(cancellationToken);
}

PrintDiagramAndQuit(context, null);

return Task.CompletedTask;
});
}

internal int ErrorExitCode { get; }

private void PrintDiagramAndQuit(InvocationContext context, ICommandHandler? next)
private void SyncHandler(InvocationContext context)
{
var parseResult = context.ParseResult;
context.Console.Out.WriteLine(parseResult.Diagram());
context.ExitCode = parseResult.Errors.Count == 0 ? 0 : ErrorExitCode;

// parse directive has a precedence over --help and --version and any command
// we don't invoke next here.
}
}
}
Loading