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}