1use serde::{Deserialize, Serialize};
12use serde_json::{json, Value};
13use thiserror::Error;
14
15use crate::wire::RpcError;
16
17pub const ERR_SCHEMA_VALIDATION: i32 = 1000;
18pub const ERR_INVALID_MODEL: i32 = 1001;
19pub const ERR_INVALID_FIELD: i32 = 1002;
20pub const ERR_TYPE_MISMATCH: i32 = 1003;
21
22pub const ERR_QUERY_PLANNING: i32 = 2000;
23pub const ERR_INVALID_FILTER: i32 = 2001;
24pub const ERR_INVALID_ORDERBY: i32 = 2002;
25pub const ERR_UNSUPPORTED_OPERATION: i32 = 2003;
26
27pub const ERR_DATABASE_EXECUTION: i32 = 3000;
28pub const ERR_CONNECTION_FAILED: i32 = 3001;
29pub const ERR_CONSTRAINT_VIOLATION: i32 = 3002;
30pub const ERR_QUERY_TIMEOUT: i32 = 3003;
31pub const ERR_RECORD_NOT_FOUND: i32 = 3004;
32pub const ERR_UNIQUE_CONSTRAINT: i32 = 3005;
33pub const ERR_FOREIGN_KEY_CONSTRAINT: i32 = 3006;
34pub const ERR_CHECK_CONSTRAINT: i32 = 3007;
35pub const ERR_NULL_CONSTRAINT: i32 = 3008;
36pub const ERR_DEADLOCK: i32 = 3009;
37pub const ERR_SERIALIZATION_FAILURE: i32 = 3010;
38
39pub const ERR_TRANSACTION_NOT_FOUND: i32 = 4001;
40pub const ERR_TRANSACTION_TIMEOUT: i32 = 4002;
41pub const ERR_TRANSACTION_ALREADY_CLOSED: i32 = 4003;
42pub const ERR_TRANSACTION_FAILED: i32 = 4004;
43
44pub const ERR_INTERNAL: i32 = 9000;
45pub const ERR_UNSUPPORTED_PROTOCOL_VERSION: i32 = 9001;
46pub const ERR_INVALID_METHOD: i32 = 9002;
47pub const ERR_INVALID_REQUEST_PARAMS: i32 = 9003;
48
49#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
51#[serde(rename_all = "camelCase")]
52pub struct ProtocolErrorCause {
53 pub code: i32,
54 pub message: String,
55 #[serde(skip_serializing_if = "Option::is_none")]
56 pub data: Option<Value>,
57}
58
59#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
61#[serde(rename_all = "camelCase")]
62pub struct BatchOperationErrorData {
63 pub batch_operation_index: usize,
64 pub batch_operation_method: String,
65 pub cause: ProtocolErrorCause,
66}
67
68#[derive(Debug, Error)]
70pub enum ProtocolError {
71 #[error("Schema validation error: {0}")]
72 SchemaValidation(String),
73
74 #[error("Invalid model: {0}")]
75 InvalidModel(String),
76
77 #[error("Invalid field: {0}")]
78 InvalidField(String),
79
80 #[error("Type mismatch: {0}")]
81 TypeMismatch(String),
82
83 #[error("Query planning error: {0}")]
84 QueryPlanning(String),
85
86 #[error("Invalid filter: {0}")]
87 InvalidFilter(String),
88
89 #[error("Invalid order by: {0}")]
90 InvalidOrderBy(String),
91
92 #[error("Unsupported operation: {0}")]
93 UnsupportedOperation(String),
94
95 #[error("Database execution error: {0}")]
96 DatabaseExecution(String),
97
98 #[error("Connection failed: {0}")]
99 ConnectionFailed(String),
100
101 #[error("Constraint violation: {0}")]
102 ConstraintViolation(String),
103
104 #[error("Unique constraint violation: {0}")]
105 UniqueConstraintViolation(String),
106
107 #[error("Foreign key constraint violation: {0}")]
108 ForeignKeyConstraintViolation(String),
109
110 #[error("Check constraint violation: {0}")]
111 CheckConstraintViolation(String),
112
113 #[error("NOT NULL constraint violation: {0}")]
114 NullConstraintViolation(String),
115
116 #[error("Deadlock detected: {0}")]
117 Deadlock(String),
118
119 #[error("Serialization failure: {0}")]
120 SerializationFailure(String),
121
122 #[error("Query timeout: {0}")]
123 QueryTimeout(String),
124
125 #[error("Record not found: {0}")]
126 RecordNotFound(String),
127
128 #[error("Transaction not found: {0}")]
129 TransactionNotFound(String),
130
131 #[error("Transaction timed out: {0}")]
132 TransactionTimeout(String),
133
134 #[error("Transaction already closed: {0}")]
135 TransactionAlreadyClosed(String),
136
137 #[error("Transaction failed: {0}")]
138 TransactionFailed(String),
139
140 #[error("{source}")]
141 BatchOperationFailed {
142 index: usize,
143 method: String,
144 #[source]
145 source: Box<ProtocolError>,
146 },
147
148 #[error("Internal engine error: {0}")]
149 Internal(String),
150
151 #[error("Unsupported protocol version: {actual}, expected {expected}")]
152 UnsupportedProtocolVersion { actual: u32, expected: u32 },
153
154 #[error("Invalid method: {0}")]
155 InvalidMethod(String),
156
157 #[error("Invalid request params: {0}")]
158 InvalidParams(String),
159
160 #[error("Serialization error: {0}")]
161 Serialization(String),
162}
163
164impl ProtocolError {
165 pub fn code(&self) -> i32 {
167 match self {
168 ProtocolError::SchemaValidation(_) => ERR_SCHEMA_VALIDATION,
169 ProtocolError::InvalidModel(_) => ERR_INVALID_MODEL,
170 ProtocolError::InvalidField(_) => ERR_INVALID_FIELD,
171 ProtocolError::TypeMismatch(_) => ERR_TYPE_MISMATCH,
172 ProtocolError::QueryPlanning(_) => ERR_QUERY_PLANNING,
173 ProtocolError::InvalidFilter(_) => ERR_INVALID_FILTER,
174 ProtocolError::InvalidOrderBy(_) => ERR_INVALID_ORDERBY,
175 ProtocolError::UnsupportedOperation(_) => ERR_UNSUPPORTED_OPERATION,
176 ProtocolError::DatabaseExecution(_) => ERR_DATABASE_EXECUTION,
177 ProtocolError::ConnectionFailed(_) => ERR_CONNECTION_FAILED,
178 ProtocolError::ConstraintViolation(_) => ERR_CONSTRAINT_VIOLATION,
179 ProtocolError::UniqueConstraintViolation(_) => ERR_UNIQUE_CONSTRAINT,
180 ProtocolError::ForeignKeyConstraintViolation(_) => ERR_FOREIGN_KEY_CONSTRAINT,
181 ProtocolError::CheckConstraintViolation(_) => ERR_CHECK_CONSTRAINT,
182 ProtocolError::NullConstraintViolation(_) => ERR_NULL_CONSTRAINT,
183 ProtocolError::Deadlock(_) => ERR_DEADLOCK,
184 ProtocolError::SerializationFailure(_) => ERR_SERIALIZATION_FAILURE,
185 ProtocolError::QueryTimeout(_) => ERR_QUERY_TIMEOUT,
186 ProtocolError::RecordNotFound(_) => ERR_RECORD_NOT_FOUND,
187 ProtocolError::TransactionNotFound(_) => ERR_TRANSACTION_NOT_FOUND,
188 ProtocolError::TransactionTimeout(_) => ERR_TRANSACTION_TIMEOUT,
189 ProtocolError::TransactionAlreadyClosed(_) => ERR_TRANSACTION_ALREADY_CLOSED,
190 ProtocolError::TransactionFailed(_) => ERR_TRANSACTION_FAILED,
191 ProtocolError::BatchOperationFailed { source, .. } => source.code(),
192 ProtocolError::Internal(_) => ERR_INTERNAL,
193 ProtocolError::UnsupportedProtocolVersion { .. } => ERR_UNSUPPORTED_PROTOCOL_VERSION,
194 ProtocolError::InvalidMethod(_) => ERR_INVALID_METHOD,
195 ProtocolError::InvalidParams(_) => ERR_INVALID_REQUEST_PARAMS,
196 ProtocolError::Serialization(_) => ERR_INTERNAL,
197 }
198 }
199
200 fn rpc_data(&self) -> Option<Value> {
201 match self {
202 ProtocolError::UnsupportedProtocolVersion { actual, expected } => Some(json!({
203 "actual": actual,
204 "expected": expected,
205 })),
206 ProtocolError::BatchOperationFailed {
207 index,
208 method,
209 source,
210 } => Some(
211 serde_json::to_value(BatchOperationErrorData {
212 batch_operation_index: *index,
213 batch_operation_method: method.clone(),
214 cause: ProtocolErrorCause {
215 code: source.code(),
216 message: source.to_string(),
217 data: source.rpc_data(),
218 },
219 })
220 .expect("serializing batch-operation error data should succeed"),
221 ),
222 _ => None,
223 }
224 }
225}
226
227impl From<ProtocolError> for RpcError {
228 fn from(err: ProtocolError) -> Self {
229 let code = err.code();
230 let message = err.to_string();
231 let data = err.rpc_data();
232 RpcError {
233 code,
234 message,
235 data,
236 }
237 }
238}
239
240impl From<serde_json::Error> for ProtocolError {
241 fn from(err: serde_json::Error) -> Self {
242 ProtocolError::Serialization(err.to_string())
243 }
244}
245
246pub type Result<T> = std::result::Result<T, ProtocolError>;
248
249#[cfg(test)]
250mod tests {
251 use super::*;
252 use serde_json::Value;
253
254 #[test]
255 fn test_error_codes() {
256 assert_eq!(
257 ProtocolError::SchemaValidation("test".into()).code(),
258 ERR_SCHEMA_VALIDATION
259 );
260 assert_eq!(
261 ProtocolError::InvalidModel("User".into()).code(),
262 ERR_INVALID_MODEL
263 );
264 assert_eq!(
265 ProtocolError::QueryPlanning("bad query".into()).code(),
266 ERR_QUERY_PLANNING
267 );
268 assert_eq!(
269 ProtocolError::DatabaseExecution("timeout".into()).code(),
270 ERR_DATABASE_EXECUTION
271 );
272 assert_eq!(
273 ProtocolError::UnsupportedProtocolVersion {
274 actual: 2,
275 expected: 1
276 }
277 .code(),
278 ERR_UNSUPPORTED_PROTOCOL_VERSION
279 );
280 }
281
282 #[test]
283 fn test_error_to_rpc_error() {
284 let err = ProtocolError::InvalidModel("Post".to_string());
285 let rpc_err: RpcError = err.into();
286
287 assert_eq!(rpc_err.code, ERR_INVALID_MODEL);
288 assert_eq!(rpc_err.message, "Invalid model: Post");
289 assert!(rpc_err.data.is_none());
290 }
291
292 #[test]
293 fn test_batch_operation_error_to_rpc_error_preserves_source_code_and_context() {
294 let err = ProtocolError::BatchOperationFailed {
295 index: 1,
296 method: "query.create".to_string(),
297 source: Box::new(ProtocolError::UniqueConstraintViolation(
298 "duplicate email".to_string(),
299 )),
300 };
301
302 let rpc_err: RpcError = err.into();
303
304 assert_eq!(rpc_err.code, ERR_UNIQUE_CONSTRAINT);
305 assert_eq!(
306 rpc_err.message,
307 "Unique constraint violation: duplicate email"
308 );
309
310 let data = rpc_err
311 .data
312 .expect("batch failures should include error.data");
313 assert_eq!(data["batchOperationIndex"], 1);
314 assert_eq!(data["batchOperationMethod"], "query.create");
315 assert_eq!(data["cause"]["code"], ERR_UNIQUE_CONSTRAINT);
316 assert_eq!(
317 data["cause"]["message"],
318 "Unique constraint violation: duplicate email"
319 );
320 }
321
322 #[test]
323 fn test_unsupported_protocol_version_rpc_error_includes_structured_data() {
324 let rpc_err: RpcError = ProtocolError::UnsupportedProtocolVersion {
325 actual: 2,
326 expected: 1,
327 }
328 .into();
329
330 assert_eq!(rpc_err.code, ERR_UNSUPPORTED_PROTOCOL_VERSION);
331 let data = rpc_err
332 .data
333 .expect("version mismatch should include error.data");
334 assert_eq!(data["actual"], 2);
335 assert_eq!(data["expected"], 1);
336 }
337
338 #[test]
339 fn test_error_display() {
340 let err = ProtocolError::UnsupportedProtocolVersion {
341 actual: 3,
342 expected: 1,
343 };
344 assert_eq!(
345 err.to_string(),
346 "Unsupported protocol version: 3, expected 1"
347 );
348 }
349
350 #[test]
351 fn test_serde_error_conversion() {
352 let bad_json = "{invalid json}";
353 let err: serde_json::Error = serde_json::from_str::<Value>(bad_json).unwrap_err();
354 let protocol_err: ProtocolError = err.into();
355
356 match protocol_err {
357 ProtocolError::Serialization(msg) => {
358 assert!(!msg.is_empty());
359 }
360 _ => panic!("Expected Serialization error"),
361 }
362 }
363}