GWにRustを触り始めたら楽しくなってきました。
新しい言語を触るのはやはり楽しいです。
今回はなんやかんやするコマンドを作ろうと思って、
引数をいい感じにしてくれるライブラリないかなと思って調べてたら、
googleのarghというライブラリがあったのでそれを触った備忘録です。
google/arghはこちら
github.com
基本的に使用するCargo.tomlはinitして、dependenciesにarghを追加した下記のようなのものでした。
Cargo.toml
[package]
name = "argh_opt"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
argh = "0.1.7"
目次
1.1 --hoge fugaのようなオプションを設定する
よくある ./bin/foo --hoge fuga
みたいなやつですね。
これを実装してみましょう。
src/main.rs
use argh::FromArgs;
#[derive(FromArgs, Debug)]
///
struct FooArg {
/// hoge
#[argh(option)]
hoge: String,
}
fn main() {
let a: FooArg = argh::from_env();
println!("{:?}", a);
println!("{:?}", a.hoge);
}
/// があるのは、descriptionを書くのが必須のようで、エラーになってしまうからこうしました。
argh(description= "...")とも書けるようですが、ドキュメンテーションコメントで書いても拾ってくれるみたいでした。
これを実行してみるとこんな感じになります。
$cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.22s
Running `target/debug/argh_opt`
Required options not provided:
--hoge
$cargo run -- --hoge fuga
Finished dev [unoptimized + debuginfo] target(s) in 0.02s
Running `target/debug/argh_opt --hoge fuga`
FooArg { hoge: "fuga" }
"fuga"
これで--hogeが必須のオプションになりました。
--hogeを必須ではなく任意にしたい場合は下記のように型を Option[String]
にするといいようです。
struct FooArg {
/// hoge
#[argh(option)]
hoge: Option[String],
}
1.2 数字やbool値を引数に渡す
次は ./bin/foo --hoge bar -- fuga --piyo 100
みたいなものを目指します。
数字は型に数字系のデータ型を指定し、
boolはoptionではなくswitchを指定することで使えるようでした。
use argh::FromArgs;
#[derive(FromArgs, Debug)]
///
struct FooArg {
/// hoge
#[argh(option)]
hoge: String,
/// fuga
#[argh(switch)]
fuga: bool,
/// piyo
#[argh(option)]
piyo: i32,
}
fn main() {
let a: FooArg = argh::from_env();
println!("{:?}", a);
println!("{:?} {:?} {:?}", a.hoge, a.fuga, a.piyo);
}
$cargo run -- --hoge foo --fuga --piyo 10
Finished dev [unoptimized + debuginfo] target(s) in 0.02s
Running `target/debug/argh_opt --hoge foo --fuga --piyo 10`
FooArg { hoge: "foo", fuga: true, piyo: 10 }
"foo" true 10
$cargo run -- --hoge foo --fuga --piyo bar
Finished dev [unoptimized + debuginfo] target(s) in 0.35s
Running `target/debug/argh_opt --hoge foo --fuga --piyo bar`
Error parsing option '--piyo' with value 'bar': invalid digit found in string
zsh: exit 1 cargo run -- --hoge foo --fuga --piyo bar
1.3 デフォルト値を設定する
default = "関数名"で指定してあげると良さそうでした。
ダブルクオートの中に文字列で関数を指定するのどことなく不思議な感じがしますね。
use argh::FromArgs;
#[derive(FromArgs, Debug)]
///
struct FooArg {
/// a
#[argh(option, default = "String::from(\"hoge1\")")]
hoge1: String,
/// a
#[argh(option, default = "default_hoge()")]
hoge2: String,
/// a
#[argh(option, default = "\"hoge\".to_string()")]
hoge3: String,
}
fn main() {
let a: FooArg = argh::from_env();
println!("{:?}", a);
println!("{:?} {:?} {:}", a.hoge1, a.hoge2, a.hoge3);
}
fn default_hoge() -> String {
return "hoge2".to_string();
}
なお、関数の型があっていないときはコンパイルエラーになりました。
例えば上記のdefault_hogeをi32が返るように変更し、
コンパイルするとエラーになった様子です。
fn default_hoge() -> i32 {
return 100;
}
error[E0308]: mismatched types
--> src/main.rs:10:30
|
10 |
| ^^^^^^^^^^^^^^^^- help: try using a conversion method: `.to_string()`
| |
| expected struct `String`, found `i32`
便利ですね〜
1.4 from_str_fnを使って文字列をstructに変換する
なんらかのフォーマットのデータを入力時に変換してもらおうと思います。
例えば 14:15
のような時間のフォーマットを変換します。
./bin/foo --time 14:15
みたいなコマンドのイメージです。
from_str_fnに、 &strを受け取りResult<T, String>を返す関数を渡すといい感じに変換してくれるみたいでした。
use argh::FromArgs;
#[derive(FromArgs, Debug)]
///
struct FooArg {
/// a
#[argh(option, from_str_fn(str_to_time))]
time: Time,
}
#[derive(Debug)]
struct Time {
_h: u32,
_m: u32,
}
fn str_to_time(v: &str) -> Result<Time, String> {
let x: Vec<&str> = v.split(":").collect();
let h = match x[0].parse::<u32>() {
Ok(x) => x,
Err(e) => return Err(e.to_string())
};
let m = match x[1].parse::<u32>() {
Ok(x) => x,
Err(e) => return Err(e.to_string())
};
if h >= 24 {
Err(format!("{} is too large", h))
} else if m >= 60 {
Err(format!("{} is too large", m))
} else {
Ok(Time { _h: h, _m: m })
}
}
fn main() {
let a: FooArg = argh::from_env();
println!("{:?}", a);
println!("{:?}", a.time);
}
実行してみると
$cargo run -- --time 14:15
Finished dev [unoptimized + debuginfo] target(s) in 0.05s
Running `target/debug/argh_opt --time '14:15'`
FooArg { time: Time { _h: 14, _m: 15 } }
Time { _h: 14, _m: 15 }
$cargo run -- --time hoge
Compiling argh_opt v0.1.0 (/Users/arata/Documents/Workspace/rust/argh_opt)
Finished dev [unoptimized + debuginfo] target(s) in 0.52s
Running `target/debug/argh_opt --time hoge`
Error parsing option '--time' with value 'hoge': invalid digit found in string
みたいな感じでした。
2.1 sub commandを使う
次はサブコマンドを実装してみます。
./bin/foo hoge
や ./bin/foo fuga
みたいなやつです。
これを実装してみます。
enumでコマンドを作成し、
それぞれarghのマクロにsubcommandと渡してあげれば良さそうでした。
use argh::FromArgs;
#[derive(FromArgs, Debug)]
///
struct FooArg {
#[argh(subcommand)]
_command: SubCommand,
}
#[derive(FromArgs, Debug)]
#[argh(subcommand)]
enum SubCommand {
Hoge(HogeCommand),
Fuga(FugaCommand),
}
#[derive(FromArgs, Debug)]
/// h
#[argh(subcommand, name = "hoge")]
struct HogeCommand {}
#[derive(FromArgs, Debug)]
/// h
#[argh(subcommand, name = "fuga")]
struct FugaCommand {}
fn main() {
let a: FooArg = argh::from_env();
println!("{:?}", a);
}
実行してみるとこんな感じ
$cargo run -- hoge
Finished dev [unoptimized + debuginfo] target(s) in 0.06s
Running `target/debug/argh_subcommand hoge`
FooArg { _command: Hoge(HogeCommand) }
$cargo run -- fuga
Finished dev [unoptimized + debuginfo] target(s) in 0.02s
Running `target/debug/argh_subcommand fuga`
FooArg { _command: Fuga(FugaCommand) }
$cargo run -- piyo
Finished dev [unoptimized + debuginfo] target(s) in 0.01s
Running `target/debug/argh_subcommand piyo`
Unrecognized argument: piyo
2.2 sub command内でオプションを使う
サブコマンドにオプションをつけるのは、
サブコマンドのstructに、1.xでやったようにオプションを追加するだけでよかったです。
試しにhogeコマンドにpiyoというオプションを追加してみます。
use argh::FromArgs;
#[derive(FromArgs, Debug)]
///
struct FooArg {
#[argh(subcommand)]
command: SubCommand,
}
#[derive(FromArgs, Debug)]
#[argh(subcommand)]
enum SubCommand {
Hoge(HogeCommand),
Fuga(FugaCommand),
}
#[derive(FromArgs, Debug)]
/// h
#[argh(subcommand, name = "hoge")]
struct HogeCommand {
/// piyo
#[argh(option)]
piyo: String,
}
#[derive(FromArgs, Debug)]
/// h
#[argh(subcommand, name = "fuga")]
struct FugaCommand {}
fn main() {
let a: FooArg = argh::from_env();
println!("{:?}", a);
match a.command {
SubCommand::Hoge(x) => println!("{}", x.piyo),
SubCommand::Fuga(_) => (),
}
}
実行してみるとこんな感じ
cargo run -- hoge --piyo fooooo
Finished dev [unoptimized + debuginfo] target(s) in 0.01s
Running `target/debug/argh_subcommand hoge --piyo fooooo`
FooArg { command: Hoge(HogeCommand { piyo: "fooooo" }) }
fooooo
感想
- とりあえずこれ使っておけばやりたいことはできそうだなーと思った
- google製だから安心かなと思ったけどREADMEの最後に↓って書いてあって少し不安になったw
NOTE: This is not an officially supported Google product.
- Rust楽しい