12
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

[Rust] dialoguerでインタラクティブな入力

Last updated at Posted at 2022-11-17

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クレートと連携したりできるようです.

12
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?