💬

Rust | Promptuity で対話型インターフェースを実装する

2024/04/30に公開

Rust で対話型の CLI ツールを作成する

Rust で CLI ツールを作成時、入力しながら実行できるように実装する必要があり、
対話型のインターフェースを構築する必要がありました。

調べていると、promptuity というライブラリが便利そうだったので使ってみました!

Promptuity = Prompt + Ingenuity

https://github.com/wadackel/promptuity

Promptuity is a library that provides interactive prompts. It is highly extensible, allowing you to build your original prompts from scratch. It brings ingenuity to various projects.

Promptuity はインタラクティブなプロンプトを提供するライブラリです。拡張性が高く、オリジナルのプロンプトをゼロから作ることができます。様々なプロジェクトに創意工夫をもたらします。

このライブラリの作者のブログ では、以下のようなことが述べられていました。

利用者側に自由度がある Crate が欲しいと思い、対話型インターフェースを構築する Promptuity という Crate を公開しました

似たような目的のライブラリがいくつか存在する中で、カスタマイズ性に富んだ対話型プロンプトライブラリという特徴があるようです。

今回は簡単に実装例を紹介します。

文字列入力 - input

オーソドックスな文字列の入力形式です。

with_placeholder でプレイスホルダーを設定することもできます。

use promptuity::prompts::Input;
use promptuity::themes::FancyTheme;
use promptuity::{Error, Promptuity, Term};

fn main() -> Result<(), Error> {
    let mut term = Term::default();
    let mut theme = FancyTheme::default();
    let mut p = Promptuity::new(&mut term, &mut theme);

    p.term().clear()?;

    // ここに説明文を記載できる
    p.with_intro("You can define a description here.").begin()?;

    // 入力値を `name` に格納できる
    let name = p.prompt(Input::new("Please enter your username").with_placeholder("username"))?;

    p.with_outro(format!("Thank you for your response, {}!", name))
        .finish()?;

    Ok(())
}

実行すると、以下のようにコマンドプロンプトに表示されます。

パスワード入力 - password

マスキングされた文字列の入力形式です。

CLI ツールで、入力された値を隠して表示したい場合に有効です。

また、with_validator でバリデーションを追加することもできます。

use promptuity::prompts::Password;
use promptuity::themes::FancyTheme;
use promptuity::{Error, Promptuity, Term};

fn main() -> Result<(), Error> {
    let mut term = Term::default();
    let mut theme = FancyTheme::default();
    let mut p = Promptuity::new(&mut term, &mut theme);

    p.term().clear()?;

    // ここに説明文を記載できる
    p.with_intro("You can define a description here.").begin()?;

    // 入力値を `secret` に格納できる
    let secret = p.prompt(
        Password::new("Please enter your password")
            .with_hint("Enter more than 8 alphanumeric characters.")
            .with_validator(|value: &String| {
                if value.len() < 8 {
                    Err("Password must be at least 8 characters long".into())
                } else {
                    Ok(())
                }
            }),
    )?;

    p.with_outro(format!("PASSWORD: {}", secret))
        .finish()?;

    Ok(())
}

実行すると、以下のようにコマンドプロンプトに表示されます。

バリデーションに引っかかっても、入力の途中で再開できるのは嬉しいポイントですね!!

単一選択 - select

複数の選択肢の中から1つを選択できる形式です。

use promptuity::prompts::{Select, SelectOption};
use promptuity::themes::FancyTheme;
use promptuity::{Error, Promptuity, Term};

fn main() -> Result<(), Error> {
    let mut term = Term::default();
    let mut theme = FancyTheme::default();
    let mut p = Promptuity::new(&mut term, &mut theme);

    p.term().clear()?;

    // ここに説明文を記載できる
    p.with_intro("You can define a description here.").begin()?;

    // 複数の選択肢の中から1つを選択できる
    // ヒントも記載できる
    let color: &str = p.prompt(
        Select::new(
            "What is your favorite color?",
            vec![
                SelectOption::new("Red", "#ff0000"),
                SelectOption::new("Green", "#00ff00").with_hint("recommended"),
                SelectOption::new("Blue", "#0000ff"),
            ],
        )
            .as_mut(),
    )?;
    p.finish()?;

    p.with_outro(format!("SELECTED: {}", color)).finish()?;

    Ok(())
}

実行すると、以下のようにコマンドプロンプトに表示されます。

複数選択 - multi select

複数の選択肢の中から任意の数だけ選択できる形式です。

スペースキーで選択できます。(Macで確認)

use promptuity::prompts::{MultiSelect, MultiSelectOption};
use promptuity::themes::FancyTheme;
use promptuity::{Error, Promptuity, Term};

fn main() -> Result<(), Error> {
    let mut term = Term::default();
    let mut theme = FancyTheme::default();
    let mut p = Promptuity::new(&mut term, &mut theme);

    p.term().clear()?;

    // ここに説明文を記載できる
    p.with_intro("You can define a description here.").begin()?;

    // 複数の選択肢の中から複数選択できる
    // ヒントも記載できる
    let colors: Vec<&str> = p.prompt(
        MultiSelect::new(
            "What are your favorite colors?",
            vec![
                MultiSelectOption::new("Red", "#ff0000"),
                MultiSelectOption::new("Green", "#00ff00").with_hint("recommended"),
                MultiSelectOption::new("Blue", "#0000ff"),
            ],
        )
        .as_mut(),
    )?;
    p.finish()?;

    p.with_outro(format!("SELECTED: {}", colors.join(",")))
        .finish()?;

    Ok(())
}

実行すると、以下のようにコマンドプロンプトに表示されます。

確認 - confirm

YES or NO の選択形式です。

YES では true が返り、NO では false が返ります。

「入力された値で実行しますか?」といった、最後の確認で使用することができます。

use promptuity::prompts::Confirm;
use promptuity::themes::FancyTheme;
use promptuity::{Error, Promptuity, Term};

fn main() -> Result<(), Error> {
    let mut term = Term::default();
    let mut theme = FancyTheme::default();
    let mut p = Promptuity::new(&mut term, &mut theme);

    p.term().clear()?;

    // ここに説明文を記載できる
    p.with_intro("You can define a description here.").begin()?;

    // YES or NO を選択できる
    // YES = true
    // NO  = false
    let execute: bool = p.prompt(
        Confirm::new("Do you want to execute?")
            .with_hint("This is just a sample prompt :)")
            .with_default(true),
    )?;
    p.finish()?;

    p.with_outro(format!("YES or NO: {}", execute)).finish()?;

    Ok(())
}

実行すると、以下のようにコマンドプロンプトに表示されます。

まとめ

CLI ツールをサクッと作成したいなと思い、ライブラリを探したところ Promptuity に出会いました。

サンプルコード も充実していて、簡単に実装することができました🎉✨

自分もこんな便利なライブラリを OSS で公開して、世界に貢献していきたいです...🔥🥰🔥

参考

コラボスタイル Developers

Discussion