Skip to content

Defining custom directives

Adam Bajguz edited this page Apr 5, 2021 · 12 revisions

To define a custom directive, just create a new class that implements the IDirective or IPipelinedDirective interface and annotate it with [Directive] attribute:

[Directive(BuiltInDirectives.Debug, Description = "Starts a debugging mode. Application will wait for debugger to be attached before proceeding.")]
public sealed class DebugDirective : IPipelinedDirective
{
    /// <inheritdoc/>
    public ValueTask OnInitializedAsync(CancellationToken cancellationToken)
    {
        return default;
    }

    /// <inheritdoc/>
    public async ValueTask HandleAsync(ICliContext context, CommandPipelineHandlerDelegate next, CancellationToken cancellationToken)
    {
#if NET5_0
        int processId = Environment.ProcessId;
#else
        int processId = Process.GetCurrentProcess().Id;
#endif

        IConsole console = context.Console;

        console.Output.WithForegroundColor(ConsoleColor.Green, (output) => output.WriteLine($"Attach debugger to PID {processId} to continue."));

        Debugger.Launch();

        while (!Debugger.IsAttached)
            await Task.Delay(100, cancellationToken);

        await next();
    }
}

To implement IDirective, the class needs to define ContinueExecution property and the HandleAsync method what gets called when the user specifies the directive in a command.

To facilitate both asynchronous and synchronous execution, this method returns a ValueTask. Since the simple command above executes synchronously, we can just put return default at the end. In an asynchronous command, however, we would use the async/await keywords instead.

As a parameter, this method takes an instance of IConsole, an abstraction around the system console. You should use this abstraction in places where you would normally use System.Console, in order to make your command testable.

ContinueExecution property is used to determine whether to stop execution of the command after exiting the directive handler. It can be a get only or a get/set property.

Similarly to commands, in every directive it is possible to define a description and a manual with [Directive] attribute. [Directive] attribute provides also an easy way for excluding a command from execution in normal mode through InteractiveModeOnly property.

Clone this wiki locally