1use std::fmt;
4use thiserror::Error;
5
6pub type Result<T> = std::result::Result<T, Error>;
8
9#[derive(Error, Debug)]
11pub enum Error {
12 #[error("I/O error: {0}")]
14 Io(#[from] std::io::Error),
15
16 #[error("Serialization error: {message}")]
18 Serialization {
19 message: String,
20 #[source]
21 source: Option<Box<dyn std::error::Error + Send + Sync>>,
22 },
23
24 #[error("Data corruption: {0}")]
26 Corruption(String),
27
28 #[error("Schema error: {0}")]
30 Schema(String),
31
32 #[error("CQL parse error: {0}")]
34 CqlParse(String),
35
36 #[error("Invalid format: {0}")]
38 InvalidFormat(String),
39
40 #[error("Unsupported format: {0}")]
42 UnsupportedFormat(String),
43
44 #[error("Operation timeout: {0}")]
46 Timeout(String),
47
48 #[error("Invalid path: {0}")]
50 InvalidPath(String),
51
52 #[error("Invalid state: {0}")]
54 InvalidState(String),
55
56 #[error("Query execution error: {0}")]
58 QueryExecution(String),
59
60 #[error("Type conversion error: {0}")]
62 TypeConversion(String),
63
64 #[error("Configuration error: {0}")]
66 Configuration(String),
67
68 #[error("Storage error: {0}")]
70 Storage(String),
71
72 #[error("Memory error: {0}")]
74 Memory(String),
75
76 #[error("Concurrency error: {0}")]
78 Concurrency(String),
79
80 #[error(
87 "write_dir '{path}' is already locked by another process. \
88 Only one Database instance may hold a write_dir at a time."
89 )]
90 WriteDirLocked {
91 path: String,
93 },
94
95 #[error("Not found: {0}")]
97 NotFound(String),
98
99 #[error("Table error: {0}")]
101 Table(String),
102
103 #[error("Already exists: {0}")]
105 AlreadyExists(String),
106
107 #[error("Invalid operation: {0}")]
109 InvalidOperation(String),
110
111 #[error("Constraint violation: {0}")]
113 ConstraintViolation(String),
114
115 #[error("Transaction error: {0}")]
117 Transaction(String),
118
119 #[error("Index error: {0}")]
121 Index(String),
122
123 #[error("Compaction error: {0}")]
125 Compaction(String),
126
127 #[cfg(target_arch = "wasm32")]
129 #[error("WASM error: {0}")]
130 Wasm(String),
131
132 #[error("Internal error: {0}")]
134 Internal(String),
135
136 #[error("Parse error: {0}")]
138 Parse(String),
139
140 #[error("Invalid input: {0}")]
142 InvalidInput(String),
143
144 #[error("Unsupported query: {0}")]
146 UnsupportedQuery(String),
147}
148
149impl Error {
150 pub fn serialization(msg: impl Into<String>) -> Self {
152 Self::Serialization {
153 message: msg.into(),
154 source: None,
155 }
156 }
157
158 pub fn corruption(msg: impl Into<String>) -> Self {
160 Self::Corruption(msg.into())
161 }
162
163 pub fn schema(msg: impl Into<String>) -> Self {
165 Self::Schema(msg.into())
166 }
167
168 pub fn cql_parse(msg: impl Into<String>) -> Self {
170 Self::CqlParse(msg.into())
171 }
172
173 pub fn invalid_format(msg: impl Into<String>) -> Self {
175 Self::InvalidFormat(msg.into())
176 }
177
178 pub fn unsupported_format(msg: impl Into<String>) -> Self {
180 Self::UnsupportedFormat(msg.into())
181 }
182
183 pub fn invalid_path(msg: impl Into<String>) -> Self {
185 Self::InvalidPath(msg.into())
186 }
187
188 pub fn invalid_state(msg: impl Into<String>) -> Self {
190 Self::InvalidState(msg.into())
191 }
192
193 pub fn query_execution(msg: impl Into<String>) -> Self {
195 Self::QueryExecution(msg.into())
196 }
197
198 pub fn type_conversion(msg: impl Into<String>) -> Self {
200 Self::TypeConversion(msg.into())
201 }
202
203 pub fn configuration(msg: impl Into<String>) -> Self {
205 Self::Configuration(msg.into())
206 }
207
208 pub fn storage(msg: impl Into<String>) -> Self {
210 Self::Storage(msg.into())
211 }
212
213 pub fn memory(msg: impl Into<String>) -> Self {
215 Self::Memory(msg.into())
216 }
217
218 pub fn concurrency(msg: impl Into<String>) -> Self {
220 Self::Concurrency(msg.into())
221 }
222
223 pub fn not_found(msg: impl Into<String>) -> Self {
225 Self::NotFound(msg.into())
226 }
227
228 pub fn already_exists(msg: impl Into<String>) -> Self {
230 Self::AlreadyExists(msg.into())
231 }
232
233 pub fn invalid_operation(msg: impl Into<String>) -> Self {
235 Self::InvalidOperation(msg.into())
236 }
237
238 pub fn constraint_violation(msg: impl Into<String>) -> Self {
240 Self::ConstraintViolation(msg.into())
241 }
242
243 pub fn transaction(msg: impl Into<String>) -> Self {
245 Self::Transaction(msg.into())
246 }
247
248 pub fn index(msg: impl Into<String>) -> Self {
250 Self::Index(msg.into())
251 }
252
253 pub fn compaction(msg: impl Into<String>) -> Self {
255 Self::Compaction(msg.into())
256 }
257
258 #[cfg(target_arch = "wasm32")]
260 pub fn wasm(msg: impl Into<String>) -> Self {
261 Self::Wasm(msg.into())
262 }
263
264 pub fn internal(msg: impl Into<String>) -> Self {
266 Self::Internal(msg.into())
267 }
268
269 pub fn invalid_input(msg: impl Into<String>) -> Self {
271 Self::InvalidInput(msg.into())
272 }
273
274 pub fn parse(msg: impl Into<String>) -> Self {
276 Self::Parse(msg.into())
277 }
278
279 pub fn unsupported_query(msg: impl Into<String>) -> Self {
281 Self::UnsupportedQuery(msg.into())
282 }
283
284 pub fn write_dir_locked(path: impl Into<String>) -> Self {
286 Self::WriteDirLocked { path: path.into() }
287 }
288
289 pub fn table_not_found(msg: impl Into<String>) -> Self {
291 Self::NotFound(format!("Table not found: {}", msg.into()))
292 }
293
294 pub fn ambiguous_table(msg: impl Into<String>) -> Self {
296 Self::Table(format!("Ambiguous table reference: {}", msg.into()))
297 }
298
299 pub fn is_recoverable(&self) -> bool {
301 match self {
302 Error::Io(_) => true,
304 Error::Concurrency(_) => true,
305 Error::Memory(_) => true,
306
307 Error::Corruption(_) => false,
309 Error::Schema(_) => false,
310 Error::CqlParse(_) => false,
311 Error::Configuration(_) => false,
312
313 Error::Storage(_) => true,
315 Error::QueryExecution(_) => false,
316 Error::TypeConversion(_) => false,
317 Error::NotFound(_) => false,
318 Error::AlreadyExists(_) => false,
319 Error::InvalidOperation(_) => false,
320 Error::ConstraintViolation(_) => false,
321 Error::Transaction(_) => true,
322 Error::Index(_) => true,
323 Error::Compaction(_) => true,
324
325 Error::Table(_) => false,
327
328 Error::WriteDirLocked { .. } => false,
330
331 #[cfg(target_arch = "wasm32")]
332 Error::Wasm(_) => false,
333
334 Error::Serialization { .. } => false,
335 Error::Internal(_) => false,
336 Error::Parse(_) => false,
337 Error::InvalidInput(_) => false,
338 Error::InvalidFormat(_) => false,
339 Error::UnsupportedFormat(_) => false,
340 Error::InvalidPath(_) => false,
341 Error::InvalidState(_) => false,
342 Error::Timeout(_) => false,
343 Error::UnsupportedQuery(_) => false,
344 }
345 }
346
347 pub fn category(&self) -> ErrorCategory {
349 match self {
350 Error::Io(_) => ErrorCategory::System,
351 Error::Serialization { .. } => ErrorCategory::Data,
352 Error::Corruption(_) => ErrorCategory::Data,
353 Error::Schema(_) => ErrorCategory::Schema,
354 Error::CqlParse(_) => ErrorCategory::Query,
355 Error::QueryExecution(_) => ErrorCategory::Query,
356 Error::TypeConversion(_) => ErrorCategory::Data,
357 Error::Configuration(_) => ErrorCategory::Configuration,
358 Error::Storage(_) => ErrorCategory::Storage,
359 Error::Memory(_) => ErrorCategory::System,
360 Error::Concurrency(_) => ErrorCategory::Concurrency,
361 Error::NotFound(_) => ErrorCategory::NotFound,
362 Error::AlreadyExists(_) => ErrorCategory::Conflict,
363 Error::InvalidOperation(_) => ErrorCategory::Logic,
364 Error::ConstraintViolation(_) => ErrorCategory::Constraint,
365 Error::Transaction(_) => ErrorCategory::Transaction,
366 Error::Index(_) => ErrorCategory::Storage,
367 Error::Compaction(_) => ErrorCategory::Storage,
368
369 Error::Table(_) => ErrorCategory::Schema,
371
372 Error::WriteDirLocked { .. } => ErrorCategory::Concurrency,
374
375 #[cfg(target_arch = "wasm32")]
376 Error::Wasm(_) => ErrorCategory::Platform,
377
378 Error::Internal(_) => ErrorCategory::Internal,
379 Error::Parse(_) => ErrorCategory::Data,
380 Error::InvalidInput(_) => ErrorCategory::Data,
381 Error::InvalidFormat(_) => ErrorCategory::Data,
382 Error::UnsupportedFormat(_) => ErrorCategory::Data,
383 Error::InvalidPath(_) => ErrorCategory::System,
384 Error::InvalidState(_) => ErrorCategory::Logic,
385 Error::Timeout(_) => ErrorCategory::System,
386 Error::UnsupportedQuery(_) => ErrorCategory::Query,
387 }
388 }
389}
390
391#[derive(Debug, Clone, Copy, PartialEq, Eq)]
393pub enum ErrorCategory {
394 System,
396 Data,
398 Schema,
400 Query,
402 Configuration,
404 Storage,
406 Concurrency,
408 NotFound,
410 Conflict,
412 Logic,
414 Constraint,
416 Transaction,
418 Platform,
420 Internal,
422}
423
424impl fmt::Display for ErrorCategory {
425 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
426 let name = match self {
427 ErrorCategory::System => "System",
428 ErrorCategory::Data => "Data",
429 ErrorCategory::Schema => "Schema",
430 ErrorCategory::Query => "Query",
431 ErrorCategory::Configuration => "Configuration",
432 ErrorCategory::Storage => "Storage",
433 ErrorCategory::Concurrency => "Concurrency",
434 ErrorCategory::NotFound => "NotFound",
435 ErrorCategory::Conflict => "Conflict",
436 ErrorCategory::Logic => "Logic",
437 ErrorCategory::Constraint => "Constraint",
438 ErrorCategory::Transaction => "Transaction",
439 ErrorCategory::Platform => "Platform",
440 ErrorCategory::Internal => "Internal",
441 };
442 write!(f, "{}", name)
443 }
444}
445
446impl From<bincode::Error> for Error {
448 fn from(err: bincode::Error) -> Self {
449 Error::Serialization {
450 message: err.to_string(),
451 source: Some(Box::new(err)),
452 }
453 }
454}
455
456impl From<serde_json::Error> for Error {
458 fn from(err: serde_json::Error) -> Self {
459 Error::Serialization {
460 message: err.to_string(),
461 source: Some(Box::new(err)),
462 }
463 }
464}
465
466impl<I> From<nom::Err<nom::error::Error<I>>> for Error
468where
469 I: std::fmt::Debug,
470{
471 fn from(err: nom::Err<nom::error::Error<I>>) -> Self {
472 Error::CqlParse(format!("Parse error: {:?}", err))
473 }
474}
475
476pub type ParseResult<I, O> = nom::IResult<I, O, Error>;
478
479#[derive(Debug, Clone)]
481pub struct ParseError {
482 pub message: String,
483}
484
485impl std::fmt::Display for ParseError {
486 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
487 write!(f, "{}", self.message)
488 }
489}
490
491impl std::error::Error for ParseError {}
492
493#[cfg(test)]
494mod tests {
495 use super::*;
496
497 #[test]
498 fn test_error_from_conversions() {
499 let io_err = std::io::Error::other("test error");
501 let bincode_err = bincode::Error::new(bincode::ErrorKind::Io(io_err));
502 let error = Error::from(bincode_err);
503 assert!(matches!(error, Error::Serialization { .. }));
504
505 let json_err = serde_json::from_str::<serde_json::Value>("invalid json").unwrap_err();
507 let error = Error::from(json_err);
508 assert!(matches!(error, Error::Serialization { .. }));
509
510 let nom_err = nom::Err::Error(nom::error::Error::new(
512 "test input",
513 nom::error::ErrorKind::Tag,
514 ));
515 let error = Error::from(nom_err);
516 assert!(matches!(error, Error::CqlParse(_)));
517 }
518
519 #[test]
520 fn test_parse_error_display() {
521 let parse_error = ParseError {
523 message: "test parse error".to_string(),
524 };
525 let display_str = format!("{}", parse_error);
526 assert_eq!(display_str, "test parse error");
527 }
528
529 #[test]
530 #[cfg(target_arch = "wasm32")]
531 fn test_wasm_error_creation() {
532 let err = Error::wasm("WASM error");
534 assert!(matches!(err, Error::Wasm(_)));
535 assert!(!err.is_recoverable());
536 assert_eq!(err.category(), ErrorCategory::Platform);
537 }
538
539 #[test]
540 fn test_new_error_types_coverage() {
541 let table_err = Error::Table("table error".to_string());
543 assert!(!table_err.is_recoverable());
544 assert_eq!(table_err.category(), ErrorCategory::Schema);
545 }
546
547 #[test]
548 fn test_error_creation() {
549 let err = Error::storage("test error");
550 assert!(matches!(err, Error::Storage(_)));
551 assert_eq!(err.to_string(), "Storage error: test error");
552 }
553
554 #[test]
555 fn test_error_categories() {
556 assert_eq!(Error::storage("test").category(), ErrorCategory::Storage);
557 assert_eq!(Error::schema("test").category(), ErrorCategory::Schema);
558 assert_eq!(Error::cql_parse("test").category(), ErrorCategory::Query);
559 }
560
561 #[test]
562 fn test_error_recoverability() {
563 assert!(Error::concurrency("test").is_recoverable());
564 assert!(!Error::corruption("test").is_recoverable());
565 assert!(!Error::schema("test").is_recoverable());
566 }
567
568 #[test]
569 fn test_all_error_constructors() {
570 let _ = Error::serialization("test");
572 let _ = Error::corruption("test");
573 let _ = Error::schema("test");
574 let _ = Error::cql_parse("test");
575 let _ = Error::invalid_format("test");
576 let _ = Error::unsupported_format("test");
577 let _ = Error::invalid_path("test");
578 let _ = Error::invalid_state("test");
579 let _ = Error::query_execution("test");
580 let _ = Error::type_conversion("test");
581 let _ = Error::configuration("test");
582 let _ = Error::storage("test");
583 let _ = Error::memory("test");
584 let _ = Error::concurrency("test");
585 let _ = Error::not_found("test");
586 let _ = Error::already_exists("test");
587 let _ = Error::invalid_operation("test");
588 let _ = Error::constraint_violation("test");
589 let _ = Error::transaction("test");
590 let _ = Error::index("test");
591 let _ = Error::compaction("test");
592 let _ = Error::internal("test");
593 let _ = Error::invalid_input("test");
594 let _ = Error::parse("test");
595 let _ = Error::write_dir_locked("/tmp/test-dir");
596 }
597
598 #[test]
599 fn test_all_error_categories() {
600 assert_eq!(Error::serialization("test").category(), ErrorCategory::Data);
602 assert_eq!(Error::corruption("test").category(), ErrorCategory::Data);
603 assert_eq!(Error::cql_parse("test").category(), ErrorCategory::Query);
604 assert_eq!(
605 Error::invalid_format("test").category(),
606 ErrorCategory::Data
607 );
608 assert_eq!(
609 Error::unsupported_format("test").category(),
610 ErrorCategory::Data
611 );
612 assert_eq!(
613 Error::invalid_path("test").category(),
614 ErrorCategory::System
615 );
616 assert_eq!(
617 Error::invalid_state("test").category(),
618 ErrorCategory::Logic
619 );
620 assert_eq!(
621 Error::query_execution("test").category(),
622 ErrorCategory::Query
623 );
624 assert_eq!(
625 Error::type_conversion("test").category(),
626 ErrorCategory::Data
627 );
628 assert_eq!(
629 Error::configuration("test").category(),
630 ErrorCategory::Configuration
631 );
632 assert_eq!(Error::memory("test").category(), ErrorCategory::System);
633 assert_eq!(
634 Error::concurrency("test").category(),
635 ErrorCategory::Concurrency
636 );
637 assert_eq!(Error::not_found("test").category(), ErrorCategory::NotFound);
638 assert_eq!(
639 Error::already_exists("test").category(),
640 ErrorCategory::Conflict
641 );
642 assert_eq!(
643 Error::invalid_operation("test").category(),
644 ErrorCategory::Logic
645 );
646 assert_eq!(
647 Error::constraint_violation("test").category(),
648 ErrorCategory::Constraint
649 );
650 assert_eq!(
651 Error::transaction("test").category(),
652 ErrorCategory::Transaction
653 );
654 assert_eq!(Error::index("test").category(), ErrorCategory::Storage);
655 assert_eq!(Error::compaction("test").category(), ErrorCategory::Storage);
656 assert_eq!(Error::internal("test").category(), ErrorCategory::Internal);
657 assert_eq!(Error::invalid_input("test").category(), ErrorCategory::Data);
658 assert_eq!(Error::parse("test").category(), ErrorCategory::Data);
659 assert_eq!(
660 Error::write_dir_locked("/tmp/test").category(),
661 ErrorCategory::Concurrency
662 );
663 }
664
665 #[test]
666 fn test_all_error_recoverability() {
667 assert!(Error::memory("test").is_recoverable());
669 assert!(Error::storage("test").is_recoverable());
670 assert!(Error::transaction("test").is_recoverable());
671 assert!(Error::index("test").is_recoverable());
672 assert!(Error::compaction("test").is_recoverable());
673
674 assert!(!Error::serialization("test").is_recoverable());
675 assert!(!Error::cql_parse("test").is_recoverable());
676 assert!(!Error::invalid_format("test").is_recoverable());
677 assert!(!Error::unsupported_format("test").is_recoverable());
678 assert!(!Error::invalid_path("test").is_recoverable());
679 assert!(!Error::invalid_state("test").is_recoverable());
680 assert!(!Error::query_execution("test").is_recoverable());
681 assert!(!Error::type_conversion("test").is_recoverable());
682 assert!(!Error::configuration("test").is_recoverable());
683 assert!(!Error::not_found("test").is_recoverable());
684 assert!(!Error::already_exists("test").is_recoverable());
685 assert!(!Error::invalid_operation("test").is_recoverable());
686 assert!(!Error::constraint_violation("test").is_recoverable());
687 assert!(!Error::internal("test").is_recoverable());
688 assert!(!Error::invalid_input("test").is_recoverable());
689 assert!(!Error::parse("test").is_recoverable());
690 assert!(!Error::write_dir_locked("/tmp/test").is_recoverable());
691 }
692
693 #[test]
694 fn test_error_category_display() {
695 assert_eq!(ErrorCategory::System.to_string(), "System");
697 assert_eq!(ErrorCategory::Data.to_string(), "Data");
698 assert_eq!(ErrorCategory::Schema.to_string(), "Schema");
699 assert_eq!(ErrorCategory::Query.to_string(), "Query");
700 assert_eq!(ErrorCategory::Configuration.to_string(), "Configuration");
701 assert_eq!(ErrorCategory::Storage.to_string(), "Storage");
702 assert_eq!(ErrorCategory::Concurrency.to_string(), "Concurrency");
703 assert_eq!(ErrorCategory::NotFound.to_string(), "NotFound");
704 assert_eq!(ErrorCategory::Conflict.to_string(), "Conflict");
705 assert_eq!(ErrorCategory::Logic.to_string(), "Logic");
706 assert_eq!(ErrorCategory::Constraint.to_string(), "Constraint");
707 assert_eq!(ErrorCategory::Transaction.to_string(), "Transaction");
708 assert_eq!(ErrorCategory::Platform.to_string(), "Platform");
709 assert_eq!(ErrorCategory::Internal.to_string(), "Internal");
710 }
711
712 #[test]
713 fn test_error_from_io_error() {
714 let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
716 let cqlite_err: Error = io_err.into();
717 assert!(matches!(cqlite_err, Error::Io(_)));
718 assert_eq!(cqlite_err.category(), ErrorCategory::System);
719 assert!(cqlite_err.is_recoverable());
720 }
721
722 #[test]
723 fn test_result_type_alias() {
724 let success: Result<i32> = Ok(42);
726 let failure: Result<i32> = Err(Error::storage("test error"));
727
728 assert!(success.is_ok());
729 if let Ok(value) = success {
730 assert_eq!(value, 42);
731 }
732 assert!(failure.is_err());
733 }
734}