この記事は ユニークビジョン株式会社 Advent Calendar 2020 の 11 日目の記事です。
はじめに
Rust 勉強中です。(初心者です)
Rust で CLI ツールを作ろうとしたときに structopt が便利だったのでご紹介します。
... ちなみに、clap というクレートでも同じ書き方でコマンドを定義できるようです。
https://github.com/clap-rs/clap#using-derive-macros
The first example shows the simplest way to use clap, by defining a struct. If you're familiar with the structopt crate you're in luck, it's the same! (In fact it's the exact same code running under the covers!)
(structopt は clap を使って実装されているので、clap を使った例のほうがよかったのかもしれません)
準備
プロジェクトを用意します。
cargo new my-cli
Cargo.toml
の dependencies
に以下を追加します。
structopt = "0.3.21"
引数として受け取る
引数を受け取って、そのまま返す簡単なコマンドを作成します。
- 構造体の定義の上のところに
#[derive(StructOpt)]
を記述します。 -
from_args()
を呼び出すと、実行時に引数から受け取った値を構造体として得ることができます。
use structopt::StructOpt;
#[derive(StructOpt)]
struct Opt {
name: String,
}
fn main() {
let opt = Opt::from_args();
println!("{}", opt.name);
}
実行すると以下のようになります。
$ cargo run hoge
hoge
オプションとして受け取る
オプションとして使いたいメンバには #[structopt(short, long)]
を記述します。
- デフォルトではメンバ変数の名前がオプションの名前として使用されます。
-
short = "g"
とすると、そのオプションを-g
で呼び出せるようになります。
use structopt::StructOpt;
#[derive(StructOpt)]
struct Opt {
name: String,
#[structopt(short = "g", long)]
greet: bool,
}
fn main() {
let opt = Opt::from_args();
if opt.greet {
println!("Hello, {}!", opt.name);
} else {
println!("{}", opt.name);
}
}
$ cargo run hoge
hoge
$ cargo run hoge --greet
Hello, hoge!
$ cargo run hoge -g
Hello, hoge!
greet
変数の型を Option<String>
にすれば文字列を渡せます。
(もしここで Option<String>
ではなく String
にすれば --greet
は必須のオプションになります。)
use structopt::StructOpt;
#[derive(StructOpt)]
struct Opt {
name: String,
#[structopt(short = "g", long)]
greet: Option<String>,
}
fn main() {
let opt = Opt::from_args();
match opt.greet {
Some(greet) => println!("{}, {}!", greet, opt.name),
None => println!("{}", opt.name),
}
}
$ cargo run hoge -g Hi
Hi, hoge!
その他
環境変数から値を受け取る
個人的にとても使いやすそうだと思いました。
use structopt::StructOpt;
#[derive(StructOpt)]
struct Opt {
name: String,
#[structopt(short = "g", default_value = "Hello", env = "GREET")]
greet: String,
}
fn main() {
let opt = Opt::from_args();
println!("{}, {}!", opt.greet, opt.name);
}
$ cargo run hoge
Hello, hoge!
$ cargo run hoge -g Hi
Hi, hoge!
$ GREET=Hi cargo run hoge
Hi, hoge!
Vec<String>
として値を受け取る
たとえば Vec<String>
として引数の値を受け取ることができます。
-
min_values = 2
とすると、引数が 1 つしか与えられない場合にエラーになってくれます。
use structopt::StructOpt;
#[derive(StructOpt)]
struct Opt {
#[structopt(required = true, min_values = 2)]
names: Vec<String>,
}
fn main() {
let opt = Opt::from_args();
println!("Hello! {}!", opt.names.join(", "));
}
$ cargo run hoge fuga
Hello! hoge, fuga!
サブコマンドを作成する
use structopt::StructOpt;
#[derive(StructOpt, Debug)]
/// help comment here!!!
enum Opt {
/// greet someone
Greet { name: String },
/// say goodbye to someone
Bye {
name: String,
#[structopt(short = "f", long)]
forever: bool,
},
}
fn main() {
match Opt::from_args() {
Opt::Greet { name } => println!("{} さん、こんにちは。", name),
Opt::Bye { name, forever } => {
if forever {
println!("{} さん、永遠にさようなら。", name)
} else {
println!("{} さん、さようなら。", name)
}
}
};
}
実行例 :
cargo run help
my-cli 0.1.0
help comment here!!!
USAGE:
my-cli <SUBCOMMAND>
FLAGS:
-h, --help Prints help information
-V, --version Prints version information
SUBCOMMANDS:
bye say goodbye to someone
greet greet someone
help Prints this message or the help of the given subcommand(s)
$ cargo run greet hoge
hoge さん、こんにちは。
$ cargo run bye fuga -f
fuga さん、永遠にさようなら。
おわりに
structopt を使用すれば Rust のプログラムを簡単に CLI にできます。ぜひ使ってみてください。
簡単ですが以上です。ありがとうございました。