わかりやすいエラー メッセージを表示する

完了

今のところ、存在しないジャーナル ファイルから読み取ろうとすると、プログラムはパニックになり、次のように出力されます。

    $ cargo run -- done 2

    thread 'main' panicked at 'Failed to perform action: Os { code: 2, kind: NotFound, message: "No such file or directory" }'

このエラーは、ユーザーにとってはやや冗長なので、よりわかりやすくする必要があります。 そのタスクを処理する大量のコードを書くこともできますが、anyhow という、有用で適切なエラーをユーザーに表示できる優れたクレートがあります。

anyhow クレートの背後にあるロジックは、独自のエラーの種類を提供することです。 この種類には整形出力のプロパティがあり、std::io::Error などの他のエラーから簡単に変換できます。 anyhow をプロジェクトに追加するのは簡単です。 main 関数の戻り値の型として配置するだけです。

まず、Cargo.toml ファイルで宣言します。

[dependencies]
anyhow = "1.0" # <--- Add `anyhow` to our project dependencies.
home = "0.5"
serde_json = "1.0"
structopt = "0.3"

[dependencies.chrono]
features = ["serde"]
version = "0.4"

[dependencies.serde]
features = ["derive"]
version = "1.0"

次に、型 anyhow::Result<()> を返すように main 関数のシグネチャを更新します。

use anyhow::anyhow;
use std::path::PathBuf;
use structopt::StructOpt;
mod cli;
mod tasks;

use cli::{Action::*, CommandLineArgs};
use tasks::Task;

fn find_default_journal_file() -> Option<PathBuf> {
    home::home_dir().map(|mut path| {
        path.push(".rust-journal.json");
        path
    })
}

fn main() -> anyhow::Result<()> {
    let CommandLineArgs {
        action,
        journal_file,
    } = CommandLineArgs::from_args();

    let journal_file = journal_file
        .or_else(find_default_journal_file)
        .ok_or(anyhow!("Failed to find journal file."))?;

    match action {
        Add { task } => tasks::add_task(journal_file, Task::new(task)),
        List => tasks::list_tasks(journal_file),
        Done { position } => tasks::complete_task(journal_file, position),
    }?;
    Ok(())
}

ほとんどのエラーの種類は anyhow::Error に変換できるので、? 構文を使用してコードから expect の呼び出しを削除できます。 また、anyhow! マクロを使用して、指定されたエラー メッセージを含む anyhow::Error をその場で生成している点を確認してください。

これで、プログラム内から返される I/O エラーが原因で発生するすべてのパニック メッセージが、次のようにユーザーに表示されます。

    $ cargo run -- -j missing-journal done 2
    Error: No such file or directory (os error 2)

数行のコードを追加しただけにしては、かなりの改善です。