Skip to content

flyingpie/declarative-command-line

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

31 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Declarative Command Line

WTQ CI

Nuget

Attribute-driven layer on top of System.CommandLine to make the most common use cases easier to set up.

Minimalistic Example

A minimal example, using DI to instantiate command objects:

Add NuGet Packages

<ItemGroup>
  <PackageReference Include="DeclarativeCommandLine" Version="2.0.0-g498c5dd86c" />
  <PackageReference Include="DeclarativeCommandLine.Generator" Version="2.0.0-g498c5dd86c" />
  <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.9"/>
</ItemGroup>

Program.cs

using DeclarativeCommandLine;
using Microsoft.Extensions.DependencyInjection;

namespace MyApp;

[Command(Description = "Math commands")]
public class AppRootCommand
{
}

[Command(Description = "Add 2 numbers", Parent = typeof(AppRootCommand))]
public class AddCommand : ICommand
{
    [Option(Required = true)]
    public int ValueA { get; set; }

    [Option(Required = true)]
    public int ValueB { get; set; }

    public void Execute()
    {
        Console.WriteLine($"A={ValueA} + {ValueB} = {ValueA + ValueB}");
    }
}

public static class Program
{
    public static int Main(string[] args)
    {
        var p = new ServiceCollection()
            .AddTransient<AppRootCommand>()
            .AddTransient<AddCommand>()
            .BuildServiceProvider();

        return new CommandBuilder()
            .Build(t => p.GetRequiredService(t))
            .Parse(args)
            .Invoke();
    }
}

Result

$ ./myapp
Required command was not provided.

Description:

Usage:
  myapp [command] [options]

Options:
  -?, -h, --help  Show help and usage information
  --version       Show version information

Commands:
  add
$ ./myapp add
Option '--value-a' is required.
Option '--value-b' is required.

Description:

Usage:
  myapp add [options]

Options:
  --value-a <value-a> (REQUIRED)
  --value-b <value-b> (REQUIRED)
  -?, -h, --help                  Show help and usage information
$ ./myapp add --value-a 20 --value-b 22
A=20 + 22 = 42

Generated

This is what the source generator has written, based on the attribute-annotated classes:

/// <auto-generated/>
using DeclarativeCommandLine;
using System;
using System.CommandLine;

namespace MyApp
{
    public partial class CommandBuilder
    {
        public virtual RootCommand Build(Func<Type, object> serviceProvider)
        {
            var cmd1 = new RootCommand();
            cmd1.Hidden = false;
            // global::MyApp.AddCommand
            {
                var cmd2 = new Command("add");
                cmd1.Add(cmd2);
                cmd2.Hidden = false;
                // Option --value-a
                var opt3 = new Option<Int32>("--value-a");
                {
                    cmd2.Add(opt3);
                    opt3.Description = "";
                    opt3.Hidden = false;
                    opt3.Required = true;
                }
                // Option --value-b
                var opt4 = new Option<Int32>("--value-b");
                {
                    cmd2.Add(opt4);
                    opt4.Description = "";
                    opt4.Hidden = false;
                    opt4.Required = true;
                }
                cmd2.SetAction(async (parseResult, ct) =>
                {
                    var cmd2Inst = (global::MyApp.AddCommand)serviceProvider(typeof(global::MyApp.AddCommand));
                    cmd2Inst.ValueA = parseResult.GetValue(opt3);
                    cmd2Inst.ValueB = parseResult.GetValue(opt4);

                    if (cmd2Inst is IAsyncCommandWithParseResult cmd2001)
                    {
                        await cmd2001.ExecuteAsync(parseResult, ct).ConfigureAwait(false);
                    }
                
                    if (cmd2Inst is IAsyncCommand cmd2002)
                    {
                        await cmd2002.ExecuteAsync(ct).ConfigureAwait(false);
                    }
                
                    if (cmd2Inst is ICommand cmd2003)
                    {
                        cmd2003.Execute();
                    }
                });

            }
            return cmd1;
        }
    }
}

Progress

Command

  • Action
  • Arguments
  • Description
  • Hidden
  • Name
  • Options
  • Subcommands
  • Aliases
  • Completions
  • TreatUnmatchedTokensAsErrors
  • Validators

Arguments

  • Description
  • Name
  • AcceptLegalFileNamesOnly
  • AcceptLegalFilePathsOnly
  • AcceptOnlyFromAmong
  • Arity
  • Completions
  • Default
  • HelpName
  • Hidden
  • Validators

Directives

  • dir.Description
  • dir.Hidden
  • dir.Name

Option

  • opt.Description
  • opt.Name
  • opt.Required
  • opt.AcceptLegalFileNamesOnly
  • opt.AcceptLegalFilePathsOnly
  • opt.AcceptOnlyFromAmong
  • opt.Aliases
  • opt.AllowMultipleArgumentsPerToken
  • opt.Arity
  • opt.Completions
  • opt.DefaultValueFactory
  • opt.HelpName
  • opt.Hidden
  • opt.Recursive
  • opt.Validators

About

Attribute-driven layer on top of System.CommandLine to make the most common use cases easier to set up.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published