Skip to main content

floz_orm/
error.rs

1//! Error types for floz operations.
2
3/// All errors that can be returned by floz operations.
4#[derive(Debug, thiserror::Error)]
5pub enum FlozError {
6    /// Record not found (used by `get()`)
7    #[error("Record not found")]
8    NotFound,
9
10    /// Unique constraint violated
11    #[error("Unique constraint violated: {0}")]
12    UniqueViolation(String),
13
14    /// Foreign key constraint violated
15    #[error("Foreign key constraint violated: {0}")]
16    ForeignKeyViolation(String),
17
18    /// Attempted to `save()` an entity with default/uninitialized primary key
19    #[error("Cannot save — entity has no primary key (use create() for new records)")]
20    UnsavedEntity,
21
22    /// Bulk insert rows have mismatched column sets
23    #[error("Bulk insert row {row} is missing column `{column}`")]
24    MismatchedBulkInsertColumns {
25        row: usize,
26        column: String,
27    },
28
29    /// DELETE without `.where_()` or `.all()` — safety guard
30    #[error("DELETE requires .where_() or .all() — refusing to delete all rows without explicit intent")]
31    UnguardedDelete,
32
33    /// UPDATE without `.where_()` — safety guard
34    #[error("UPDATE requires .where_() — refusing to update all rows without explicit intent")]
35    UnguardedUpdate,
36
37    /// Underlying database error from sqlx
38    #[error("Database error: {0}")]
39    Database(#[from] sqlx::Error),
40
41    /// Serialization/deserialization error
42    #[error("Serialization error: {0}")]
43    Serialization(String),
44
45    /// Validation error from a lifecycle hook
46    #[error("Validation error: {0}")]
47    ValidationError(String),
48}
49
50#[cfg(test)]
51mod tests {
52    use super::*;
53
54    #[test]
55    fn error_display_not_found() {
56        let e = FlozError::NotFound;
57        assert_eq!(e.to_string(), "Record not found");
58    }
59
60    #[test]
61    fn error_display_unsaved_entity() {
62        let e = FlozError::UnsavedEntity;
63        assert!(e.to_string().contains("create()"));
64    }
65
66    #[test]
67    fn error_display_mismatched_bulk() {
68        let e = FlozError::MismatchedBulkInsertColumns {
69            row: 2,
70            column: "age".to_string(),
71        };
72        assert!(e.to_string().contains("row 2"));
73        assert!(e.to_string().contains("age"));
74    }
75
76    #[test]
77    fn error_display_unguarded_delete() {
78        let e = FlozError::UnguardedDelete;
79        assert!(e.to_string().contains(".where_()"));
80        assert!(e.to_string().contains(".all()"));
81    }
82
83    #[test]
84    fn error_display_unique_violation() {
85        let e = FlozError::UniqueViolation("users_email_key".into());
86        assert!(e.to_string().contains("users_email_key"));
87    }
88
89    #[test]
90    fn error_is_send_sync() {
91        fn assert_send<T: Send>() {}
92        fn assert_sync<T: Sync>() {}
93        assert_send::<FlozError>();
94        assert_sync::<FlozError>();
95    }
96}