rustで簡単にインタラクティブな標準入力ができるdialoguerを使ってみます.サンプルプログラム全体はこちらにあります.
Confirm
yes/noで答えられる質問を表示し,入力をboolで返します.yかnの入力ですぐ終了するため,yesやnoと入力してエンターで確定したい場合は後述するInput
を使うのがいいと思います.
以下の例ではyを入力するたびに"プログラムを続けます."が表示されます.
use dialoguer::Confirm;
'main: loop {
if Confirm::new()
.with_prompt("プログラムを続けますか?")
.default(true)
.interact()? // Result<bool, std::io::Error>
{
println!("プログラムを続けます.");
} else {
println!("プログラムを終了します.");
break 'main;
}
}
コードを実行すると
プログラムを続けますか? [Y/n]
のように表示され,yを入力すれば
プログラムを続けますか? yes
プログラムを続けます.
プログラムを続けますか? [Y/n]
nを入力すれば
プログラムを続けますか? no
プログラムを終了します.
となります.
Input
質問を表示し,エンターで確定するまでの入力を受け取ります.
以下の例では入力した名前を表示します.プレースホルダの初期の値として"name"が入っています.
use dialoguer::Input;
let input = Input::<String>::new()
.with_prompt("名前を入力してください")
.with_initial_text("name")
.interact()?;
println!("ようこそ!{}さん", input);
コードを実行すると
名前を入力してください: name
のように表示され,名前を入力すると
名前を入力してください: 山田太郎
ようこそ!山田太郎さん
と表示されます.空の状態を許容していないため名前を入力しないと終了しません.
Input
では入力値の一次保存とバリデーションができます.
入力値の一時保存にはdialoguer::History
トレイトを実装した型を利用することができます.FIFOを利用することで新しいものから順に履歴を取得できます.
use dialoguer::History; // featuresにhistoryを指定
use std::collections::VecDeque;
struct MyHistory(VecDeque<String>);
impl MyHistory {
fn new() -> Self {
Self(VecDeque::new())
}
}
impl<T: ToString> History<T> for MyHistory {
fn read(&self, pos: usize) -> Option<String> {
self.0.get(pos).cloned()
}
fn write(&mut self, val: &T) {
self.0.push_front(val.to_string());
}
}
以下の例では入力した名前の履歴としての保存と文字数によるバリデーションを行っています.Input::validate_with
に与えるバリデーションを行うクロージャ―では()
を返すResult
型を返すようにします.このクロージャ―には引数と返り値に型注釈をする必要があります.また,History
を使う場合はInput::interact
は使えず,変わりにInput::interact_text
を使います.interact_text
は日本語を入力すると表示が崩れることがありますが,問題なく入力できます.
名前として"exit"を入力することでループを終了します.
fn main() -> Result<(), std::io::Error> {
let mut history = MyHistory::new();
'main: loop {
let input = Input::<String>::new()
.with_prompt("あなたの名前を入力してください")
.validate_with(|input: &String| -> Result<(), &str> {
if input.chars().count() <= 10 {
Ok(())
} else {
Err("名前は10文字以内にしてください.")
}
})
.history_with(&mut history)
.interact_text()?;
if &input == "exit" {
break 'main;
}
println!("ようこそ!{}", input);
}
Ok(())
}
以下のように入力した場合
あなたの名前を入力してください: Smith
ようこそ!Smith
あなたの名前を入力してください: Johnson
ようこそ!Johnson
あなたの名前を入力してください: Williams
ようこそ!Williams
その次の入力では上下キーで履歴を参照できます.
あなたの名前を入力してください: Williams
一方で
あなたの名前を入力してください: JohnsonSmithWilliams
のように文字数をオーバーした場合は
error: 名前は10文字以内にしてください.
あなたの名前を入力してください:
のようにエラーとなり再び入力を待ちます.
Password
基本はInput
と同じですが,確認のための再入力とそれが失敗したときに表示するエラーを指定できます.パスワードは入力中も表示されません.
use dialoguer::Password;
let password = Password::new()
.with_prompt("作成するパスワードを入力してください")
.with_confirmation("再び入力してください", "パスワードが違います")
.interact()?;
println!("{}", password);
コードを実行すると
作成するパスワードを入力してください:
と表示され,入力すると再入力を求められます.
再び入力してください:
再入力に成功した場合はパスワードが表示され(例のプログラムの場合),失敗した場合は
error: パスワードが違います
作成するパスワードを入力してください:
のようにエラーが表示されて最初に戻ります.
Select
選択肢を矢印キーで選択できます.
以下の例では選択肢の言語について,確定した選択肢に対応するインデックスが返り,挨拶が表示されます.
use dialoguer::Select;
let choices = vec!["English", "日本語", "中文"];
let selection = Select::new().items(&choices).interact()?;
match selection {
0_usize => println!("Hello!"),
1_usize => println!("こんにちは!"),
2_usize => println!("你好!"),
_ => println!("undefined selection"),
}
コードを実行すると
> English
日本語
中文
のように表示され,矢印キーで選択を変更・エンターで確定すると
こんにちは!
挨拶が表示されます.
MultiSelect
選択肢を複数選択できます.
以下の例では選択肢のタグについてデフォルトを指定し,選択されたタグを全て表示しています.
use dialoguer::MultiSelect;
let choices = vec!["プログラミング", "Rust", "Python", "数学", "歴史"];
let defaults = vec![true, false, false, false, false];
let selections: Vec<usize> = MultiSelect::new()
.with_prompt("タグを選んでください")
.items(&choices)
.defaults(&defaults)
.interact()?;
let tags = selections.iter().map(|i| choices[*i]).collect::<Vec<_>>();
println!("タグは{:?}です.", tags);
コードを実行すると
タグを選んでください:
> [x] プログラミング
[ ] Rust
[ ] Python
[ ] 数学
[ ] 歴史
のように選択肢が出るため,矢印キーで変更・スペースキーで選択・エンターで確定すると
タグを選んでください: プログラミング, Rust, 数学
タグは["プログラミング", "Rust", "数学"]です.
選択されたタグが全て表示されます.
FuzzySelect
選択肢を入力によって絞り込むことができます.
以下の例ではプログラミング言語の選択肢を絞り込み,選択した言語を表示します.
use dialoguer::FuzzySelect; // featuresでfuzzy-selectを指定
let selections = vec![
"ActionScript",
"AppleScript",
"ASP.NET",
"Assembly",
"BASIC",
"Brainf**k",
"C",
"C#",
"C++",
...省略
];
let selection = FuzzySelect::new()
.with_prompt("言語を選択してください")
.items(&selections)
.interact()?;
println!("{}が選択されました", selections[selection]);
コードを実行すると
言語を選択してください |
ActionScript
AppleScript
ASP.NET
Assembly
BASIC
Brainf**k
C
C#
C++
...省略
のように選択肢が表示され,"C"と入力すると
言語を選択してください C|
> C
C#
C++
COBOL
CoffeeScript
Common Lisp
CSS
CUE
Objective-C
Objective-C++
BASIC
OCaml
SCSS
のように選択肢が絞り込まれます.矢印で変更・エンターで確定をすると
言語を選択してください: C++
C++が選択されました
選択した言語が表示されます.
Sort
リストのソートができます.
以下の例では複数のコマンドのリストをソートしてその順番に表示します.
use dialoguer::Sort;
let list = vec!["command A", "command B", "command C", "command D"];
let sorted: Vec<usize> = Sort::new()
.with_prompt("実行する順序を選んでください")
.items(&list)
.interact()?;
for command in sorted.into_iter().map(|i| list[i]) {
println!("{}を実行しました", command);
}
コードを実行すると
実行する順序を選んでください:
> [ ] command A
[ ] command B
[ ] command C
[ ] command D
のように選択肢として表示されるため,スペースキーで選択・矢印キーで移動・エンターで確定すると
実行する順序を選んでください: command B, command A, command D, command C
command Bを実行しました
command Aを実行しました
command Dを実行しました
command Cを実行しました
ソートした順番に表示されます.
紹介した以外でもテーマを設定したり,consoleクレートと連携したりできるようです.