mainmatter/gerust

Improve error types

Closed this issue · 0 comments

Currently, Gerust generates an Error type in the db crate, which is of the following form:

/// Errors that can occur as a result of a data layer operation.
#[derive(Error, Debug)]
pub enum Error {
    /// General database error, e.g. communicating with the database failed
    #[error("database query failed")]
    DbError(anyhow::Error),
    /// No record was found where one was expected, e.g. when loading a record by ID
    #[error("no record found where one was expected")]
    NoRecordFound,
    #[error("validation failed")]
    /// An invalid changeset was passed to a writing operation such as creating or updating a record.
    ValidationError(validator::ValidationErrors),
}

In the generated entity modules (e.g. tasks), sqlx::Errors are converted directly into Errro::DbError, which wraps an anyhow::Error, thereby throwing away information about the cause of the error. Instead, the DbError variant should wrap a sqlx::Error directly.

Furthermore, the web crate should expose another Error enum, which among others, wraps a db::Error in one of its variants and implements axum::IntoResponse. For example:

#[derive(thiserror::Error, Debug)]
pub enum Error {
    #[error("Database error")]
    Database(#[from] server_db::Error),

    // todo add other variants
}

impl IntoResponse for Error {
    fn into_response(self) -> axum::response::Response {
        match self {
            Error::Database(db_error) => match db_error {
                server_db::Error::NoRecordFound => StatusCode::NOT_FOUND.into_response(),
                _ => StatusCode::INTERNAL_SERVER_ERROR.into_response(),
            },
             _ => StatusCode::INTERNAL_SERVER_ERROR.into_response()
        }
    }
}

This allows returning a Result<T, web::error::Error> directly from route handlers, greatly improving ergonomics.