「実践Rustプログラミング入門」に入門する(5日目) 

[「実践Rustプログラミング入門」](https://www.shuwasystem.co.jp/book/9784798061702.html)「プログラムをつくる」p.152から。
[2日目](https://qiita.com/kencoba/items/cdc56a3ad1a1c00bc99d)の続き。

# p.152 deriveマクロを使った記述方式

```rust:main.rs
use clap::Clap;

#[derive(Clap, Debug)]
#[clap(
    name = "My RPN program",
    version = "1.0.0",
    author = "kencoba",
    about = "Super awesome sample RPN calculator"
)]
struct Opts {
    /// Sets the level of verbosity
    #[clap(short, long)]
    verbose: bool,

    /// Formulas written in RPN
    #[clap(name = "FILE")]
    formula_file: Option<String>,
}

fn main() {
    let opts = Opts::parse();
    match opts.formula_file {
        Some(file) => println!("File specified: {}", file),
        None => println!("No file specified."),
    }
    println!("Is verbosity specified?: {}", opts.verbose);
}
```

実行する。

```shell
PS samplecli> cargo run
   Compiling samplecli v0.1.0 (samplecli)
    Finished dev [unoptimized + debuginfo] target(s) in 1.58s
     Running `target\debug\samplecli.exe`
No file specified.
Is verbosity specified?: false
PS samplecli> 
```

これが引数なしの動作。formula_fileの指定がない旨のメッセージが出る。

```shell
PS samplecli> cargo run -- -v input.txt
    Finished dev [unoptimized + debuginfo] target(s) in 0.10s                                                  
     Running `target\debug\samplecli.exe -v input.txt`
File specified: input.txt
Is verbosity specified?: true
```

trueが返される。`-v`が`verbose`のオプションなのはデフォルトで組み込まれているということ?

```shell
PS samplecli> cargo run -- -v -d input.txt
    Finished dev [unoptimized + debuginfo] target(s) in 0.11s
     Running `target\debug\samplecli.exe -v -d input.txt`
error: Found argument '-d' which wasn't expected, or isn't valid in this context

If you tried to supply `-d` as a PATTERN use `-- -d`

USAGE:
    samplecli.exe [FLAGS] [FILE]

For more information try --help
error: process didn't exit successfully: `target\debug\samplecli.exe -v -d input.txt` (exit code: 2)
```

`-d`は定義していないわけだし、当然エラーになる。

# p.153 第一引数に数値を与えられるようにする。

NUMBERの指定を追加する。

```rust:main.rs
use clap::Clap;

#[derive(Clap, Debug)]
#[clap(
    name = "My RPN program",
    version = "1.0.0",
    author = "kencoba",
    about = "Super awesome sample RPN calculator"
)]
struct Opts {
    /// Sets the level of verbosity
    #[clap(short, long)]
    verbose: bool,

    /// Number
    #[clap(name = "NUMBER")]
    num: i32,

    /// Formulas written in RPN
    #[clap(name = "FILE")]
    formula_file: Option<String>,
}

fn main() {
    let opts = Opts::parse();
    match opts.formula_file {
        Some(file) => println!("File specified: {}", file),
        None => println!("No file specified."),
    }
    println!("Is verbosity specified?: {}", opts.verbose);
}
```

引数を`-v input.txt`として実行する。

```shell
PS samplecli> cargo run -- -v input.txt
   Compiling samplecli v0.1.0 (samplecli)
    Finished dev [unoptimized + debuginfo] target(s) in 1.17s
     Running `target\debug\samplecli.exe -v input.txt`       
error: Invalid value for '<NUMBER>': invalid digit found in stringerror: process didn't exit successfully: `target\debug\samplecli.exe -v input.txt` (exit code: 2)
```

最初の引数が数値、ということなのでエラー。

```shell
PS samplecli> cargo run -- 42 -v input.txt
    Finished dev [unoptimized + debuginfo] target(s) in 0.09s
     Running `target\debug\samplecli.exe 42 -v input.txt`
File specified: input.txt
Is verbosity specified?: true
```

先頭が数値であればOKとなる。

# p.154 デフォルト値を指定する。

こんな書き方をしたら・・・

```rust:main.rs(一部)
    /// Formulas written in RPN
    #[clap(name = "FILE", default_value = "default.txt")]
    formula_file: Option<String>,
```

`cargo run`で、こんなエラーが出た。VSCode上では`default_value`の箇所で"default_value is meaningless for Option"と出ている。`default_value`を指定するなら、型が`Option<String>`はおかしい、ということ。

```shell
PS samplecli> cargo run
   Compiling samplecli v0.1.0 (samplecli)
error: default_value is meaningless for Option
  --> src\main.rs:20:27
   |
20 |     #[clap(name = "FILE", default_value = "default.txt")]
   |                           ^^^^^^^^^^^^^

error: aborting due to previous error

error: could not compile `samplecli`.

To learn more, run the command again with --verbose.
```

いい感じ。

あらためて、コンパイルが通るコードを示す。書籍p.154に書かれている修正だけはダメで、実際にはmain()の中でOptionからファイル名を取得していた箇所も修正が必要。

```rust:main.rs
use clap::Clap;

#[derive(Clap, Debug)]
#[clap(
    name = "My RPN program",
    version = "1.0.0",
    author = "kencoba",
    about = "Super awesome sample RPN calculator"
)]
struct Opts {
    /// Sets the level of verbosity
    #[clap(short, long)]
    verbose: bool,

    /// Number
    #[clap(name = "NUMBER")]
    num: i32,

    /// Formulas written in RPN
    #[clap(name = "FILE", default_value = "default.txt")]
    formula_file: String,
}

fn main() {
    let opts = Opts::parse();
    println!("Number specified: {}", opts.num); // 追加
    println!("File specified: {}", opts.formula_file); // ファイル名取得処理修正
    println!("Is verbosity specified?: {}", opts.verbose);
}
```

実行する。

```shell
PS samplecli> cargo run
   Compiling samplecli v0.1.0 (samplecli)
    Finished dev [unoptimized + debuginfo] target(s) in 0.90s
     Running `target\debug\samplecli.exe`
error: The following required arguments were not provided:
    <NUMBER>

USAGE:
    samplecli.exe <NUMBER> <FILE>

For more information try --help
error: process didn't exit successfully: `target\debug\samplecli.exe` (exit code: 2)
```

必須引数が指定されない場合は、そもそもUSAGEが出る。

```shell
PS samplecli> cargo run -- 42
    Finished dev [unoptimized + debuginfo] target(s) in 0.11s
     Running `target\debug\samplecli.exe 42`
Number specified: 42
File specified: default.txt
Is verbosity specified?: false
```

期待通り。

# 振り返り
1.[clap](https://github.com/clap-rs/clap)便利。引数処理がここまで簡単確実にできると、コマンド開発が大変はかどる。
2. メッセージを日本語化するにはどうしたら良いか(要調査)。