• Stars
    star
    3,209
  • Rank 13,993 (Top 0.3 %)
  • Language
    C#
  • License
    MIT License
  • Created almost 5 years ago
  • Updated 3 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Micro-framework for .NET console application. Cocona makes it easy and fast to build console applications on .NET.

Cocona

Micro-framework for .NET Core console application. Cocona makes it easy and fast to build console applications on .NET.🚀

Build Status NuGet Package: Cocona NuGet Package: Cocona.Lite

Create a console application with Cocona in seconds.

CoconaApp.Run((string? name, bool hey) =>
    Console.WriteLine($"{(hey ? "Hey" :"Hello")} {(name ?? "Guest")}!"));

Feature

  • 🚀 Make it easy to build console applications on .NET.
    • ASP.NET Core-like Minimal API
    • public method as a command
    • Provides ASP.NET Core MVC-like development experience to console application development.
  • Command-line option semantics like UNIX tools standard. (getopt/getopt_long like options)
    • Your app can handle both -rf / and -r -f / :-)
    • Support single command and multiple commands style
      • myapp --foo --bar -n arg0 "arg1" (e.g. dir, cp, ls ...)
      • myapp server -m "Hello world!" (e.g. dotnet, git, kubectl ...)
  • Built-in help documentation support.
    • You want to see a help message; you type -h or --help.
    • Built-in similar commands suggestion
    • Shell command-line completion support for bash and zsh
  • 🛠 Highly modulable/customizable CLI framework.

You can find sample code for various features.

Table of contents

Installing

Install NuGet package from NuGet.org

$ dotnet add package Cocona

# A lightweight version is also available if you prefer less dependency.
$ dotnet add package Cocona.Lite

Requirements

  • .NET 6 (Required to use Minimal API)
  • .NET 5
  • .NET Standard 2.0, 2.1

Getting Started

using Cocona;
CoconaApp.Run((string name) =>
{
    Console.WriteLine($"Hello {name}");
})
Class-based style (for .NET Standard / .NET 5)
using Cocona;
class Program
{
    static void Main(string[] args)
    {
        // Cocona parses command-line and executes a command.
        CoconaApp.Run<Program>(args);
    }

    // public method as a command ™
    public void Hello(string name)
    {
        Console.WriteLine($"Hello {name}");
    }
}

Try to run!

$ dotnet run
Usage: ConsoleAppSample [--name <String>]

Options:
  --name <String>    (Required)
  -h, --help         Show help message
  --version          Show version

$ dotnet run -- --name Cocona
Hello Cocona

Extra: Publish the application as a single-file executable

If your application runs on .NET Core 3.0 or later, you can publish the app as a single-file executable. (see. What's new in .NET Core 3.0)

PS> dotnet publish -r win-x64 -p:PublishSingleFile=true
PS> app.exe --name Cocona

$ dotnet publish -r linux-x64 -p:PublishSingleFile=true
$ ./app --name Cocona

Command-line handling basics

Command

Minimal API style

If your application has a single command, you can easily define and run it with CoconaApp.Run.

CoconaApp.Run((string name, int age) => { ... });

This is equivalent to the following code using the Minimal API Builder.

var builder = CoconaApp.CreateBuilder();
var app = builder.Build();

app.AddCommand((string name, int age) => { ... });

app.Run();

If you want your application to have more than one command, you can add named commands. See Sub commands for details.

var app = CoconaApp.Create(); // is a shorthand for `CoconaApp.CreateBuilder().Build()`

app.AddCommand("list", () => { ... });
app.AddCommand("add", () => { ... });
app.AddCommand("delete", () => { ... });

app.Run();

You can add (classic) Class-based style commands with the AddCommands<T> method.

app.AddCommands<MyCommand>();

Public method as a command (Class-based style)

By default, Cocona treats public methods as commands.

If an application has one public method, Cocona calls it on startup. If there are more than one, they are treated as sub-commands. (see also Sub commands)

// Treats a method name as a command name. (Below method is named `command`)
public void Command() { ... }

// Specify a command name using CommandAttribute.
[Command("commandname")]
public void Command() { ... }

// Cocona will ignore this method.
[Ignore]
public void Ignored() { ... }

If you want to specify a method as a command manually, set false to TreatPublicMethodsAsCommands option at startup. All command methods require CommandAttribute.

CoconaApp.Run<Program>(args, options =>
{
    // If the option value is `false`, All command methods require `CommandAttribute`.
    options.TreatPublicMethodsAsCommands = false;
});

Options

Cocona exposes method parameters as command-line options (also known as flags).

// This command accepts `--name <string>` and `--hey` options.
app.AddCommand((string name, bool hey) => { ... });
Class-based style (for .NET Standard / .NET 5)
// This command accepts `--name <string>` and `--hey` options.
public void Hello(string name, bool hey) { ... }

If the parameter of a method is defined as nullable, Cocona will treat them as non-mandatory option for a command. (That is, the parameters are treated as required option by default excepts boolean). If a parameter is boolean, it's assumed that false default value is specified.

// `--name` is non-mandatory option.
// If the user runs the application without this option, the parameter will be `null`.
app.AddCommand((string? name) => { ... });
Optional with default value (Class-based style)

If method parameters are optional argument, Cocona treats those as optional command options. (That is, the parameters are treated as required option by default excepts boolean). If a parameter is boolean, it's assumed that false default value is specified.

// `--name "default user"` is specified implicity.
public void Hello(string name = "default user") { ... }

Do you want to use short-name option -f instead of --force? You can specify short-name to an option using OptionAttribute.

// The command accepts `-f` or `--force` option.
// Cocona's command-line parser accepts getopt-like styles. See below.
// $ remove --force --recursive
// $ remove -r -f
// $ remove -rf
app.AddCommand(([Option('f')]bool force, [Option('r')]bool recursive) => { ... });
Class-based style (for .NET Standard / .NET 5))
// The command accepts `-f` or `--force` option.
// Cocona's command-line parser accepts getopt-like styles. See below.
// $ remove --force --recursive
// $ remove -r -f
// $ remove -rf
public void Remove([Option('f')]bool force, [Option('r')]bool recursive) { ... }

If a parameter is T[] or IEnumerable<T>, a command accepts one or more options by the same name.

// $ compile -I../path/to/foo.h -I/usr/include/bar.h -I/usr/include/baz.h nantoka.c
// include = new [] { "../path/to/foo.h", "/usr/include/bar.h", "/usr/include/baz.h" };
app.AddCommand(([Option('I')]string[] include, [Argument]string file) => { ... });
Class-based style (for .NET Standard / .NET 5))
// $ compile -I../path/to/foo.h -I/usr/include/bar.h -I/usr/include/baz.h nantoka.c
// include = new [] { "../path/to/foo.h", "/usr/include/bar.h", "/usr/include/baz.h" };
public void Compile([Option('I')]string[] include, [Argument]string file) { ... }

You can also specify a description for options that appear in the help.

app.AddCommand((
    [Option(Description = "Description of the option")] int value,
    [Argument(Description = "Description of the argument")]string arg
) => { ... });
Class-based style (for .NET Standard / .NET 5))
public void HasDescription([Option(Description = "Description of the option")] int value, [Argument(Description = "Description of the argument")]string arg) { ... }
Usage: CoconaSample.InAction.CommandOptions has-description [--value <Int32>] [--help] arg

Arguments:
  0: arg    Description of the argument (Required)

Options:
  --value <Int32>    Description of the option (Required)
  -h, --help         Show help message

Arguments

Command-line arguments are defined as method parameters as same as options.

// ./app alice karen
app.AddCommand(([Argument]string from, [Argument]string to) => { ... });
Class-based style (for .NET Standard / .NET 5)
// ./app alice karen
public void Hello([Argument]string from, [Argument]string to) { ... }

You can define a parameter as T[]. It allows defining cp-like command which accepts many file paths and one destination path (cp file1 file2 file3 dest).

// ./copy file1 file2 file3 dest
app.AddCommand(([Argument]string[] src, [Argument]string dest) => { ... });
Class-based style (for .NET Standard / .NET 5)
// ./copy file1 file2 file3 dest
public void Copy([Argument]string[] src, [Argument]string dest) { ... }

Sub-commands

You can add multiple commands with names and expose them as sub-commands. You can implement an application that has sub-commands similar to dotnet, git, kubectl etc...

var app = CoconaApp.Create();
app.AddCommand("hello", ([Argument]string name) => Console.WriteLine($"Hello {name}!"))
    .WithDescription("Say hello");
app.AddCommand("bye", ([Argument]string name) => Console.WriteLine($"Goodbye {name}!"))
    .WithDescription("Say goodbye");
app.Run();
Class-based style (for .NET Standard / .NET 5)

If a command type has more than one public method or [Command], those commands are exposed as sub-commands. You can implement an application that has sub-commands similar to dotnet, git, kubectl etc...

static void Main(string[] args)
{
    CoconaApp.Run<Program>(args);
}

[Command(Description = "Say hello")]
public void Hello([Argument]string name)
{
    Console.WriteLine($"Hello {name}!");
}

[Command(Description = "Say goodbye")]
public void Bye([Argument]string name)
{
    Console.WriteLine($"Goodbye {name}!");
}
$ ./SubCommandApp
Usage: SubCommandApp [command]
Usage: SubCommandApp [--help] [--version]

SubCommandApp

Commands:
  hello    Say hello
  bye      Say goodbye

Options:
  -h, --help    Show help message
  --version     Show version

When a user mistypes a command, Cocona prints command autogenerated suggestions.

$ ./SubCommandApp hell
Error: 'hell' is not a command. See '--help' for usage.

Similar commands:
  hello
Nested sub-commands

Cocona also supports nested sub-commands. Specify the class that has nested sub-commands using AddSubCommand method.

var app = CoconaApp.Create();
// ./myapp info
app.AddCommand("info", () => Console.WriteLine("Show information"));

// ./myapp server [command]
app.AddSubCommand("server", x =>
{
    x.AddCommand("start", () => Console.WriteLine("Start"));
    x.AddCommand("stop", () => Console.WriteLine("Stop"));
})
.WithDescription("Server commands");

// ./myapp client [command]
app.AddSubCommand("client", x =>
{
    x.AddCommand("connect", () => Console.WriteLine("Connect"));
    x.AddCommand("disconnect", () => Console.WriteLine("Disconnect"));
})
.WithDescription("Client commands");

app.Run();
Class-based style (for .NET Standard / .NET 5)

Cocona also supports nested sub-commands. Specify the class that has nested sub-commands using HasSubCommands attribute.

[HasSubCommands(typeof(Server), Description = "Server commands")]
[HasSubCommands(typeof(Client), Description = "Client commands")]
class Program
{
    static void Main(string[] args) => CoconaApp.Run<Program>(args);

    // ./myapp info
    public void Info() => Console.WriteLine("Show information");
}

// ./myapp server [command]
class Server
{
    public void Start() => Console.WriteLine("Start");
    public void Stop() => Console.WriteLine("Stop");
}

// ./myapp client [command]
class Client
{
    public void Connect() => Console.WriteLine("Connect");
    public void Disconnect() => Console.WriteLine("Disconnect");
}
$ ./SubCommandApp
Usage: SubCommandApp [command]
Usage: SubCommandApp [--help] [--version]

SubCommandApp

Commands:
  info
  server    Server commands
  client    Client commands

Options:
  -h, --help    Show help message
  --version     Show version

$ ./SubCommandApp server
Usage: SubCommandApp server [command]
Usage: SubCommandApp server [--help]

SubCommandApp

Commands:
  start
  stop

Options:
  -h, --help    Show help message

PrimaryCommand

var app = CoconaApp.Create();
app.AddCommand((bool foo, string bar) => { ... }); // Primary command

app.AddCommand("hello", () => { ... });
app.AddCommand("goodbye", () => { ... });
app.Run();
Class-based style (for .NET Standard / .NET 5)
[PrimaryCommand]
public void Primary(bool foo, string bar) { ... }

[Command]
public void Hello() { ... }

[Command]
public void Goodbye() { ... }

Option-like commands

The option-like command is a way to achieve an independent command that at first glance, looks like an option in a command.

For example, easy to understand examples like --version and --help. These are the options of a command, but they behave as a command when specified.

var app = CoconaApp.Create();
app.AddCommand(() => Console.WriteLine("Execute"))
    .OptionLikeCommand(x =>
    {
        x.AddCommand("hello", ([Argument]string name) => Console.WriteLine($"Hello {name}!"))
            .WithAliases('f');
    });
app.Run();
Class-based style (for .NET Standard / .NET 5)
[OptionLikeCommand("hello", new[] {'f'}, typeof(Program), nameof(Hello))]
public void Execute()
    => Console.WriteLine("Execute");

private void Hello([Argument]string name)
    => Console.WriteLine($"Hello {name}!");
$ ./myapp
Execute

$ ./myapp --hello Alice
Hello Alice!
Limitations
  • Any previous options or arguments specified by OptionLikeCommand will be ignored.
    • Example: If --foo --bar --optionlikecommand --baz arg0 and --optionlikecommand is an Option-like command, the command will be passed --baz arg0.
  • Arguments are not displayed in help.

Cocona in action

Parameter set

Cocona has a mechanism called Parameter set that defines common parameters for multiple commands. For example, if every command receives a user name, host name, etc., it would be annoying to define them in a method for each command.

A class or record implements the ICommandParameterSet interface and treats it as a Parameter set.

By parameterized constructor (includes record class)

If a class (or record class) has a parameterized constructor, it is treated as part of the definition of a command method.

public record CommonParameters(
    [Option('t', Description = "Specifies the remote host to connect.")]
    string Host,
    [Option('p', Description = "Port to connect to on the remote host.")]
    int Port,
    [Option('u', Description = "Specifies the user to log in as on the remote host.")]
    string User = "root",
    [Option('f', Description = "Perform without user confirmation.")]
    bool Force = false
) : ICommandParameterSet;

public void Add(CommonParameters commonParams, [Argument] string from, [Argument] string to)
    => Console.WriteLine($"Add: {commonParams.User}@{commonParams.Host}:{commonParams.Port} {(commonParams.Force ? " (Force)" : "")}");

public void Update(CommonParameters commonParams, [Option('r', Description = "Traverse recursively to perform.")] bool recursive, [Argument] string path)
    => Console.WriteLine($"Update: {commonParams.User}@{commonParams.Host}:{commonParams.Port} {(commonParams.Force ? " (Force)" : "")}");

By properties (parameter-less constructor)

If a class has a parameter-less constructor, you can mark the public property as Option or Argument.

NOTE: Option defined as a property is treated as required by default. If you want a non-required Option to have a default value, mark it with HasDefaultValue attribute.

public class CommonParameters : ICommandParameterSet
{
    [Option('t', Description = "Specifies the remote host to connect.")]
    public string Host { get; set; }

    [Option('p', Description = "Port to connect to on the remote host.")]
    public int Port { get; set; }

    [Option('u', Description = "Specifies the user to log in as on the remote host.")]
    [HasDefaultValue]
    public string User  { get; set; } = "root";

    [Option('f', Description = "Perform without user confirmation.")]
    public bool Force  { get; set; } = false;
}

public void Add(CommonParameters commonParams, [Argument] string from, [Argument] string to)
    => Console.WriteLine($"Add: {commonParams.User}@{commonParams.Host}:{commonParams.Port} {(commonParams.Force ? " (Force)" : "")}");

public void Update(CommonParameters commonParams, [Option('r', Description = "Traverse recursively to perform.")] bool recursive, [Argument] string path)
    => Console.WriteLine($"Update: {commonParams.User}@{commonParams.Host}:{commonParams.Port} {(commonParams.Force ? " (Force)" : "")}");

Exit code

// Exit Code: 0
public void NoReturn() { }

// Exit Code: 123
public int Return() { return 123; }

// Exit Code: 255
public async Task<int> ReturnAsync() { return 255; }

// Exit Code: -1
public async ValueTask<int> ReturnValueTaskAsync() { return -1; }

// Exit Code: 128
public void Throw() { throw new CommandExitedException(128); }

Validation

Cocona can use attributes to validate options and arguments. It is similar to ASP.NET Core MVC.

.NET BCL (System.ComponentModel.DataAnnotations) has some pre-defined attributes:

  • RangeAttribute
  • MaxLangeAttribute
  • MinLengthAttribute
  • ...

If you want to implement custom validation attribute, it should inherit System.ComponentModel.DataAnnotations.ValidationAttribute attribute.

class Program
{
    static void Main(string[] args)
    {
        CoconaApp.Run<Program>(args);
    }

    public void Run([Range(1, 128)]int width, [Range(1, 128)]int height, [Argument][PathExists]string filePath)
    {
        Console.WriteLine($"Size: {width}x{height}");
        Console.WriteLine($"Path: {filePath}");
    }
}

class PathExistsAttribute : ValidationAttribute
{
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        if (value is string path && (Directory.Exists(path) || Directory.Exists(path)))
        {
            return ValidationResult.Success;
        }
        return new ValidationResult($"The path '{value}' is not found.");
    }
}

Shutdown event handling

app.AddCommand(async (CoconaAppContext ctx) =>
{
    while (!ctx.CancellationToken.IsCancellationRequested)
    {
        await Task.Delay(100);
    }
});
Class-based style (for .NET Standard / .NET 5)
class Program : CoconaConsoleAppBase
{
    ...
    public async Task RunAsync()
    {
        while (!Context.CancellationToken.IsCancellationRequested)
        {
            await Task.Delay(100);
        }
    }
}

Alternatively, you can use ICoconaAppContextAccessor and CoconaAppContext to access CancellationToken.

public async Task RunAsync([FromService]ICoconaAppContextAccessor contextAccessor)
{
    var ctx = contextAccessor.Current ?? throw new InvalidOperationException();
    while (!ctx.CancellationToken.IsCancellationRequested)
    {
        await Task.Delay(100);
    }
}

Command filter

Cocona has filter mechanism like ASP.NET Core's action filter. Filters allow custom processing before or after you run a command.

  • ICommandFilter interface
  • CommandFilterAttribute attribute
  • IFilterProvider interface
  • IFilterMetadata interface
var app = CoconaApp.Create();

// Add a command with command filters.
app.AddCommand(() =>
    {
        Console.WriteLine($"Hello Konnichiwa");
    })
    .WithFilter(new SampleCommandFilter())
    .WithFilter(async (ctx, next) =>
    {
        // You can declare and apply a filter using a delegate.
        return await next(ctx);
    });

// Add a command filter and apply it to commands after this call.
app.UseFilter(new MyFilter());

class SampleCommandFilterAttribute : CommandFilterAttribute
{
    public override async ValueTask<int> OnCommandExecutionAsync(CoconaCommandExecutingContext ctx, CommandExecutionDelegate next)
    {
        Console.WriteLine($"Before Command: {ctx.Command.Name}");
        try
        {
            return await next(ctx);
        }
        finally
        {
            Console.WriteLine($"End Command: {ctx.Command.Name}");
        }
    }
}
Class-based style (for .NET Standard / .NET 5)
class Program
{
    static void Main(string[] args)
    {
        CoconaApp.Run<Program>(args);
    }

    [SampleCommandFilter]
    public void Hello()
    {
        Console.WriteLine($"Hello Konnichiwa");
    }
}

class SampleCommandFilterAttribute : CommandFilterAttribute
{
    public override async ValueTask<int> OnCommandExecutionAsync(CoconaCommandExecutingContext ctx, CommandExecutionDelegate next)
    {
        Console.WriteLine($"Before Command: {ctx.Command.Name}");
        try
        {
            return await next(ctx);
        }
        finally
        {
            Console.WriteLine($"End Command: {ctx.Command.Name}");
        }
    }
}

Dependency Injection

If a constructor has parameters, Cocona injects an instance obtained from IServiceProvider into the parameter.

var builder = CoconaApp.CreateBuilder();
builder.Services.AddTransient<MyService>();

var app = builder.Build();
app.AddCommand((MyService myService) =>
{
    myService.Hello("Hello Konnichiwa!");
});
app.Run();

class MyService
{
    private readonly ILogger _logger;

    public MyService(ILogger<MyService> logger)
    {
        _logger = logger;
    }

    public void Hello(string message)
    {
        _logger.LogInformation(message);
    }
}
Class-based style (for .NET Standard / .NET 5)

If a constructor has parameters, Cocona injects an instance obtained from IServiceProvider into the parameter. Cocona will also inject an instance into the parameter if a command method parameter is marked as [FromService].

class Program
{
    public Program(ILogger<Program> logger)
    {
        logger.LogInformation("Create Instance");
    }

    static void Main(string[] args)
    {
        CoconaApp.Create()
            .ConfigureServices(services =>
            {
                services.AddTransient<MyService>();
            })
            .Run<Program>(args);
    }

    public void Hello([FromService]MyService myService)
    {
        myService.Hello("Hello Konnichiwa!");
    }
}

class MyService
{
    private readonly ILogger _logger;

    public MyService(ILogger<MyService> logger)
    {
        _logger = logger;
    }

    public void Hello(string message)
    {
        _logger.LogInformation(message);
    }
}

Configuration

Logging

var builder = CoconaApp.CreateBuilder();
builder.Logging.AddDebug();

app.AddCommand((ILogger<Program> logger) => logger.LogInformation("Hello Konnichiwa!")));

var app = builder.Build();
app.Run();
Class-based style (for .NET Standard / .NET 5)
class Program : CoconaConsoleAppBase
{
    static void Main(string[] args)
    {
        CoconaApp.Create()
            .ConfigureLogging(logging =>
            {
                logging.AddDebug();
            })
            .Run<Program>(args);
    }

    public async Task RunAsync()
    {
        Context.Logger.LogInformation("Hello Konnichiwa!");
    }
}

Shell command-line completion

Cocona provides support for shell command-line completion (also known as tab completion).

Tab shell completion

Cocona generates a shell script for command-line completion from a command definition and allows users to use command-line completion by loading it. The --completion built-in option is used to specify the name of a shell to generate a script.

$ source <(./myapp --completion bash)
or
% ./myapp --completion zsh > ~/.zsh/functions

Currently, The supported shells are bash and zsh.

This feature is disabled by default, or you can set the EnableShellCompletionSupport option to true if you need it.

It is also possible to dynamically generate command-line completion candidates and to prepare candidates at script generation time. Please see the sample below for more details.

Performance & Cocona.Lite

Microsoft.Extensions.* are powerful but little heavy libraries. If you don't needMicrosoft.Extensions.*, you can use a lightweight version of Cocona. (named Cocona.Lite)

Feature & Limitation

  • Almost the same features and APIs as Cocona (command-line, help, etc.)
  • No Microsoft.Extensions.* dependencies
    • No Logging, DI, Configuration are provided
  • Fewer overheads
  • The minimal Dependency Injection function

Installing & How to use

Just install NuGet package Cocona.Lite instead of Cocona.

$ dotnet add package Cocona.Lite

Then in your source code, use CoconaLiteApp class instead of CoconaApp class.

CoconaLiteApp.Run(() => { ... });
var app = CoconaLiteApp.Create();
app.AddCommand(() => { ... });
app.Run();
static void Main(string[] args)
{
    CoconaLiteApp.Run<Program>(args);
}

Advanced

Localization

Microsoft.Extensions.Localization can be used to localize your application. Please refer to the sample code for details.

// Register Microsoft.Extensions.Localization and ICoconaLocalizer services
// Cocona uses `ICoconaLocalizer` to localize command descriptions.
var builder = CoconaApp.CreateBuilder();
builder.Services.AddLocalization(options =>
{
    options.ResourcesPath = "Resources";
});

// `MicrosoftExtensionLocalizationCoconaLocalizer` is not included in Cocona core library.
builder.Services.TryAddTransient<ICoconaLocalizer, MicrosoftExtensionLocalizationCoconaLocalizer>();

var app = builder.Build();
app.AddCommand("hello", ([Argument(Description = "Name")]string name, IStringLocalizer<Program> localizer) =>
    {
        // Get a localized text from Microsoft.Extensions.Localization.IStringLocalizer (same as ASP.NET Core)
        Console.WriteLine(localizer.GetString("Hello {0}!", name));
    })
    .WithDescription("Say Hello");
app.Run();

Hide command from help

var app = CoconaApp.Create();
app.AddCommand("hello", (string name) =>
    {
        Console.WriteLine("Hello {0}!", name);
    });
app.AddCommand("secret-command", (string name) =>
    {
        Console.WriteLine("🙊");
    })
    .WithMetadata(new HiddenAttribute());
app.Run();

Help customization

CommandMethodForwardedTo attribute

The CommandMethodForwardedTo attribute allows you to specify that the substance of the specified command method is a different method and that the operation should be forwarded. If this attribute is given to a command method, the destination's attribute and its implementation are used. Excepts for the Command and Hidden attributes specified by the method.

For example, it can be used if the command implementation is defined in an external assembly or to call a built-in command (such as help) or compatibility purposes.

[CommandMethodForwardedTo(typeof(BuiltInOptionLikeCommands), nameof(BuiltInOptionLikeCommands.ShowHelp))]
public void MyHelp()
    => throw new NotSupportedException(); // NOTE: The method body and parameters used is BuiltInOptionLikeCommands.ShowHelp.

IgnoreUnknownOptions attribute

Cocona treats unknown options as errors by default. Now, you can set the IgnoreUnknownOptions attribute to ignore unknown options.

GenericHost integration

Cocona can be integrated with GenericHost of Microsoft.Extensions.Hosting. You can register the services with UseCocona extension method.

class Program
{
    static async Task Main(string[] args)
    {
        await Host.CreateDefaultBuilder()
            .ConfigureCocona(args, new[] { typeof(Program) })
            .Build()
            .RunAsync();
    }

    public void Hello()
    {
        Console.WriteLine($"Hello Konnichiwa!");
    }
}

Related projects

License

MIT License

Copyright © 2020-present Mayuki Sawatari <[email protected]>

More Repositories

1

Kurukuru

Terminal Spinner for .NET platform
C#
656
star
2

Rin

Request/response Inspector middleware for ASP.NET Core
C#
643
star
3

Chell

Write scripts with the power of C# and .NET
C#
75
star
4

Beatrice

Everything is a smart home device works with Google Home/Assistant.
C#
66
star
5

WeekRef.NET

WeekRef.NET: 週刊 .NET
JavaScript
59
star
6

PlatformStatusTracker

Tracker for the Web browser platform statuses
C#
41
star
7

ChatworkExtension

ChatWorkを改造します
JavaScript
37
star
8

Qulaly

A library that queries Roslyn's C# syntax tree with CSS selector-like syntax.
C#
30
star
9

Inazuma

Minimal CLR Implementation (MSIL Interpreter) for .NET Framework. Nanodesu!
C#
25
star
10

AppleWirelessKeyboardHelper

C#
22
star
11

Yafp

A toolkit for implementing forward proxy based on ASP.NET Core and YARP.
C#
18
star
12

BraviaControl

Sony BRAVIA remote control API client for .NET
C#
17
star
13

dotnet-sail

Downloads .NET project from Gist, GitHub, Git, or the web and runs it in a container.
C#
14
star
14

Kanae

Kanae - Private Gyazo server written in C# + ASP.NET
JavaScript
12
star
15

Cartelet

.NET Framework向けHTMLパーサー/フィルターライブラリ
C#
11
star
16

SourceGeneratorPlayground

Templates for playing with Source Generator.
C#
10
star
17

Mimosa

Syntax highlighter built with Monaco Editor (Visual Studio Code)
JavaScript
8
star
18

JsonStreamLogger

JSON Stream logger provider implementation for Microsoft.Extensions.Logging.
C#
8
star
19

Pripod

Pripod enables you to easily access Pod information from the .NET app inside a Pod.
C#
8
star
20

AgqrPlayer4Tv

超A&G+をAndroid TVで再生するための雑なアプリ
Kotlin
7
star
21

LetterWriter

Unity向けの日本語テキスト表示ライブラリ
C#
7
star
22

aircvw

Tiarra IRC Log Viewer (Aircvw: えあくぎゅう)
JavaScript
7
star
23

KumoDictionary

IDictionary<TKey, TValue> in the Cloud - Simple NoSQL/KVS wrapper for .NET
C#
6
star
24

Downwrite

Markdown Editor for Metro style apps
JavaScript
5
star
25

SimpleBasicAuthenticationModule

Simple Basic Authentication Module for IIS/ASP.NET
C#
5
star
26

Sample.MonoCecil

C#
5
star
27

Backlog

Backlog API for .NET
C#
4
star
28

MraaSharp

.NET(C#, Mono) binding/wrapper for Intel MRAA library
C#
4
star
29

VistaFace.gadget

JavaScript
4
star
30

KssSharp

A .NET Framework (C#) Implementation of KSS: A methodology for documenting CSS and generating styleguides
C#
3
star
31

Metrica

IRC Client for Metro style apps
JavaScript
3
star
32

Iroha

コンポーネントエディタみたいな何かとASP.NET Web Pagesを吐き出すCMSみたいな何か。
JavaScript
3
star
33

RadioWhip

フィードがないページのフィードを作るアレ
C#
2
star
34

mayuki.github.com

mayuki.github.com
HTML
2
star
35

ThumbSense4Surface

ThumbSense for Surface Pro
C#
2
star
36

SimpleAspNetWebHost

C#
2
star
37

MyPlaceholder

A clone of "placehold.it" / ASP.NET MVC
C#
2
star
38

WatchDotNetGitHubRepos

C#
2
star
39

UniCorefx

.NET 4 BCL の一部 for Unityっぽい
C#
2
star
40

IndentAutoConfigurator

Visual Studio 2012でファイルごとにインデントの設定を自動で切り替える拡張
C#
2
star
41

ViewTgaInGitHub

Enable viewing for TARGA format in GitHub
JavaScript
1
star
42

Samidare

File based blog/page engine for ASP.NET Web Pages
C#
1
star
43

HyperEstraierBinding

HyperEstraier Binding for .NET Framework (C#)
C#
1
star
44

ClassicCgiApp

古き良きCGIアプリっぽいものをASP.NET Coreで再現する
C#
1
star
45

Madosta-20160521-Demo

C#
1
star
46

Mvc4Samples

JavaScript
1
star
47

SassTray

Sass コマンドを実行しつつ通知領域にしまわれるようにするためのアレ。
C#
1
star
48

Cod2013

Community Open Day 2013
JavaScript
1
star
49

DockIconPreviewer

Dock Icon Previewer
1
star