Skip to main content

systemprompt_traits/repository/
sqlx_classify.rs

1//! SQLSTATE classification of `sqlx::Error` into [`RepositoryError`].
2//!
3//! Copyright (c) systemprompt.io — Business Source License 1.1.
4//! See <https://systemprompt.io> for licensing details.
5
6use sqlx::error::ErrorKind;
7
8use super::{ConstraintKind, RepositoryError};
9
10const UNNAMED_CONSTRAINT: &str = "<unnamed>";
11
12impl From<sqlx::Error> for RepositoryError {
13    fn from(err: sqlx::Error) -> Self {
14        if matches!(err, sqlx::Error::RowNotFound) {
15            return Self::NotFound {
16                entity: "row",
17                key: None,
18            };
19        }
20        let classified = err.as_database_error().map(|db_error| {
21            let kind = match db_error.kind() {
22                ErrorKind::UniqueViolation => Some(ConstraintKind::Unique),
23                ErrorKind::ExclusionViolation => Some(ConstraintKind::Exclusion),
24                ErrorKind::ForeignKeyViolation => Some(ConstraintKind::ForeignKey),
25                ErrorKind::NotNullViolation => Some(ConstraintKind::NotNull),
26                ErrorKind::CheckViolation => Some(ConstraintKind::Check),
27                _ => None,
28            };
29            let constraint = db_error
30                .constraint()
31                .unwrap_or(UNNAMED_CONSTRAINT)
32                .to_owned();
33            let sqlstate = db_error.code().map(std::borrow::Cow::into_owned);
34            (kind, constraint, sqlstate)
35        });
36        match classified {
37            Some((Some(kind), constraint, _)) => Self::Constraint {
38                kind,
39                constraint,
40                source: Box::new(err),
41            },
42            Some((None, _, sqlstate)) => Self::Database {
43                sqlstate,
44                source: Box::new(err),
45            },
46            None => Self::Database {
47                sqlstate: None,
48                source: Box::new(err),
49            },
50        }
51    }
52}