Skip to main content

umbral_core/orm/queryset/
errors.rs

1//! Error types surfaced by QuerySet terminals.
2//!
3//! These two enums are public surface (re-exported from
4//! `crate::orm::queryset` and through the facade), so each carries a
5//! full `Display` + `Error` impl plus the appropriate
6//! `From<sqlx::Error>`.
7
8/// Error type for [`super::QuerySet::get`] / [`super::Manager::get`]
9/// (the exactly-one shape).
10///
11/// `.get()` deliberately returns this rather than `Result<Option<T>,
12/// sqlx::Error>` because three outcomes need three branches:
13///
14/// - `Ok(row)` — exactly one matched.
15/// - `Err(NotFound)` — zero matched. The common 404 path.
16/// - `Err(MultipleObjectsReturned)` — more than one matched. A
17///   data-integrity signal: filters that should pin a unique row
18///   (PK lookup, UNIQUE-constrained column) hitting this variant
19///   means an invariant has already broken upstream.
20/// - `Err(Sqlx)` — the DB itself returned an error.
21#[derive(Debug)]
22pub enum GetError {
23    NotFound,
24    MultipleObjectsReturned,
25    Sqlx(sqlx::Error),
26}
27
28impl std::fmt::Display for GetError {
29    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30        match self {
31            Self::NotFound => write!(f, "no matching row"),
32            Self::MultipleObjectsReturned => {
33                write!(f, "expected exactly one row, found more")
34            }
35            Self::Sqlx(e) => write!(f, "{e}"),
36        }
37    }
38}
39
40impl std::error::Error for GetError {
41    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
42        match self {
43            Self::Sqlx(e) => Some(e),
44            _ => None,
45        }
46    }
47}
48
49impl From<sqlx::Error> for GetError {
50    fn from(e: sqlx::Error) -> Self {
51        Self::Sqlx(e)
52    }
53}
54
55/// Feature 29 — composite error returned by
56/// [`super::QuerySet::try_for_each`]. The chunked streaming terminal
57/// can fail in two ways and the call site usually wants to
58/// distinguish: a SQL fetch failure is a system-level problem (DB
59/// went away, schema mismatch, etc.), while a callback error is
60/// whatever domain-specific failure the user's body produced (file
61/// write blew up, validation rejected the row, etc.).
62#[derive(Debug)]
63pub enum TryForEachError<E> {
64    /// A database fetch returned an error mid-iteration. The
65    /// callback never saw this row.
66    Sqlx(sqlx::Error),
67    /// The user's callback returned an error for some row. The
68    /// walk stopped immediately; rows after the failing one were
69    /// not fetched.
70    Callback(E),
71}
72
73impl<E: std::fmt::Display> std::fmt::Display for TryForEachError<E> {
74    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75        match self {
76            Self::Sqlx(e) => write!(f, "{e}"),
77            Self::Callback(e) => write!(f, "{e}"),
78        }
79    }
80}
81
82impl<E: std::fmt::Debug + std::fmt::Display> std::error::Error for TryForEachError<E> {
83    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
84        match self {
85            Self::Sqlx(e) => Some(e),
86            Self::Callback(_) => None,
87        }
88    }
89}