Skip to main content

ferro_orm/
error.rs

1//! `GuardedError` — the single error type for the ferro-orm crate.
2//!
3//! Every variant's `Display` impl prefixes `"guarded: …"` so production
4//! log greps stay surgical (matches the workspace convention used by
5//! `WalletError` with `"wallet: …"`, `ConfigError` with `"config: …"`).
6
7use thiserror::Error;
8
9#[derive(Debug, Error)]
10pub enum GuardedError {
11    /// The conditional UPDATE matched zero rows — the predicate was not
12    /// satisfied. For counter mutations this is the load-bearing
13    /// "capacity exhausted" signal.
14    #[error("guarded: predicate matched no rows")]
15    NoRowsAffected,
16
17    /// The conditional UPDATE matched more than one row — every guarded
18    /// update is morally a unique-key-equivalent operation; `>1`
19    /// indicates an index/uniqueness bug.
20    #[error(
21        "guarded: predicate matched {affected} rows (expected 1) — likely an index/uniqueness bug"
22    )]
23    TooManyRows { affected: u64 },
24
25    /// The builder was executed with no `set_*` calls — a programming
26    /// error. Without this guard, sea-orm's `Updater::exec` short-circuits
27    /// with `rows_affected: 0`, which would silently look like a
28    /// predicate miss (see Pitfall 1 in 152-RESEARCH.md).
29    #[error("guarded: no columns to set — builder is empty")]
30    EmptyUpdate,
31
32    /// Underlying SeaORM database error.
33    #[error("guarded: db error: {0}")]
34    Db(#[from] sea_orm::DbErr),
35}
36
37#[cfg(test)]
38mod tests {
39    use super::*;
40
41    #[test]
42    fn error_no_rows_affected_displays_message() {
43        assert_eq!(
44            GuardedError::NoRowsAffected.to_string(),
45            "guarded: predicate matched no rows"
46        );
47    }
48
49    #[test]
50    fn error_too_many_rows_displays_message() {
51        assert_eq!(
52            GuardedError::TooManyRows { affected: 3 }.to_string(),
53            "guarded: predicate matched 3 rows (expected 1) — likely an index/uniqueness bug"
54        );
55    }
56
57    #[test]
58    fn error_empty_update_displays_message() {
59        assert_eq!(
60            GuardedError::EmptyUpdate.to_string(),
61            "guarded: no columns to set — builder is empty"
62        );
63    }
64
65    #[test]
66    fn db_from_sea_orm_dberr() {
67        let db_err = sea_orm::DbErr::Custom("test".into());
68        let guarded_err: GuardedError = GuardedError::from(db_err);
69        assert!(matches!(guarded_err, GuardedError::Db(_)));
70    }
71}