RSCG – ConsoleAppFramework

RSCG – ConsoleAppFramework
 
 

name ConsoleAppFramework
nuget https://www.nuget.org/packages/ConsoleAppFramework/
link https://github.com/Cysharp/ConsoleAppFramework
author Cysharp, Inc.

Generating console parser for functions

 

This is how you can use ConsoleAppFramework .

The code that you start with is


<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net9.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>
	<PropertyGroup>
		<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
		<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
	</PropertyGroup>
	<ItemGroup>
	  <PackageReference Include="ConsoleAppFramework" Version="5.6.1">
	    <PrivateAssets>all</PrivateAssets>
	    <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
	  </PackageReference>
	</ItemGroup>
</Project>


The code that you will use is


var app = ConsoleAppFramework.ConsoleApp.Create();

app.Add("", (string msg) => Console.WriteLine(msg));
app.Add("echo", (string msg) => Console.WriteLine(msg));
app.Add("sum", (int x, int y) => Console.WriteLine(x + y));

// --help
// --msg Andrei
// echo --msg Andrei
// sum --x 55 --y 0
app.Run(args);

 

The code that is generated is

// <auto-generated/>
#nullable enable
#pragma warning disable

namespace ConsoleAppFramework;

using System;
using System.Text;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Diagnostics.CodeAnalysis;
using System.ComponentModel.DataAnnotations;

internal static partial class ConsoleApp
{
    partial class ConsoleAppBuilder
    {
        Action<string> command0 = default!;
        Action<string> command1 = default!;
        Action<int, int> command2 = default!;

        partial void AddCore(string commandName, Delegate command)
        {
            switch (commandName)
            {
                case "":
                    this.command0 = Unsafe.As<Action<string>>(command);
                    break;
                case "echo":
                    this.command1 = Unsafe.As<Action<string>>(command);
                    break;
                case "sum":
                    this.command2 = Unsafe.As<Action<int, int>>(command);
                    break;
                default:
                    break;
            }
        }

        partial void RunCore(string[] args, CancellationToken cancellationToken)
        {
            if (args.Length == 1 && args[0] is "--help" or "-h")
            {
                ShowHelp(-1);
                return;
            }
            if (args.Length == 0)
            {
                RunCommand0(args, 0, args.AsSpan().IndexOf("--"), command0, cancellationToken);
                return;
            }
            switch (args[0])
            {
                case "echo":
                    RunCommand1(args, 1, args.AsSpan().IndexOf("--"), command1, cancellationToken);
                    break;
                case "sum":
                    RunCommand2(args, 1, args.AsSpan().IndexOf("--"), command2, cancellationToken);
                    break;
                default:
                    RunCommand0(args, 0, args.AsSpan().IndexOf("--"), command0, cancellationToken);
                    break;
            }
        }

        private static void RunCommand0(string[] args, int commandDepth, int escapeIndex, Action<string> command, CancellationToken __ExternalCancellationToken__)
        {
            var commandArgs = (escapeIndex == -1) ? args.AsSpan(commandDepth) : args.AsSpan(commandDepth, escapeIndex - commandDepth);
            if (TryShowHelpOrVersion(commandArgs, 1, 0)) return;

            var arg0 = default(string);
            var arg0Parsed = false;

            try
            {
                for (int i = 0; i < commandArgs.Length; i++)
                {
                    var name = commandArgs&#91;i&#93;;

                    switch (name)
                    {
                        case "--msg":
                        {
                            if (!TryIncrementIndex(ref i, commandArgs.Length)) { ThrowArgumentParseFailed("msg", commandArgs&#91;i&#93;); } else { arg0 = commandArgs&#91;i&#93;; }
                            arg0Parsed = true;
                            break;
                        }
                        default:
                            if (string.Equals(name, "--msg", StringComparison.OrdinalIgnoreCase))
                            {
                                if (!TryIncrementIndex(ref i, commandArgs.Length)) { ThrowArgumentParseFailed("msg", commandArgs&#91;i&#93;); } else { arg0 = commandArgs&#91;i&#93;; }
                                arg0Parsed = true;
                                break;
                            }
                            ThrowArgumentNameNotFound(name);
                            break;
                    }
                }
                if (!arg0Parsed) ThrowRequiredArgumentNotParsed("msg");

                command(arg0!);
            }
            catch (Exception ex)
            {
                Environment.ExitCode = 1;
                if (ex is ValidationException or ArgumentParseFailedException)
                {
                    LogError(ex.Message);
                }
                else
                {
                    LogError(ex.ToString());
                }
            }
        }
        private static void RunCommand1(string&#91;&#93; args, int commandDepth, int escapeIndex, Action<string> command, CancellationToken __ExternalCancellationToken__)
        {
            var commandArgs = (escapeIndex == -1) ? args.AsSpan(commandDepth) : args.AsSpan(commandDepth, escapeIndex - commandDepth);
            if (TryShowHelpOrVersion(commandArgs, 1, 1)) return;

            var arg0 = default(string);
            var arg0Parsed = false;

            try
            {
                for (int i = 0; i < commandArgs.Length; i++)
                {
                    var name = commandArgs&#91;i&#93;;

                    switch (name)
                    {
                        case "--msg":
                        {
                            if (!TryIncrementIndex(ref i, commandArgs.Length)) { ThrowArgumentParseFailed("msg", commandArgs&#91;i&#93;); } else { arg0 = commandArgs&#91;i&#93;; }
                            arg0Parsed = true;
                            break;
                        }
                        default:
                            if (string.Equals(name, "--msg", StringComparison.OrdinalIgnoreCase))
                            {
                                if (!TryIncrementIndex(ref i, commandArgs.Length)) { ThrowArgumentParseFailed("msg", commandArgs&#91;i&#93;); } else { arg0 = commandArgs&#91;i&#93;; }
                                arg0Parsed = true;
                                break;
                            }
                            ThrowArgumentNameNotFound(name);
                            break;
                    }
                }
                if (!arg0Parsed) ThrowRequiredArgumentNotParsed("msg");

                command(arg0!);
            }
            catch (Exception ex)
            {
                Environment.ExitCode = 1;
                if (ex is ValidationException or ArgumentParseFailedException)
                {
                    LogError(ex.Message);
                }
                else
                {
                    LogError(ex.ToString());
                }
            }
        }
        private static void RunCommand2(string&#91;&#93; args, int commandDepth, int escapeIndex, Action<int, int> command, CancellationToken __ExternalCancellationToken__)
        {
            var commandArgs = (escapeIndex == -1) ? args.AsSpan(commandDepth) : args.AsSpan(commandDepth, escapeIndex - commandDepth);
            if (TryShowHelpOrVersion(commandArgs, 2, 2)) return;

            var arg0 = default(int);
            var arg0Parsed = false;
            var arg1 = default(int);
            var arg1Parsed = false;

            try
            {
                for (int i = 0; i < commandArgs.Length; i++)
                {
                    var name = commandArgs&#91;i&#93;;

                    switch (name)
                    {
                        case "--x":
                        {
                            if (!TryIncrementIndex(ref i, commandArgs.Length) || !int.TryParse(commandArgs&#91;i&#93;, out arg0)) { ThrowArgumentParseFailed("x", commandArgs&#91;i&#93;); }
                            arg0Parsed = true;
                            break;
                        }
                        case "--y":
                        {
                            if (!TryIncrementIndex(ref i, commandArgs.Length) || !int.TryParse(commandArgs&#91;i&#93;, out arg1)) { ThrowArgumentParseFailed("y", commandArgs&#91;i&#93;); }
                            arg1Parsed = true;
                            break;
                        }
                        default:
                            if (string.Equals(name, "--x", StringComparison.OrdinalIgnoreCase))
                            {
                                if (!TryIncrementIndex(ref i, commandArgs.Length) || !int.TryParse(commandArgs&#91;i&#93;, out arg0)) { ThrowArgumentParseFailed("x", commandArgs&#91;i&#93;); }
                                arg0Parsed = true;
                                break;
                            }
                            if (string.Equals(name, "--y", StringComparison.OrdinalIgnoreCase))
                            {
                                if (!TryIncrementIndex(ref i, commandArgs.Length) || !int.TryParse(commandArgs&#91;i&#93;, out arg1)) { ThrowArgumentParseFailed("y", commandArgs&#91;i&#93;); }
                                arg1Parsed = true;
                                break;
                            }
                            ThrowArgumentNameNotFound(name);
                            break;
                    }
                }
                if (!arg0Parsed) ThrowRequiredArgumentNotParsed("x");
                if (!arg1Parsed) ThrowRequiredArgumentNotParsed("y");

                command(arg0!, arg1!);
            }
            catch (Exception ex)
            {
                Environment.ExitCode = 1;
                if (ex is ValidationException or ArgumentParseFailedException)
                {
                    LogError(ex.Message);
                }
                else
                {
                    LogError(ex.ToString());
                }
            }
        }
    }
}

&#91;/code&#93;

&#91;code lang="csharp"&#93;
// <auto-generated/>
#nullable enable
#pragma warning disable

namespace ConsoleAppFramework;

using System;
using System.Text;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Diagnostics.CodeAnalysis;
using System.ComponentModel.DataAnnotations;

internal static partial class ConsoleApp
{
    internal partial class ConsoleAppBuilder
    {
        static partial void ShowHelp(int helpId)
        {
            switch (helpId)
            {
                case 0:
                    Log("""
Usage: [options...] [-h|--help] [--version]

Options:
  --msg <string>     (Required)
""");
                    break;
                case 1:
                    Log("""
Usage: echo [options...] [-h|--help] [--version]

Options:
  --msg <string>     (Required)
""");
                    break;
                case 2:
                    Log("""
Usage: sum [options...] [-h|--help] [--version]

Options:
  --x <int>     (Required)
  --y <int>     (Required)
""");
                    break;
                default:
                    Log("""
Usage: [command] [options...] [-h|--help] [--version]

Options:
  --msg <string>     (Required)

Commands:
  echo
  sum
""");
                    break;
            }
        }
    }
}

// <auto-generated/>
#nullable enable
#pragma warning disable

namespace ConsoleAppFramework;

using System;
using System.Text;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Diagnostics.CodeAnalysis;
using System.ComponentModel.DataAnnotations;

internal static partial class ConsoleApp
{
    internal partial class ConsoleAppBuilder
    {
        public void Run(string[] args) => Run(args, true);
        public void Run(string[] args, CancellationToken cancellationToken) => Run(args, true, cancellationToken);

        public void Run(string[] args, bool disposeServiceProvider, CancellationToken cancellationToken = default)
        {
            BuildAndSetServiceProvider();
            try
            {
                RunCore(args, cancellationToken);
            }
            finally
            {
                if (disposeServiceProvider)
                {
                    if (ServiceProvider is IDisposable d)
                    {
                        d.Dispose();
                    }
                }
            }
        }

        public Task RunAsync(string[] args) => RunAsync(args, true);
        public Task RunAsync(string[] args, CancellationToken cancellationToken) => RunAsync(args, true, cancellationToken);

        public async Task RunAsync(string[] args, bool disposeServiceProvider, CancellationToken cancellationToken = default)
        {
            BuildAndSetServiceProvider();
            try
            {
                Task? task = null;
                RunAsyncCore(args, cancellationToken, ref task!);
                if (task != null)
                {
                    await task;
                }
            }
            finally
            {
                if (disposeServiceProvider)
                {
                    if (ServiceProvider is IAsyncDisposable ad)
                    {
                        await ad.DisposeAsync();
                    }
                    else if (ServiceProvider is IDisposable d)
                    {
                        d.Dispose();
                    }
                }
            }
        }
    }
}
// <auto-generated/>
#nullable enable
#pragma warning disable

namespace ConsoleAppFramework;

using System;
using System.Text;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Diagnostics.CodeAnalysis;
using System.ComponentModel.DataAnnotations;

#if !USE_EXTERNAL_CONSOLEAPP_ABSTRACTIONS

internal interface IArgumentParser<T>
{
    static abstract bool TryParse(ReadOnlySpan<char> s, out T result);
}

internal record ConsoleAppContext
{
    public string CommandName { get; init; }
    public string[] Arguments { get; init; }
    public object? State { get; init; }
    internal int CommandDepth { get; }
    internal int EscapeIndex  { get; }

    public ReadOnlySpan<string> CommandArguments
    {
        get => (EscapeIndex == -1)
            ? Arguments.AsSpan(CommandDepth)
            : Arguments.AsSpan(CommandDepth, EscapeIndex - CommandDepth);
    }

    public ReadOnlySpan<string> EscapedArguments
    {
        get => (EscapeIndex == -1)
            ? Array.Empty<string>()
            : Arguments.AsSpan(EscapeIndex + 1);
    }

    public ConsoleAppContext(string commandName, string[] arguments, object? state, int commandDepth, int escapeIndex)
    {
        this.CommandName = commandName;
        this.Arguments = arguments;
        this.State = state;
        this.CommandDepth = commandDepth;
        this.EscapeIndex = escapeIndex;
    }

    public override string ToString()
    {
        return string.Join(" ", Arguments);
    }
}

internal abstract class ConsoleAppFilter(ConsoleAppFilter next)
{
    protected readonly ConsoleAppFilter Next = next;

    public abstract Task InvokeAsync(ConsoleAppContext context, CancellationToken cancellationToken);
}

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
internal sealed class ConsoleAppFilterAttribute<T> : Attribute
    where T : ConsoleAppFilter
{
}

internal sealed class ArgumentParseFailedException(string message) : Exception(message)
{
}

#endif

[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
internal sealed class FromServicesAttribute : Attribute
{
}

[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
internal sealed class ArgumentAttribute : Attribute
{
}

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
internal sealed class CommandAttribute : Attribute
{
    public string Command { get; }

    public CommandAttribute(string command)
    {
        this.Command = command;
    }
}

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
internal sealed class HiddenAttribute : Attribute
{
}

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
internal sealed class RegisterCommandsAttribute : Attribute
{
    public string CommandPath { get; }

    public RegisterCommandsAttribute()
    {
        this.CommandPath = "";
    }

    public RegisterCommandsAttribute(string commandPath)
    {
        this.CommandPath = commandPath;
    }
}

[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false, Inherited = false)]
public class ConsoleAppFrameworkGeneratorOptionsAttribute : Attribute
{
    public bool DisableNamingConversion { get; set; }
}

[UnconditionalSuppressMessage("Trimming", "IL2026")]
[UnconditionalSuppressMessage("AOT", "IL3050")]
internal static partial class ConsoleApp
{
    public static IServiceProvider? ServiceProvider { get; set; }
    public static TimeSpan Timeout { get; set; } = TimeSpan.FromSeconds(5);
    public static System.Text.Json.JsonSerializerOptions? JsonSerializerOptions { get; set; }
    public static string? Version { get; set; }

    static Action<string>? logAction;
    public static Action<string> Log
    {
        get => logAction ??= Console.WriteLine;
        set => logAction = value;
    }

    static Action<string>? logErrorAction;
    public static Action<string> LogError
    {
        get => logErrorAction ??= (static msg => Log(msg));
        set => logErrorAction = value;
    }

    /// <summary>
    /// <para>You can pass second argument that generates new Run overload.</para>
    /// ConsoleApp.Run(args, (int x, int y) => { });<br/>
    /// ConsoleApp.Run(args, Foo);<br/>
    /// ConsoleApp.Run(args, &amp;Foo);<br/>
    /// </summary>
    public static void Run(string[] args)
    {
    }

    /// <summary>
    /// <para>You can pass second argument that generates new RunAsync overload.</para>
    /// ConsoleApp.RunAsync(args, (int x, int y) => { });<br/>
    /// ConsoleApp.RunAsync(args, Foo);<br/>
    /// ConsoleApp.RunAsync(args, &amp;Foo);<br/>
    /// </summary>
    public static Task RunAsync(string[] args)
    {
        return Task.CompletedTask;
    }

    public static ConsoleAppBuilder Create() => new ConsoleAppBuilder();

    static void ThrowArgumentParseFailed(string argumentName, string value)
    {
        throw new ArgumentParseFailedException($"Argument '{argumentName}' failed to parse, provided value: {value}");
    }

    static void ThrowRequiredArgumentNotParsed(string name)
    {
        throw new ArgumentParseFailedException($"Required argument '{name}' was not specified.");
    }

    static void ThrowArgumentNameNotFound(string argumentName)
    {
        throw new ArgumentParseFailedException($"Argument '{argumentName}' is not recognized.");
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    static bool TryIncrementIndex(ref int index, int length)
    {
        if ((index + 1) < length)
        {
            index += 1;
            return true;
        }
        return false;
    }

    static bool TryParseParamsArray<T>(ReadOnlySpan<string> args, ref T[] result, ref int i)
       where T : IParsable<T>
    {
        result = new T[args.Length - i];
        var resultIndex = 0;
        for (; i < args.Length; i++)
        {
            if (!T.TryParse(args&#91;i&#93;, null, out result&#91;resultIndex++&#93;!)) return false;
        }
        return true;
    }

    static bool TrySplitParse<T>(ReadOnlySpan<char> s, out T[] result)
       where T : ISpanParsable<T>
    {
        if (s.StartsWith("["))
        {
            try
            {
                result = System.Text.Json.JsonSerializer.Deserialize<T&#91;&#93;>(s, JsonSerializerOptions)!;
                return true;
            }
            catch
            {
                result = default!;
                return false;
            }
        }

        var count = s.Count(',') + 1;
        result = new T[count];

        var source = s;
        var destination = result.AsSpan();
        Span<Range> ranges = stackalloc Range[Math.Min(count, 128)];

        while (true)
        {
            var splitCount = source.Split(ranges, ',');
            var parseTo = splitCount;
            if (splitCount == 128 && source[ranges[^1]].Contains(','))
            {
                parseTo = splitCount - 1;
            }

            for (int i = 0; i < parseTo; i++)
            {
                if (!T.TryParse(source&#91;ranges&#91;i&#93;&#93;, null, out destination&#91;i&#93;!))
                {
                    return false;
                }
            }
            destination = destination.Slice(parseTo);

            if (destination.Length != 0)
            {
                source = source&#91;ranges&#91;^1&#93;&#93;;
                continue;
            }
            else
            {
                break;
            }
        }

        return true;
    }

    static void ValidateParameter(object? value, ParameterInfo parameter, ValidationContext validationContext, ref StringBuilder? errorMessages)
    {
        validationContext.DisplayName = parameter.Name ?? "";
        validationContext.Items.Clear();

        foreach (var validator in parameter.GetCustomAttributes<ValidationAttribute>(false))
        {
            var result = validator.GetValidationResult(value, validationContext);
            if (result != null)
            {
                if (errorMessages == null)
                {
                    errorMessages = new StringBuilder();
                }
                errorMessages.AppendLine(result.ErrorMessage);
            }
        }
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    static bool TryShowHelpOrVersion(ReadOnlySpan<string> args, int requiredParameterCount, int helpId)
    {
        if (args.Length == 0)
        {
            if (requiredParameterCount == 0) return false;

            ShowHelp(helpId);
            return true;
        }

        if (args.Length == 1)
        {
            switch (args[0])
            {
                case "--version":
                    ShowVersion();
                    return true;
                case "-h":
                case "--help":
                    ShowHelp(helpId);
                    return true;
                default:
                    break;
            }
        }

        return false;
    }

    static void ShowVersion()
    {
        if (Version != null)
        {
            Log(Version);
            return;
        }

        var asm = Assembly.GetEntryAssembly();
        var version = "1.0.0";
        var infoVersion = asm!.GetCustomAttribute<AssemblyInformationalVersionAttribute>();
        if (infoVersion != null)
        {
            version = infoVersion.InformationalVersion;
            var i = version.IndexOf('+');
            if (i != -1)
            {
                version = version.Substring(0, i);
            }
        }
        else
        {
            var asmVersion = asm!.GetCustomAttribute<AssemblyVersionAttribute>();
            if (asmVersion != null)
            {
                version = asmVersion.Version;
            }
        }
        Log(version);
    }

    static partial void ShowHelp(int helpId);

    static async Task RunWithFilterAsync(string commandName, string[] args, int commandDepth, int escapeIndex, ConsoleAppFilter invoker, CancellationToken cancellationToken)
    {
        using var posixSignalHandler = PosixSignalHandler.Register(Timeout, cancellationToken);
        try
        {
            await Task.Run(() => invoker.InvokeAsync(new ConsoleAppContext(commandName, args, null, commandDepth, escapeIndex), posixSignalHandler.Token)).WaitAsync(posixSignalHandler.TimeoutToken);
        }
        catch (Exception ex)
        {
            if (ex is OperationCanceledException)
            {
                Environment.ExitCode = 130;
                return;
            }

            Environment.ExitCode = 1;
            if (ex is ValidationException or ArgumentParseFailedException)
            {
                LogError(ex.Message);
            }
            else
            {
                LogError(ex.ToString());
            }
        }
    }

    sealed class PosixSignalHandler : IDisposable
    {
        public CancellationToken Token => cancellationTokenSource.Token;
        public CancellationToken TimeoutToken => timeoutCancellationTokenSource.Token;

        CancellationTokenSource cancellationTokenSource;
        CancellationTokenSource timeoutCancellationTokenSource;
        TimeSpan timeout;

        PosixSignalRegistration? sigInt;
        PosixSignalRegistration? sigQuit;
        PosixSignalRegistration? sigTerm;

        PosixSignalHandler(TimeSpan timeout, CancellationToken cancellationToken)
        {
            this.cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
            this.timeoutCancellationTokenSource = new CancellationTokenSource();
            this.timeout = timeout;
        }

        public static PosixSignalHandler Register(TimeSpan timeout, CancellationToken cancellationToken)
        {
            var handler = new PosixSignalHandler(timeout, cancellationToken);

            Action<PosixSignalContext> handleSignal = handler.HandlePosixSignal;

            handler.sigInt = PosixSignalRegistration.Create(PosixSignal.SIGINT, handleSignal);
            handler.sigQuit = PosixSignalRegistration.Create(PosixSignal.SIGQUIT, handleSignal);
            handler.sigTerm = PosixSignalRegistration.Create(PosixSignal.SIGTERM, handleSignal);

            return handler;
        }

        void HandlePosixSignal(PosixSignalContext context)
        {
            context.Cancel = true;
            cancellationTokenSource.Cancel();
            timeoutCancellationTokenSource.CancelAfter(timeout);
        }

        public void Dispose()
        {
            sigInt?.Dispose();
            sigQuit?.Dispose();
            sigTerm?.Dispose();
            cancellationTokenSource.Dispose();
            timeoutCancellationTokenSource.Dispose();
        }
    }

    struct SyncAsyncDisposeWrapper<T>(T value) : IDisposable
        where T : IAsyncDisposable
    {
        public readonly T Value => value;

        public void Dispose()
        {
            value.DisposeAsync().AsTask().GetAwaiter().GetResult();
        }
    }

    internal partial class ConsoleAppBuilder
    {
        public ConsoleAppBuilder()
        {
        }

        public void Add(string commandName, Delegate command)
        {
            AddCore(commandName, command);
        }

        [System.Diagnostics.Conditional("DEBUG")]
        public void Add<T>() { }

        [System.Diagnostics.Conditional("DEBUG")]
        public void Add<T>(string commandPath) { }

        [System.Diagnostics.Conditional("DEBUG")]
        public void UseFilter<T>() where T : ConsoleAppFilter { }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        partial void AddCore(string commandName, Delegate command);

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        partial void RunCore(string[] args, CancellationToken cancellationToken);

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        partial void RunAsyncCore(string[] args, CancellationToken cancellationToken, ref Task result);

        partial void BuildAndSetServiceProvider();

        static partial void ShowHelp(int helpId);

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        static bool TryShowHelpOrVersion(ReadOnlySpan<string> args, int requiredParameterCount, int helpId)
        {
            if (args.Length == 0)
            {
                if (requiredParameterCount == 0) return false;

                ShowHelp(helpId);
                return true;
            }

            if (args.Length == 1)
            {
                switch (args[0])
                {
                    case "--version":
                        ShowVersion();
                        return true;
                    case "-h":
                    case "--help":
                        ShowHelp(helpId);
                        return true;
                    default:
                        break;
                }
            }

            return false;
        }
    }
}

Code and pdf at

https://ignatandrei.github.io/RSCG_Examples/v2/docs/ConsoleAppFramework


Posted

in

, ,

by

Tags: