saddle-db 0.1.1

Saddle managed asynchronous database access and transactions
Documentation
use saddle_core::{ErrorKind, SaddleError};

pub(crate) fn connection_unavailable() -> SaddleError {
    SaddleError::new(
        ErrorKind::Unavailable,
        "db.connection_unavailable",
        "database connection is unavailable",
    )
}

pub(crate) fn query_failed() -> SaddleError {
    SaddleError::new(
        ErrorKind::Infrastructure,
        "db.query_failed",
        "database operation failed",
    )
}

pub(crate) fn transaction_begin_failed() -> SaddleError {
    SaddleError::new(
        ErrorKind::Infrastructure,
        "db.transaction_begin_failed",
        "database transaction could not be started",
    )
}

pub(crate) fn transaction_commit_failed() -> SaddleError {
    SaddleError::new(
        ErrorKind::Infrastructure,
        "db.transaction_commit_failed",
        "database transaction could not be committed",
    )
}

pub(crate) fn transaction_rollback_failed() -> SaddleError {
    SaddleError::new(
        ErrorKind::Infrastructure,
        "db.transaction_rollback_failed",
        "database transaction could not be rolled back",
    )
}

pub(crate) fn map_operation_error(error: sqlx::Error) -> SaddleError {
    match error {
        sqlx::Error::PoolTimedOut | sqlx::Error::PoolClosed | sqlx::Error::Io(_) => {
            connection_unavailable()
        }
        _ => query_failed(),
    }
}

pub(crate) fn invalid_column() -> SaddleError {
    SaddleError::new(
        ErrorKind::Internal,
        "db.invalid_column",
        "database result did not match the expected column type",
    )
}

pub(crate) fn result_limit_exceeded() -> SaddleError {
    SaddleError::new(
        ErrorKind::Infrastructure,
        "db.result_limit_exceeded",
        "database query exceeded the V1 row limit",
    )
}

pub(crate) fn unsupported_column_type() -> SaddleError {
    SaddleError::new(
        ErrorKind::Infrastructure,
        "db.unsupported_column_type",
        "database returned a column type outside the V1 controlled API",
    )
}

pub(crate) fn transaction_cancelled() -> SaddleError {
    SaddleError::new(
        ErrorKind::Infrastructure,
        "db.transaction_cancelled",
        "database transaction was cancelled and rolled back",
    )
}

pub(crate) fn cleanup_failed() -> SaddleError {
    SaddleError::new(
        ErrorKind::Internal,
        "db.cleanup_failed",
        "database cancellation cleanup worker failed",
    )
}

pub(crate) fn invalid_config(message: &'static str) -> SaddleError {
    SaddleError::new(ErrorKind::InvalidArgument, "db.invalid_config", message)
}

pub(crate) fn invalid_statement(message: &'static str) -> SaddleError {
    SaddleError::new(ErrorKind::InvalidArgument, "db.invalid_statement", message)
}

#[cfg(test)]
mod tests {
    use std::io;

    use super::*;

    #[test]
    fn sqlx_details_are_replaced_by_stable_safe_errors() {
        let protocol = map_operation_error(sqlx::Error::Protocol("password=secret".to_owned()));
        assert_eq!(protocol.kind(), ErrorKind::Infrastructure);
        assert_eq!(protocol.code(), "db.query_failed");
        assert!(!protocol.to_string().contains("secret"));

        let io = map_operation_error(sqlx::Error::Io(io::Error::other("host-secret")));
        assert_eq!(io.kind(), ErrorKind::Unavailable);
        assert_eq!(io.code(), "db.connection_unavailable");
        assert!(!io.to_string().contains("host-secret"));
    }
}