Skip to main content

sova_db/
error.rs

1use sova_core::extend::ErrorResponse;
2use sova_core::{Error, IntoResponse, Response};
3use sea_orm::{DbErr, SqlErr};
4
5/// Newtype around SeaORM [`DbErr`] mapped to HTTP via [`Error::Response`].
6#[derive(Debug)]
7pub struct DbError(pub DbErr);
8
9impl From<DbErr> for DbError {
10    fn from(err: DbErr) -> Self {
11        Self(err)
12    }
13}
14
15fn map_db_err(err: DbErr) -> Error {
16    let (status, public) = if matches!(err, DbErr::RecordNotFound(_)) {
17        (404, "Not Found")
18    } else if let Some(sql) = err.sql_err() {
19        match sql {
20            SqlErr::UniqueConstraintViolation(_) => (409, "Conflict"),
21            SqlErr::ForeignKeyConstraintViolation(_) => (422, "Unprocessable Entity"),
22            _ => match &err {
23                DbErr::ConnectionAcquire(_) | DbErr::Conn(_) => (503, "Service Unavailable"),
24                _ => (500, "Internal Server Error"),
25            },
26        }
27    } else {
28        match &err {
29            DbErr::ConnectionAcquire(_) | DbErr::Conn(_) => (503, "Service Unavailable"),
30            _ => (500, "Internal Server Error"),
31        }
32    };
33    if status >= 500 {
34        tracing::error!(error = %err, "database error");
35    }
36    Error::Response(Box::new(Response::text(public).status(status)))
37}
38
39impl From<DbError> for Error {
40    fn from(err: DbError) -> Self {
41        map_db_err(err.0)
42    }
43}
44
45impl IntoResponse for DbError {
46    fn into_response(self) -> Response {
47        Error::from(self).into_response()
48    }
49}
50
51impl ErrorResponse for DbError {}