Skip to main content

cqlite_core/
error.rs

1//! Error types for CQLite
2
3use std::fmt;
4use thiserror::Error;
5
6/// Result type alias for CQLite operations
7pub type Result<T> = std::result::Result<T, Error>;
8
9/// Main error type for CQLite operations
10#[derive(Error, Debug)]
11pub enum Error {
12    /// I/O related errors
13    #[error("I/O error: {0}")]
14    Io(#[from] std::io::Error),
15
16    /// Serialization/deserialization errors
17    #[error("Serialization error: {message}")]
18    Serialization {
19        message: String,
20        #[source]
21        source: Option<Box<dyn std::error::Error + Send + Sync>>,
22    },
23
24    /// Data corruption errors
25    #[error("Data corruption: {0}")]
26    Corruption(String),
27
28    /// Schema validation errors
29    #[error("Schema error: {0}")]
30    Schema(String),
31
32    /// CQL parsing errors
33    #[error("CQL parse error: {0}")]
34    CqlParse(String),
35
36    /// Invalid format error (for SSTable parsing)
37    #[error("Invalid format: {0}")]
38    InvalidFormat(String),
39
40    /// Unsupported format error
41    #[error("Unsupported format: {0}")]
42    UnsupportedFormat(String),
43
44    /// Timeout error
45    #[error("Operation timeout: {0}")]
46    Timeout(String),
47
48    /// Invalid path error
49    #[error("Invalid path: {0}")]
50    InvalidPath(String),
51
52    /// Invalid state error
53    #[error("Invalid state: {0}")]
54    InvalidState(String),
55
56    /// Query execution errors
57    #[error("Query execution error: {0}")]
58    QueryExecution(String),
59
60    /// Type conversion errors
61    #[error("Type conversion error: {0}")]
62    TypeConversion(String),
63
64    /// Configuration errors
65    #[error("Configuration error: {0}")]
66    Configuration(String),
67
68    /// Storage engine errors
69    #[error("Storage error: {0}")]
70    Storage(String),
71
72    /// Memory management errors
73    #[error("Memory error: {0}")]
74    Memory(String),
75
76    /// Lock/concurrency errors
77    #[error("Concurrency error: {0}")]
78    Concurrency(String),
79
80    /// Resource not found
81    #[error("Not found: {0}")]
82    NotFound(String),
83
84    /// Table errors
85    #[error("Table error: {0}")]
86    Table(String),
87
88    /// Resource already exists
89    #[error("Already exists: {0}")]
90    AlreadyExists(String),
91
92    /// Invalid operation
93    #[error("Invalid operation: {0}")]
94    InvalidOperation(String),
95
96    /// Constraint violation
97    #[error("Constraint violation: {0}")]
98    ConstraintViolation(String),
99
100    /// Transaction errors
101    #[error("Transaction error: {0}")]
102    Transaction(String),
103
104    /// Index errors
105    #[error("Index error: {0}")]
106    Index(String),
107
108    /// Compaction errors
109    #[error("Compaction error: {0}")]
110    Compaction(String),
111
112    /// WASM-specific errors
113    #[cfg(target_arch = "wasm32")]
114    #[error("WASM error: {0}")]
115    Wasm(String),
116
117    /// Generic internal error
118    #[error("Internal error: {0}")]
119    Internal(String),
120
121    /// Parse error
122    #[error("Parse error: {0}")]
123    Parse(String),
124
125    /// Invalid input error
126    #[error("Invalid input: {0}")]
127    InvalidInput(String),
128
129    /// Unsupported query error
130    #[error("Unsupported query: {0}")]
131    UnsupportedQuery(String),
132}
133
134impl Error {
135    /// Create a serialization error
136    pub fn serialization(msg: impl Into<String>) -> Self {
137        Self::Serialization {
138            message: msg.into(),
139            source: None,
140        }
141    }
142
143    /// Create a corruption error
144    pub fn corruption(msg: impl Into<String>) -> Self {
145        Self::Corruption(msg.into())
146    }
147
148    /// Create a schema error
149    pub fn schema(msg: impl Into<String>) -> Self {
150        Self::Schema(msg.into())
151    }
152
153    /// Create a CQL parse error
154    pub fn cql_parse(msg: impl Into<String>) -> Self {
155        Self::CqlParse(msg.into())
156    }
157
158    /// Create an invalid format error
159    pub fn invalid_format(msg: impl Into<String>) -> Self {
160        Self::InvalidFormat(msg.into())
161    }
162
163    /// Create an unsupported format error
164    pub fn unsupported_format(msg: impl Into<String>) -> Self {
165        Self::UnsupportedFormat(msg.into())
166    }
167
168    /// Create an invalid path error
169    pub fn invalid_path(msg: impl Into<String>) -> Self {
170        Self::InvalidPath(msg.into())
171    }
172
173    /// Create an invalid state error
174    pub fn invalid_state(msg: impl Into<String>) -> Self {
175        Self::InvalidState(msg.into())
176    }
177
178    /// Create a query execution error
179    pub fn query_execution(msg: impl Into<String>) -> Self {
180        Self::QueryExecution(msg.into())
181    }
182
183    /// Create a type conversion error
184    pub fn type_conversion(msg: impl Into<String>) -> Self {
185        Self::TypeConversion(msg.into())
186    }
187
188    /// Create a configuration error
189    pub fn configuration(msg: impl Into<String>) -> Self {
190        Self::Configuration(msg.into())
191    }
192
193    /// Create a storage error
194    pub fn storage(msg: impl Into<String>) -> Self {
195        Self::Storage(msg.into())
196    }
197
198    /// Create a memory error
199    pub fn memory(msg: impl Into<String>) -> Self {
200        Self::Memory(msg.into())
201    }
202
203    /// Create a concurrency error
204    pub fn concurrency(msg: impl Into<String>) -> Self {
205        Self::Concurrency(msg.into())
206    }
207
208    /// Create a not found error
209    pub fn not_found(msg: impl Into<String>) -> Self {
210        Self::NotFound(msg.into())
211    }
212
213    /// Create an already exists error
214    pub fn already_exists(msg: impl Into<String>) -> Self {
215        Self::AlreadyExists(msg.into())
216    }
217
218    /// Create an invalid operation error
219    pub fn invalid_operation(msg: impl Into<String>) -> Self {
220        Self::InvalidOperation(msg.into())
221    }
222
223    /// Create a constraint violation error
224    pub fn constraint_violation(msg: impl Into<String>) -> Self {
225        Self::ConstraintViolation(msg.into())
226    }
227
228    /// Create a transaction error
229    pub fn transaction(msg: impl Into<String>) -> Self {
230        Self::Transaction(msg.into())
231    }
232
233    /// Create an index error
234    pub fn index(msg: impl Into<String>) -> Self {
235        Self::Index(msg.into())
236    }
237
238    /// Create a compaction error
239    pub fn compaction(msg: impl Into<String>) -> Self {
240        Self::Compaction(msg.into())
241    }
242
243    /// Create a WASM error
244    #[cfg(target_arch = "wasm32")]
245    pub fn wasm(msg: impl Into<String>) -> Self {
246        Self::Wasm(msg.into())
247    }
248
249    /// Create an internal error
250    pub fn internal(msg: impl Into<String>) -> Self {
251        Self::Internal(msg.into())
252    }
253
254    /// Create an invalid input error
255    pub fn invalid_input(msg: impl Into<String>) -> Self {
256        Self::InvalidInput(msg.into())
257    }
258
259    /// Create a parse error
260    pub fn parse(msg: impl Into<String>) -> Self {
261        Self::Parse(msg.into())
262    }
263
264    /// Create an unsupported query error
265    pub fn unsupported_query(msg: impl Into<String>) -> Self {
266        Self::UnsupportedQuery(msg.into())
267    }
268
269    /// Create a table not found error
270    pub fn table_not_found(msg: impl Into<String>) -> Self {
271        Self::NotFound(format!("Table not found: {}", msg.into()))
272    }
273
274    /// Create an ambiguous table error
275    pub fn ambiguous_table(msg: impl Into<String>) -> Self {
276        Self::Table(format!("Ambiguous table reference: {}", msg.into()))
277    }
278
279    /// Check if this error is recoverable
280    pub fn is_recoverable(&self) -> bool {
281        match self {
282            // These errors are typically recoverable with retry
283            Error::Io(_) => true,
284            Error::Concurrency(_) => true,
285            Error::Memory(_) => true,
286
287            // These errors are typically not recoverable
288            Error::Corruption(_) => false,
289            Error::Schema(_) => false,
290            Error::CqlParse(_) => false,
291            Error::Configuration(_) => false,
292
293            // Context-dependent errors
294            Error::Storage(_) => true,
295            Error::QueryExecution(_) => false,
296            Error::TypeConversion(_) => false,
297            Error::NotFound(_) => false,
298            Error::AlreadyExists(_) => false,
299            Error::InvalidOperation(_) => false,
300            Error::ConstraintViolation(_) => false,
301            Error::Transaction(_) => true,
302            Error::Index(_) => true,
303            Error::Compaction(_) => true,
304
305            // New error types
306            Error::Table(_) => false,
307
308            #[cfg(target_arch = "wasm32")]
309            Error::Wasm(_) => false,
310
311            Error::Serialization { .. } => false,
312            Error::Internal(_) => false,
313            Error::Parse(_) => false,
314            Error::InvalidInput(_) => false,
315            Error::InvalidFormat(_) => false,
316            Error::UnsupportedFormat(_) => false,
317            Error::InvalidPath(_) => false,
318            Error::InvalidState(_) => false,
319            Error::Timeout(_) => false,
320            Error::UnsupportedQuery(_) => false,
321        }
322    }
323
324    /// Get the error category
325    pub fn category(&self) -> ErrorCategory {
326        match self {
327            Error::Io(_) => ErrorCategory::System,
328            Error::Serialization { .. } => ErrorCategory::Data,
329            Error::Corruption(_) => ErrorCategory::Data,
330            Error::Schema(_) => ErrorCategory::Schema,
331            Error::CqlParse(_) => ErrorCategory::Query,
332            Error::QueryExecution(_) => ErrorCategory::Query,
333            Error::TypeConversion(_) => ErrorCategory::Data,
334            Error::Configuration(_) => ErrorCategory::Configuration,
335            Error::Storage(_) => ErrorCategory::Storage,
336            Error::Memory(_) => ErrorCategory::System,
337            Error::Concurrency(_) => ErrorCategory::Concurrency,
338            Error::NotFound(_) => ErrorCategory::NotFound,
339            Error::AlreadyExists(_) => ErrorCategory::Conflict,
340            Error::InvalidOperation(_) => ErrorCategory::Logic,
341            Error::ConstraintViolation(_) => ErrorCategory::Constraint,
342            Error::Transaction(_) => ErrorCategory::Transaction,
343            Error::Index(_) => ErrorCategory::Storage,
344            Error::Compaction(_) => ErrorCategory::Storage,
345
346            // New error types
347            Error::Table(_) => ErrorCategory::Schema,
348
349            #[cfg(target_arch = "wasm32")]
350            Error::Wasm(_) => ErrorCategory::Platform,
351
352            Error::Internal(_) => ErrorCategory::Internal,
353            Error::Parse(_) => ErrorCategory::Data,
354            Error::InvalidInput(_) => ErrorCategory::Data,
355            Error::InvalidFormat(_) => ErrorCategory::Data,
356            Error::UnsupportedFormat(_) => ErrorCategory::Data,
357            Error::InvalidPath(_) => ErrorCategory::System,
358            Error::InvalidState(_) => ErrorCategory::Logic,
359            Error::Timeout(_) => ErrorCategory::System,
360            Error::UnsupportedQuery(_) => ErrorCategory::Query,
361        }
362    }
363}
364
365/// Error categories for grouping related errors
366#[derive(Debug, Clone, Copy, PartialEq, Eq)]
367pub enum ErrorCategory {
368    /// System-level errors (I/O, memory, etc.)
369    System,
370    /// Data-related errors (corruption, serialization)
371    Data,
372    /// Schema-related errors
373    Schema,
374    /// Query-related errors (parsing, execution)
375    Query,
376    /// Configuration errors
377    Configuration,
378    /// Storage engine errors
379    Storage,
380    /// Concurrency-related errors
381    Concurrency,
382    /// Resource not found
383    NotFound,
384    /// Resource conflicts
385    Conflict,
386    /// Logic errors
387    Logic,
388    /// Constraint violations
389    Constraint,
390    /// Transaction errors
391    Transaction,
392    /// Platform-specific errors
393    Platform,
394    /// Internal errors
395    Internal,
396}
397
398impl fmt::Display for ErrorCategory {
399    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
400        let name = match self {
401            ErrorCategory::System => "System",
402            ErrorCategory::Data => "Data",
403            ErrorCategory::Schema => "Schema",
404            ErrorCategory::Query => "Query",
405            ErrorCategory::Configuration => "Configuration",
406            ErrorCategory::Storage => "Storage",
407            ErrorCategory::Concurrency => "Concurrency",
408            ErrorCategory::NotFound => "NotFound",
409            ErrorCategory::Conflict => "Conflict",
410            ErrorCategory::Logic => "Logic",
411            ErrorCategory::Constraint => "Constraint",
412            ErrorCategory::Transaction => "Transaction",
413            ErrorCategory::Platform => "Platform",
414            ErrorCategory::Internal => "Internal",
415        };
416        write!(f, "{}", name)
417    }
418}
419
420/// Convert from bincode errors
421impl From<bincode::Error> for Error {
422    fn from(err: bincode::Error) -> Self {
423        Error::Serialization {
424            message: err.to_string(),
425            source: Some(Box::new(err)),
426        }
427    }
428}
429
430/// Convert from serde_json errors
431impl From<serde_json::Error> for Error {
432    fn from(err: serde_json::Error) -> Self {
433        Error::Serialization {
434            message: err.to_string(),
435            source: Some(Box::new(err)),
436        }
437    }
438}
439
440/// Convert from nom errors
441impl<I> From<nom::Err<nom::error::Error<I>>> for Error
442where
443    I: std::fmt::Debug,
444{
445    fn from(err: nom::Err<nom::error::Error<I>>) -> Self {
446        Error::CqlParse(format!("Parse error: {:?}", err))
447    }
448}
449
450// Helper function to create custom parse error type
451pub type ParseResult<I, O> = nom::IResult<I, O, Error>;
452
453/// Custom error type for parsing operations
454#[derive(Debug, Clone)]
455pub struct ParseError {
456    pub message: String,
457}
458
459impl std::fmt::Display for ParseError {
460    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
461        write!(f, "{}", self.message)
462    }
463}
464
465impl std::error::Error for ParseError {}
466
467#[cfg(test)]
468mod tests {
469    use super::*;
470
471    #[test]
472    fn test_error_from_conversions() {
473        // Test bincode error conversion (covers line 399-400)
474        let io_err = std::io::Error::other("test error");
475        let bincode_err = bincode::Error::new(bincode::ErrorKind::Io(io_err));
476        let error = Error::from(bincode_err);
477        assert!(matches!(error, Error::Serialization { .. }));
478
479        // Test serde_json error conversion (covers line 406-407)
480        let json_err = serde_json::from_str::<serde_json::Value>("invalid json").unwrap_err();
481        let error = Error::from(json_err);
482        assert!(matches!(error, Error::Serialization { .. }));
483
484        // Test nom error conversion (covers line 416-417)
485        let nom_err = nom::Err::Error(nom::error::Error::new(
486            "test input",
487            nom::error::ErrorKind::Tag,
488        ));
489        let error = Error::from(nom_err);
490        assert!(matches!(error, Error::CqlParse(_)));
491    }
492
493    #[test]
494    fn test_parse_error_display() {
495        // Test ParseError Display implementation (covers line 431-432)
496        let parse_error = ParseError {
497            message: "test parse error".to_string(),
498        };
499        let display_str = format!("{}", parse_error);
500        assert_eq!(display_str, "test parse error");
501    }
502
503    #[test]
504    #[cfg(target_arch = "wasm32")]
505    fn test_wasm_error_creation() {
506        // Test WASM error creation (covers line 234-235)
507        let err = Error::wasm("WASM error");
508        assert!(matches!(err, Error::Wasm(_)));
509        assert!(!err.is_recoverable());
510        assert_eq!(err.category(), ErrorCategory::Platform);
511    }
512
513    #[test]
514    fn test_new_error_types_coverage() {
515        // Test Table error
516        let table_err = Error::Table("table error".to_string());
517        assert!(!table_err.is_recoverable());
518        assert_eq!(table_err.category(), ErrorCategory::Schema);
519    }
520
521    #[test]
522    fn test_error_creation() {
523        let err = Error::storage("test error");
524        assert!(matches!(err, Error::Storage(_)));
525        assert_eq!(err.to_string(), "Storage error: test error");
526    }
527
528    #[test]
529    fn test_error_categories() {
530        assert_eq!(Error::storage("test").category(), ErrorCategory::Storage);
531        assert_eq!(Error::schema("test").category(), ErrorCategory::Schema);
532        assert_eq!(Error::cql_parse("test").category(), ErrorCategory::Query);
533    }
534
535    #[test]
536    fn test_error_recoverability() {
537        assert!(Error::concurrency("test").is_recoverable());
538        assert!(!Error::corruption("test").is_recoverable());
539        assert!(!Error::schema("test").is_recoverable());
540    }
541
542    #[test]
543    fn test_all_error_constructors() {
544        // Test all error constructor methods for coverage
545        let _ = Error::serialization("test");
546        let _ = Error::corruption("test");
547        let _ = Error::schema("test");
548        let _ = Error::cql_parse("test");
549        let _ = Error::invalid_format("test");
550        let _ = Error::unsupported_format("test");
551        let _ = Error::invalid_path("test");
552        let _ = Error::invalid_state("test");
553        let _ = Error::query_execution("test");
554        let _ = Error::type_conversion("test");
555        let _ = Error::configuration("test");
556        let _ = Error::storage("test");
557        let _ = Error::memory("test");
558        let _ = Error::concurrency("test");
559        let _ = Error::not_found("test");
560        let _ = Error::already_exists("test");
561        let _ = Error::invalid_operation("test");
562        let _ = Error::constraint_violation("test");
563        let _ = Error::transaction("test");
564        let _ = Error::index("test");
565        let _ = Error::compaction("test");
566        let _ = Error::internal("test");
567        let _ = Error::invalid_input("test");
568        let _ = Error::parse("test");
569    }
570
571    #[test]
572    fn test_all_error_categories() {
573        // Test all error categories for coverage
574        assert_eq!(Error::serialization("test").category(), ErrorCategory::Data);
575        assert_eq!(Error::corruption("test").category(), ErrorCategory::Data);
576        assert_eq!(Error::cql_parse("test").category(), ErrorCategory::Query);
577        assert_eq!(
578            Error::invalid_format("test").category(),
579            ErrorCategory::Data
580        );
581        assert_eq!(
582            Error::unsupported_format("test").category(),
583            ErrorCategory::Data
584        );
585        assert_eq!(
586            Error::invalid_path("test").category(),
587            ErrorCategory::System
588        );
589        assert_eq!(
590            Error::invalid_state("test").category(),
591            ErrorCategory::Logic
592        );
593        assert_eq!(
594            Error::query_execution("test").category(),
595            ErrorCategory::Query
596        );
597        assert_eq!(
598            Error::type_conversion("test").category(),
599            ErrorCategory::Data
600        );
601        assert_eq!(
602            Error::configuration("test").category(),
603            ErrorCategory::Configuration
604        );
605        assert_eq!(Error::memory("test").category(), ErrorCategory::System);
606        assert_eq!(
607            Error::concurrency("test").category(),
608            ErrorCategory::Concurrency
609        );
610        assert_eq!(Error::not_found("test").category(), ErrorCategory::NotFound);
611        assert_eq!(
612            Error::already_exists("test").category(),
613            ErrorCategory::Conflict
614        );
615        assert_eq!(
616            Error::invalid_operation("test").category(),
617            ErrorCategory::Logic
618        );
619        assert_eq!(
620            Error::constraint_violation("test").category(),
621            ErrorCategory::Constraint
622        );
623        assert_eq!(
624            Error::transaction("test").category(),
625            ErrorCategory::Transaction
626        );
627        assert_eq!(Error::index("test").category(), ErrorCategory::Storage);
628        assert_eq!(Error::compaction("test").category(), ErrorCategory::Storage);
629        assert_eq!(Error::internal("test").category(), ErrorCategory::Internal);
630        assert_eq!(Error::invalid_input("test").category(), ErrorCategory::Data);
631        assert_eq!(Error::parse("test").category(), ErrorCategory::Data);
632    }
633
634    #[test]
635    fn test_all_error_recoverability() {
636        // Test recoverability for all error types
637        assert!(Error::memory("test").is_recoverable());
638        assert!(Error::storage("test").is_recoverable());
639        assert!(Error::transaction("test").is_recoverable());
640        assert!(Error::index("test").is_recoverable());
641        assert!(Error::compaction("test").is_recoverable());
642
643        assert!(!Error::serialization("test").is_recoverable());
644        assert!(!Error::cql_parse("test").is_recoverable());
645        assert!(!Error::invalid_format("test").is_recoverable());
646        assert!(!Error::unsupported_format("test").is_recoverable());
647        assert!(!Error::invalid_path("test").is_recoverable());
648        assert!(!Error::invalid_state("test").is_recoverable());
649        assert!(!Error::query_execution("test").is_recoverable());
650        assert!(!Error::type_conversion("test").is_recoverable());
651        assert!(!Error::configuration("test").is_recoverable());
652        assert!(!Error::not_found("test").is_recoverable());
653        assert!(!Error::already_exists("test").is_recoverable());
654        assert!(!Error::invalid_operation("test").is_recoverable());
655        assert!(!Error::constraint_violation("test").is_recoverable());
656        assert!(!Error::internal("test").is_recoverable());
657        assert!(!Error::invalid_input("test").is_recoverable());
658        assert!(!Error::parse("test").is_recoverable());
659    }
660
661    #[test]
662    fn test_error_category_display() {
663        // Test ErrorCategory Display implementation
664        assert_eq!(ErrorCategory::System.to_string(), "System");
665        assert_eq!(ErrorCategory::Data.to_string(), "Data");
666        assert_eq!(ErrorCategory::Schema.to_string(), "Schema");
667        assert_eq!(ErrorCategory::Query.to_string(), "Query");
668        assert_eq!(ErrorCategory::Configuration.to_string(), "Configuration");
669        assert_eq!(ErrorCategory::Storage.to_string(), "Storage");
670        assert_eq!(ErrorCategory::Concurrency.to_string(), "Concurrency");
671        assert_eq!(ErrorCategory::NotFound.to_string(), "NotFound");
672        assert_eq!(ErrorCategory::Conflict.to_string(), "Conflict");
673        assert_eq!(ErrorCategory::Logic.to_string(), "Logic");
674        assert_eq!(ErrorCategory::Constraint.to_string(), "Constraint");
675        assert_eq!(ErrorCategory::Transaction.to_string(), "Transaction");
676        assert_eq!(ErrorCategory::Platform.to_string(), "Platform");
677        assert_eq!(ErrorCategory::Internal.to_string(), "Internal");
678    }
679
680    #[test]
681    fn test_error_from_io_error() {
682        // Test conversion from std::io::Error
683        let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
684        let cqlite_err: Error = io_err.into();
685        assert!(matches!(cqlite_err, Error::Io(_)));
686        assert_eq!(cqlite_err.category(), ErrorCategory::System);
687        assert!(cqlite_err.is_recoverable());
688    }
689
690    #[test]
691    fn test_result_type_alias() {
692        // Test the Result type alias
693        let success: Result<i32> = Ok(42);
694        let failure: Result<i32> = Err(Error::storage("test error"));
695
696        assert!(success.is_ok());
697        if let Ok(value) = success {
698            assert_eq!(value, 42);
699        }
700        assert!(failure.is_err());
701    }
702}