Skip to main content

mongreldb_kit/
error.rs

1//! Error model for `mongreldb-kit`.
2//!
3//! Storage errors from MongrelDB core and validation errors from the core model
4//! are folded into a small, stable set of categories so consumers can handle
5//! failures without depending on internal crate details.
6
7use thiserror::Error;
8
9pub type Result<T> = std::result::Result<T, KitError>;
10
11/// A storage/transaction error in kit terminology.
12#[derive(Debug, Error, Clone, PartialEq)]
13pub enum KitError {
14    #[error("validation error: {0}")]
15    Validation(String),
16    #[error("duplicate key: {0}")]
17    Duplicate(String),
18    #[error("foreign key violation: {0}")]
19    ForeignKey(String),
20    #[error("restrict violation: {0}")]
21    Restrict(String),
22    #[error("migration error: {0}")]
23    Migration(String),
24    #[error("conflict: {0}")]
25    Conflict(String),
26    #[error("trigger validation error: {0}")]
27    TriggerValidation(String),
28    #[error("storage error: {0}")]
29    Storage(String),
30    #[error("integrity error: {0}")]
31    Integrity(String),
32}
33
34impl From<std::io::Error> for KitError {
35    fn from(e: std::io::Error) -> Self {
36        KitError::Storage(e.to_string())
37    }
38}
39
40impl From<mongreldb_core::MongrelError> for KitError {
41    fn from(e: mongreldb_core::MongrelError) -> Self {
42        use mongreldb_core::MongrelError;
43        match e {
44            MongrelError::Conflict(msg) if is_trigger_error(&msg) => {
45                KitError::TriggerValidation(msg)
46            }
47            MongrelError::Conflict(msg) => KitError::Conflict(msg),
48            MongrelError::InvalidArgument(msg) if is_trigger_error(&msg) => {
49                KitError::TriggerValidation(msg)
50            }
51            MongrelError::InvalidArgument(msg) => KitError::Validation(msg),
52            MongrelError::Schema(msg) => KitError::Validation(msg),
53            MongrelError::ColumnNotFound(msg) => KitError::Integrity(msg),
54            MongrelError::NotFound(msg) => KitError::Integrity(msg),
55            MongrelError::Io(e) => KitError::Storage(e.to_string()),
56            MongrelError::Serialization(e) => KitError::Storage(e.to_string()),
57            MongrelError::ChecksumMismatch { .. }
58            | MongrelError::MagicMismatch { .. }
59            | MongrelError::CorruptWal { .. }
60            | MongrelError::TornWrite { .. } => KitError::Integrity(e.to_string()),
61            MongrelError::EncryptionDisabled
62            | MongrelError::Encryption(_)
63            | MongrelError::Decryption(_) => KitError::Integrity(e.to_string()),
64            MongrelError::Full(msg) => KitError::Storage(msg),
65            MongrelError::Other(msg) => KitError::Storage(msg),
66            _ => KitError::Storage(e.to_string()),
67        }
68    }
69}
70
71fn is_trigger_error(message: &str) -> bool {
72    message.contains("trigger ")
73        || message.contains("Trigger ")
74        || message.contains("external trigger bridge")
75}
76
77impl From<mongreldb_kit_core::schema::SchemaError> for KitError {
78    fn from(e: mongreldb_kit_core::schema::SchemaError) -> Self {
79        KitError::Validation(e.to_string())
80    }
81}
82
83impl From<mongreldb_kit_core::validation::ValidationError> for KitError {
84    fn from(e: mongreldb_kit_core::validation::ValidationError) -> Self {
85        KitError::Validation(e.to_string())
86    }
87}
88
89impl From<mongreldb_kit_core::planner::PlannerError> for KitError {
90    fn from(e: mongreldb_kit_core::planner::PlannerError) -> Self {
91        match e {
92            mongreldb_kit_core::planner::PlannerError::TableNotFound(msg) => {
93                KitError::Integrity(msg)
94            }
95            mongreldb_kit_core::planner::PlannerError::CircularDelete(msg) => {
96                KitError::Restrict(msg)
97            }
98        }
99    }
100}
101
102impl From<serde_json::Error> for KitError {
103    fn from(e: serde_json::Error) -> Self {
104        KitError::Storage(e.to_string())
105    }
106}
107
108impl From<mongreldb_query::MongrelQueryError> for KitError {
109    fn from(e: mongreldb_query::MongrelQueryError) -> Self {
110        use mongreldb_query::MongrelQueryError;
111        match e {
112            // Core errors carry the engine's declarative constraint failures
113            // (unique / FK / check / conflict) — route them through the same
114            // mapping as direct core errors so callers see the right category.
115            MongrelQueryError::Core(core) => KitError::from(core),
116            MongrelQueryError::Schema(msg) => KitError::Validation(msg),
117            MongrelQueryError::Arrow(msg) | MongrelQueryError::DataFusion(msg) => {
118                KitError::Storage(msg)
119            }
120            _ => KitError::Storage(e.to_string()),
121        }
122    }
123}
124
125#[cfg(test)]
126mod tests {
127    use super::KitError;
128
129    #[test]
130    fn maps_trigger_core_errors_to_trigger_validation() {
131        let conflict = KitError::from(mongreldb_core::MongrelError::Conflict(
132            "trigger raised".into(),
133        ));
134        assert_eq!(
135            conflict,
136            KitError::TriggerValidation("trigger raised".into())
137        );
138
139        let invalid = KitError::from(mongreldb_core::MongrelError::InvalidArgument(
140            "external trigger bridge rejected".into(),
141        ));
142        assert_eq!(
143            invalid,
144            KitError::TriggerValidation("external trigger bridge rejected".into())
145        );
146    }
147}