Visualización de mensajes de error descriptivos

Completado

En este momento, si intentamos leer un archivo de diario que no existe, el programa entra en estado de alarma con el siguiente resultado:

    $ cargo run -- done 2

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

Este error tiene una cantidad de detalles algo excesiva para nuestros usuarios, así que deberíamos hacerlo más presentable. Podríamos escribir mucho código para ocuparnos de esa tarea, pero hay un excelente contenedor para mostrar errores útiles y atractivos a los usuarios llamado anyhow.

La lógica que subyace al contenedor anyhow es que proporciona su propio tipo de error. Este tipo tiene propiedades de impresión atractivas y se puede convertir fácilmente a partir de otros errores, como std::io::Error. Es fácil agregar anyhow a nuestro proyecto. Todo lo que tenemos que hacer es colocarlo como el tipo de valor devuelto de la función main.

En primer lugar, declárelo en el archivo 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"

Ahora, actualice la signatura de función main de modo que devuelva el tipo anyhow::Result<()>:

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(())
}

Dado que la mayoría de los tipos de error se pueden convertir en anyhow::Error, podemos usar la sintaxis ? para quitar las llamadas de expect del código. Además, considere que estamos usando la macro anyhow! para generar un elemento anyhow::Error sobre la marcha que contiene el mensaje de error proporcionado.

Ahora cada mensaje de pánico causado por un error de E/S devuelto desde nuestro programa se muestra a los usuarios así:

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

Es una gran mejora con unas pocas líneas de código adicionales.