1use thiserror::Error;
8
9pub type Result<T> = std::result::Result<T, KitError>;
10
11#[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 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}