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    #[error("authentication required: {0}")]
33    AuthRequired(String),
34    #[error("authentication not required: {0}")]
35    AuthNotRequired(String),
36    #[error("invalid credentials: {0}")]
37    InvalidCredentials(String),
38    #[error("permission denied: {0}")]
39    PermissionDenied(String),
40}
41
42impl From<std::io::Error> for KitError {
43    fn from(e: std::io::Error) -> Self {
44        KitError::Storage(e.to_string())
45    }
46}
47
48impl From<mongreldb_core::MongrelError> for KitError {
49    fn from(e: mongreldb_core::MongrelError) -> Self {
50        use mongreldb_core::MongrelError;
51        match e {
52            MongrelError::Conflict(msg) if is_trigger_error(&msg) => {
53                KitError::TriggerValidation(msg)
54            }
55            MongrelError::Conflict(msg) => KitError::Conflict(msg),
56            MongrelError::InvalidArgument(msg) if is_trigger_error(&msg) => {
57                KitError::TriggerValidation(msg)
58            }
59            MongrelError::InvalidArgument(msg) => KitError::Validation(msg),
60            MongrelError::Schema(msg) => KitError::Validation(msg),
61            MongrelError::ColumnNotFound(msg) => KitError::Integrity(msg),
62            MongrelError::NotFound(msg) => KitError::Integrity(msg),
63            MongrelError::Io(e) => KitError::Storage(e.to_string()),
64            MongrelError::Serialization(e) => KitError::Storage(e.to_string()),
65            MongrelError::ChecksumMismatch { .. }
66            | MongrelError::MagicMismatch { .. }
67            | MongrelError::CorruptWal { .. }
68            | MongrelError::TornWrite { .. } => KitError::Integrity(e.to_string()),
69            MongrelError::EncryptionDisabled
70            | MongrelError::Encryption(_)
71            | MongrelError::Decryption(_) => KitError::Integrity(e.to_string()),
72            MongrelError::Full(msg) => KitError::Storage(msg),
73            MongrelError::Other(msg) => KitError::Storage(msg),
74            MongrelError::AuthRequired => KitError::AuthRequired(e.to_string()),
75            MongrelError::AuthNotRequired => KitError::AuthNotRequired(e.to_string()),
76            MongrelError::InvalidCredentials { username } => KitError::InvalidCredentials(username),
77            MongrelError::PermissionDenied {
78                required,
79                principal,
80            } => KitError::PermissionDenied(format!("{principal} lacks {required}")),
81            _ => KitError::Storage(e.to_string()),
82        }
83    }
84}
85
86fn is_trigger_error(message: &str) -> bool {
87    message.contains("trigger ")
88        || message.contains("Trigger ")
89        || message.contains("external trigger bridge")
90}
91
92impl From<mongreldb_kit_core::schema::SchemaError> for KitError {
93    fn from(e: mongreldb_kit_core::schema::SchemaError) -> Self {
94        KitError::Validation(e.to_string())
95    }
96}
97
98impl From<mongreldb_kit_core::validation::ValidationError> for KitError {
99    fn from(e: mongreldb_kit_core::validation::ValidationError) -> Self {
100        KitError::Validation(e.to_string())
101    }
102}
103
104impl From<mongreldb_kit_core::planner::PlannerError> for KitError {
105    fn from(e: mongreldb_kit_core::planner::PlannerError) -> Self {
106        match e {
107            mongreldb_kit_core::planner::PlannerError::TableNotFound(msg) => {
108                KitError::Integrity(msg)
109            }
110            mongreldb_kit_core::planner::PlannerError::CircularDelete(msg) => {
111                KitError::Restrict(msg)
112            }
113        }
114    }
115}
116
117impl From<serde_json::Error> for KitError {
118    fn from(e: serde_json::Error) -> Self {
119        KitError::Storage(e.to_string())
120    }
121}
122
123impl From<mongreldb_query::MongrelQueryError> for KitError {
124    fn from(e: mongreldb_query::MongrelQueryError) -> Self {
125        use mongreldb_query::MongrelQueryError;
126        match e {
127            // Core errors carry the engine's declarative constraint failures
128            // (unique / FK / check / conflict) — route them through the same
129            // mapping as direct core errors so callers see the right category.
130            MongrelQueryError::Core(core) => KitError::from(core),
131            MongrelQueryError::Schema(msg) => KitError::Validation(msg),
132            MongrelQueryError::Arrow(msg) | MongrelQueryError::DataFusion(msg) => {
133                KitError::Storage(msg)
134            }
135            _ => KitError::Storage(e.to_string()),
136        }
137    }
138}
139
140#[cfg(test)]
141mod tests {
142    use super::KitError;
143
144    #[test]
145    fn maps_trigger_core_errors_to_trigger_validation() {
146        let conflict = KitError::from(mongreldb_core::MongrelError::Conflict(
147            "trigger raised".into(),
148        ));
149        assert_eq!(
150            conflict,
151            KitError::TriggerValidation("trigger raised".into())
152        );
153
154        let invalid = KitError::from(mongreldb_core::MongrelError::InvalidArgument(
155            "external trigger bridge rejected".into(),
156        ));
157        assert_eq!(
158            invalid,
159            KitError::TriggerValidation("external trigger bridge rejected".into())
160        );
161    }
162}