athena_rs 2.5.3

Database gateway API
Documentation
//! tokio-postgres error parser for converting database errors into ProcessedError responses.
use actix_web::http::StatusCode;
use serde_json::json;

use crate::error::{ErrorCategory, ProcessedError, generate_trace_id};

pub fn process_tokio_postgres_error(
    err: &tokio_postgres::Error,
    context: Option<&str>,
) -> ProcessedError {
    let trace_id = generate_trace_id();
    if let Some(db_err) = err.as_db_error() {
        let code = db_err.code().code();
        let message = db_err.message().to_string();

        let (status, error_code, category) = match code {
            "23505" => (
                StatusCode::CONFLICT,
                "unique_violation",
                ErrorCategory::DatabaseConstraint,
            ),
            "23503" => (
                StatusCode::CONFLICT,
                "foreign_key_violation",
                ErrorCategory::DatabaseConstraint,
            ),
            "23502" => (
                StatusCode::BAD_REQUEST,
                "not_null_violation",
                ErrorCategory::DatabaseConstraint,
            ),
            _ if code.starts_with("22") => (StatusCode::BAD_REQUEST, "data_exception", ErrorCategory::QuerySyntax),
            _ if code.starts_with("42") => (StatusCode::BAD_REQUEST, "query_syntax", ErrorCategory::QuerySyntax),
            _ => (StatusCode::INTERNAL_SERVER_ERROR, "db_error", ErrorCategory::Internal),
        };

        let mut processed = ProcessedError::new(
            category,
            status,
            error_code,
            message,
            trace_id,
        )
        .with_metadata("sql_state", json!(code));

        if let Some(ctx) = context {
            processed = processed.with_metadata("context", json!(ctx));
        }

        return processed;
    }

    let mut processed = ProcessedError::new(
        ErrorCategory::DatabaseConnection,
        StatusCode::BAD_GATEWAY,
        "postgres_driver_error",
        err.to_string(),
        trace_id,
    );
    if let Some(ctx) = context {
        processed = processed.with_metadata("context", json!(ctx));
    }
    processed
}

pub fn process_tokio_postgres_db_error(
    sql_state: &str,
    message: &str,
    context: Option<&str>,
) -> ProcessedError {
    let trace_id = generate_trace_id();
    let (status, error_code, category) = match sql_state {
        "23505" => (StatusCode::CONFLICT, "unique_violation", ErrorCategory::DatabaseConstraint),
        "23503" => (StatusCode::CONFLICT, "foreign_key_violation", ErrorCategory::DatabaseConstraint),
        "23502" => (StatusCode::BAD_REQUEST, "not_null_violation", ErrorCategory::DatabaseConstraint),
        _ if sql_state.starts_with("22") => (StatusCode::BAD_REQUEST, "data_exception", ErrorCategory::QuerySyntax),
        _ if sql_state.starts_with("42") => (StatusCode::BAD_REQUEST, "query_syntax", ErrorCategory::QuerySyntax),
        _ => (StatusCode::INTERNAL_SERVER_ERROR, "db_error", ErrorCategory::Internal),
    };

    let mut processed = ProcessedError::new(
        category,
        status,
        error_code,
        message.to_string(),
        trace_id,
    )
    .with_metadata("sql_state", json!(sql_state));

    if let Some(ctx) = context {
        processed = processed.with_metadata("context", json!(ctx));
    }

    processed
}