.NET System.CommandLine

# 概要
https://learn.microsoft.com/ja-jp/dotnet/standard/commandline/

> System.CommandLine ライブラリには、コマンドライン入力の解析やヘルプ テキストの表示など、コマンドライン アプリで一般的に必要とされる機能が用意されています。

:::note warn
System.CommandLineパッケージはプレリリース版なので、ご利用は計画的に。
:::

やってみよう。

# NuGet
https://www.nuget.org/packages/System.CommandLine

# 基本

```csharp:Program.cs
    // ルートコマンドを設定
    var root = new RootCommand("コマンド全体の説明");

    // ルートコマンドの動作を設定
    root.SetHandler(() => System.Console.WriteLine("実行されました。"));

    // 実行
    await root.InvokeAsync(args);
```

実行してみます。
```console
> TestCommandLine
実行されました。
```

ヘルプを表示します。
```console
> TestCommandLine --help
Description:
  コマンド全体の説明

Usage:
  TestCommandLine [options]

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

バージョンを表示します。
```console
> TestCommandLine --version
1.0.0
```

# 応用

```csharp:Program.cs

    // ルートコマンドを設定
    var root = new RootCommand("コマンド全体の説明");

    // グローバルオプションに--verboseオプション(値なし)を設定
    var verboseOption = new Option<bool>("--verbose", "verboseオプションの説明");
    verboseOption.AddAlias("-v");
    verboseOption.Arity = ArgumentArity.Zero;
    root.AddGlobalOption(verboseOption);

    // コマンド1
    var cmd1 = new Command("cmd1", "コマンド1の説明");
    root.Add(cmd1);

    // コマンド1の引数(省略可)
    var cmd1arg1 = new Argument<string>("arg1", "コマンド1-引数1 の説明");
    cmd1arg1.SetDefaultValue("arg1Value");
    cmd1.AddArgument(cmd1arg1);

    // コマンド1のオプション(省略可)
    var cmd1op1 = new Option<string>("--op1", "コマンド1-オプション1 の説明");
    cmd1op1.SetDefaultValue("op1Value");
    cmd1.AddOption(cmd1op1);

    // コマンド１の動作
    cmd1.SetHandler((cmd1arg1Value, cmd1op1Value, verboseOptionValue) =>
    {
        Console.WriteLine($"cmd1 arg1 = {cmd1arg1Value} --op1 = {cmd1op1Value} --verbose = {verboseOptionValue}");
    }, cmd1arg1, cmd1op1, verboseOption);

    // コマンド2
    var cmd2 = new Command("cmd2", "コマンド2の説明");
    root.Add(cmd2);

    // コマンド２の引数(必須)
    var cmd2arg1 = new Argument<FileInfo>("arg1", "コマンド2-引数1 の説明");
    cmd2.AddArgument(cmd2arg1);

    // コマンド２の動作
    cmd2.SetHandler((file, verboseOptionValue) =>
    {
        Console.WriteLine($"cmd2 arg1 = {file.FullName} --verbose = {verboseOptionValue}");
    }, cmd2arg1, verboseOption);

    // 実行
    await root.InvokeAsync(args);
```

全体のヘルプ
```console
> TestCommandLine --help
Description:
  コマンド全体の説明

Usage:
  TestCommandLine [command] [options]

Options:
  -v, --verbose   verboseオプションの説明
  --version       Show version information
  -?, -h, --help  Show help and usage information

Commands:
  cmd1 <arg1>  コマンド1の説明 [default: arg1Value]
  cmd2 <arg1>  コマンド2の説明
```

cmd1のヘルプ
```console
> TestCommandLine cmd1 --help
Description:
  コマンド1の説明

Usage:
  TestCommandLine cmd1 [<arg1>] [options]

Arguments:
  <arg1>  コマンド1-引数1 の説明 [default: arg1Value]

Options:
  --op1 <op1>     コマンド1-オプション1 の説明 [default: op1Value]
  -v, --verbose   verboseオプションの説明
  -?, -h, --help  Show help and usage information
```

cmd1を実行 (引数・オプションなし)
```console
> TestCommandLine cmd1
cmd1 arg1 = arg1Value --op1 = op1Value --verbose = False
```

cmd1を実行 (引数・オプションあり)
```console
> TestCommandLine cmd1 abc --op1 def -v
cmd1 arg1 = abc --op1 = def --verbose = True
```

cmd2のヘルプ
```console
> TestCommandLine cmd2 --help
Description:
  コマンド2の説明

Usage:
  TestCommandLine cmd2 <arg1> [options]

Arguments:
  <arg1>  コマンド2-引数1 の説明

Options:
  -v, --verbose   verboseオプションの説明
  -?, -h, --help  Show help and usage information
```

cmd2の実行(引数なし) 
```console
>TestCommandLine cmd2
Required argument missing for command: 'cmd2'.
```

cmd2の実行(引数あり) 
```console
C:\temp> TestCommandLine cmd2 hoge.txt
cmd2 arg1 = C:\temp\hoge.txt --verbose = False
```

# 終了コードを返したい
```csharp:Program.cs
    cmd2.SetHandler((file, verboseOptionValue) =>
    {
        bool result = true;

        // ～省略～
      
        //コマンドの戻り値
        return Task.FromResult(result ? 0 : 1);
    }, cmd2arg1, verboseOption);

    // 実行
    return await root.InvokeAsync(args);
```

# 解析だけしたい

```csharp:Program.cs
    // 実行
    //await root.InvokeAsync(args);
    
    var result = root.Parse(args);
    if(result.Errors.Count > 0)
    {
        Console.WriteLine(result.Errors.Select(r => r.Message).Aggregate((r1, r2) => $"{r1}\r\n{r2}"));
    }
    else if (result.CommandResult.Command == cmd1)
    {
        Console.WriteLine(result.CommandResult.GetValueForArgument(cmd1arg1));
        Console.WriteLine(result.CommandResult.GetValueForOption(cmd1op1));
        Console.WriteLine(result.CommandResult.GetValueForOption(verboseOption));
    }
    else if (result.CommandResult.Command == cmd2)
    {
        Console.WriteLine(result.CommandResult.GetValueForArgument(cmd2arg1));
        Console.WriteLine(result.CommandResult.GetValueForOption(verboseOption));
    }

```

# DI
https://learn.microsoft.com/ja-jp/dotnet/standard/commandline/dependency-injection

# タブ補完
https://learn.microsoft.com/ja-jp/dotnet/standard/commandline/tab-completion