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    /// SSTable version below the supported Cassandra 5.0 floor.
45    ///
46    /// CQLite targets Cassandra 5.0 (`na`+/`nb` BIG, `oa`/`da` BTI). A
47    /// pre-`na` (`ma`–`me`, Cassandra 3.x) BIG version, or a non-`da` BTI
48    /// version, is out of scope and rejected at version-parse time.
49    #[error("Unsupported SSTable version {version:?}: below supported floor {floor:?}")]
50    UnsupportedVersion { version: String, floor: String },
51
52    /// Timeout error
53    #[error("Operation timeout: {0}")]
54    Timeout(String),
55
56    /// Invalid path error
57    #[error("Invalid path: {0}")]
58    InvalidPath(String),
59
60    /// Invalid state error
61    #[error("Invalid state: {0}")]
62    InvalidState(String),
63
64    /// Query execution errors
65    #[error("Query execution error: {0}")]
66    QueryExecution(String),
67
68    /// A materialized result set exceeded the configured byte budget (issue #1582).
69    ///
70    /// Raised by the SELECT executor *while collecting* a materialized result
71    /// set, before it grows large enough to threaten the process's <128MB
72    /// memory target. This is the byte-unit successor to the coarse row-count
73    /// safety valve: a byte ceiling correctly distinguishes 1M harmless skinny
74    /// rows from 100k memory-blowing wide rows. The remedy is in the message —
75    /// bound the result with a `LIMIT` clause, or consume it incrementally via
76    /// the streaming query API instead of materializing the whole set.
77    #[error(
78        "result set exceeded the {budget_bytes}-byte materialization budget \
79         (estimated {estimated_bytes} bytes across {rows} rows so far); \
80         add a LIMIT clause to bound the result, or use the streaming query \
81         API (e.g. execute_streaming) to consume rows incrementally"
82    )]
83    ResultTooLarge {
84        /// Configured materialization byte budget that was exceeded.
85        budget_bytes: usize,
86        /// Estimated logical size (bytes) of the result collected so far.
87        estimated_bytes: usize,
88        /// Number of rows collected when the budget was exceeded.
89        rows: usize,
90    },
91
92    /// Type conversion errors
93    #[error("Type conversion error: {0}")]
94    TypeConversion(String),
95
96    /// Configuration errors
97    #[error("Configuration error: {0}")]
98    Configuration(String),
99
100    /// Storage engine errors
101    #[error("Storage error: {0}")]
102    Storage(String),
103
104    /// Memory management errors
105    #[error("Memory error: {0}")]
106    Memory(String),
107
108    /// Lock/concurrency errors
109    #[error("Concurrency error: {0}")]
110    Concurrency(String),
111
112    /// Write directory already locked by another process or Database instance
113    ///
114    /// Returned by `WriteEngine::new` when the advisory lock on `write_dir`
115    /// cannot be acquired because another `WriteEngine` (in this or another
116    /// process) already holds it.  Only one `Database` instance may hold a
117    /// `write_dir` at a time.
118    #[error(
119        "write_dir '{path}' is already locked by another process. \
120         Only one Database instance may hold a write_dir at a time."
121    )]
122    WriteDirLocked {
123        /// The path that could not be locked
124        path: String,
125    },
126
127    /// Resource not found
128    #[error("Not found: {0}")]
129    NotFound(String),
130
131    /// Table errors
132    #[error("Table error: {0}")]
133    Table(String),
134
135    /// Resource already exists
136    #[error("Already exists: {0}")]
137    AlreadyExists(String),
138
139    /// Invalid operation
140    #[error("Invalid operation: {0}")]
141    InvalidOperation(String),
142
143    /// Constraint violation
144    #[error("Constraint violation: {0}")]
145    ConstraintViolation(String),
146
147    /// Transaction errors
148    #[error("Transaction error: {0}")]
149    Transaction(String),
150
151    /// Index errors
152    #[error("Index error: {0}")]
153    Index(String),
154
155    /// Compaction errors
156    #[error("Compaction error: {0}")]
157    Compaction(String),
158
159    /// WASM-specific errors
160    #[cfg(target_arch = "wasm32")]
161    #[error("WASM error: {0}")]
162    Wasm(String),
163
164    /// Generic internal error
165    #[error("Internal error: {0}")]
166    Internal(String),
167
168    /// Parse error
169    #[error("Parse error: {0}")]
170    Parse(String),
171
172    /// Invalid input error
173    #[error("Invalid input: {0}")]
174    InvalidInput(String),
175
176    /// Unsupported query error
177    #[error("Unsupported query: {0}")]
178    UnsupportedQuery(String),
179
180    /// The operation was cooperatively cancelled (issue #2264).
181    ///
182    /// Raised by a long-running scan (e.g. the compaction streaming read) when
183    /// its cancellation token is tripped — a client disconnect propagated from
184    /// the Flight `do_get` path. Distinct from a genuine failure so callers can
185    /// treat it as a clean, expected abort rather than corruption.
186    #[error("Operation cancelled")]
187    Cancelled,
188}
189
190impl Error {
191    /// Create a serialization error
192    pub fn serialization(msg: impl Into<String>) -> Self {
193        Self::Serialization {
194            message: msg.into(),
195            source: None,
196        }
197    }
198
199    /// Create a corruption error
200    pub fn corruption(msg: impl Into<String>) -> Self {
201        Self::Corruption(msg.into())
202    }
203
204    /// Create a schema error
205    pub fn schema(msg: impl Into<String>) -> Self {
206        Self::Schema(msg.into())
207    }
208
209    /// Create a CQL parse error
210    pub fn cql_parse(msg: impl Into<String>) -> Self {
211        Self::CqlParse(msg.into())
212    }
213
214    /// Create an invalid format error
215    pub fn invalid_format(msg: impl Into<String>) -> Self {
216        Self::InvalidFormat(msg.into())
217    }
218
219    /// Create an unsupported format error
220    pub fn unsupported_format(msg: impl Into<String>) -> Self {
221        Self::UnsupportedFormat(msg.into())
222    }
223
224    /// Create an invalid path error
225    pub fn invalid_path(msg: impl Into<String>) -> Self {
226        Self::InvalidPath(msg.into())
227    }
228
229    /// Create an invalid state error
230    pub fn invalid_state(msg: impl Into<String>) -> Self {
231        Self::InvalidState(msg.into())
232    }
233
234    /// Create a query execution error
235    pub fn query_execution(msg: impl Into<String>) -> Self {
236        Self::QueryExecution(msg.into())
237    }
238
239    /// Create a type conversion error
240    pub fn type_conversion(msg: impl Into<String>) -> Self {
241        Self::TypeConversion(msg.into())
242    }
243
244    /// Create a configuration error
245    pub fn configuration(msg: impl Into<String>) -> Self {
246        Self::Configuration(msg.into())
247    }
248
249    /// Create a storage error
250    pub fn storage(msg: impl Into<String>) -> Self {
251        Self::Storage(msg.into())
252    }
253
254    /// Create a memory error
255    pub fn memory(msg: impl Into<String>) -> Self {
256        Self::Memory(msg.into())
257    }
258
259    /// Create a concurrency error
260    pub fn concurrency(msg: impl Into<String>) -> Self {
261        Self::Concurrency(msg.into())
262    }
263
264    /// Create a not found error
265    pub fn not_found(msg: impl Into<String>) -> Self {
266        Self::NotFound(msg.into())
267    }
268
269    /// Create an already exists error
270    pub fn already_exists(msg: impl Into<String>) -> Self {
271        Self::AlreadyExists(msg.into())
272    }
273
274    /// Create an invalid operation error
275    pub fn invalid_operation(msg: impl Into<String>) -> Self {
276        Self::InvalidOperation(msg.into())
277    }
278
279    /// Create a constraint violation error
280    pub fn constraint_violation(msg: impl Into<String>) -> Self {
281        Self::ConstraintViolation(msg.into())
282    }
283
284    /// Create a transaction error
285    pub fn transaction(msg: impl Into<String>) -> Self {
286        Self::Transaction(msg.into())
287    }
288
289    /// Create an index error
290    pub fn index(msg: impl Into<String>) -> Self {
291        Self::Index(msg.into())
292    }
293
294    /// Create a compaction error
295    pub fn compaction(msg: impl Into<String>) -> Self {
296        Self::Compaction(msg.into())
297    }
298
299    /// Create a WASM error
300    #[cfg(target_arch = "wasm32")]
301    pub fn wasm(msg: impl Into<String>) -> Self {
302        Self::Wasm(msg.into())
303    }
304
305    /// Create an internal error
306    pub fn internal(msg: impl Into<String>) -> Self {
307        Self::Internal(msg.into())
308    }
309
310    /// Create an invalid input error
311    pub fn invalid_input(msg: impl Into<String>) -> Self {
312        Self::InvalidInput(msg.into())
313    }
314
315    /// Create a parse error
316    pub fn parse(msg: impl Into<String>) -> Self {
317        Self::Parse(msg.into())
318    }
319
320    /// Create an unsupported query error
321    pub fn unsupported_query(msg: impl Into<String>) -> Self {
322        Self::UnsupportedQuery(msg.into())
323    }
324
325    /// Create a write-dir locked error
326    pub fn write_dir_locked(path: impl Into<String>) -> Self {
327        Self::WriteDirLocked { path: path.into() }
328    }
329
330    /// Create a table not found error
331    pub fn table_not_found(msg: impl Into<String>) -> Self {
332        Self::NotFound(format!("Table not found: {}", msg.into()))
333    }
334
335    /// Create an ambiguous table error
336    pub fn ambiguous_table(msg: impl Into<String>) -> Self {
337        Self::Table(format!("Ambiguous table reference: {}", msg.into()))
338    }
339
340    /// Check if this error is recoverable
341    pub fn is_recoverable(&self) -> bool {
342        match self {
343            // These errors are typically recoverable with retry
344            Error::Io(_) => true,
345            Error::Concurrency(_) => true,
346            Error::Memory(_) => true,
347
348            // These errors are typically not recoverable
349            Error::Corruption(_) => false,
350            Error::Schema(_) => false,
351            Error::CqlParse(_) => false,
352            Error::Configuration(_) => false,
353
354            // Context-dependent errors
355            Error::Storage(_) => true,
356            Error::QueryExecution(_) => false,
357            // Not recoverable by retry: the same query would re-materialize the
358            // same oversized result. The user must add LIMIT or stream.
359            Error::ResultTooLarge { .. } => false,
360            Error::TypeConversion(_) => false,
361            Error::NotFound(_) => false,
362            Error::AlreadyExists(_) => false,
363            Error::InvalidOperation(_) => false,
364            Error::ConstraintViolation(_) => false,
365            Error::Transaction(_) => true,
366            Error::Index(_) => true,
367            Error::Compaction(_) => true,
368
369            // New error types
370            Error::Table(_) => false,
371
372            // Write-dir lock conflict — not recoverable without releasing the lock
373            Error::WriteDirLocked { .. } => false,
374
375            #[cfg(target_arch = "wasm32")]
376            Error::Wasm(_) => false,
377
378            Error::Serialization { .. } => false,
379            Error::Internal(_) => false,
380            Error::Parse(_) => false,
381            Error::InvalidInput(_) => false,
382            Error::InvalidFormat(_) => false,
383            Error::UnsupportedFormat(_) => false,
384            Error::UnsupportedVersion { .. } => false,
385            Error::InvalidPath(_) => false,
386            Error::InvalidState(_) => false,
387            Error::Timeout(_) => false,
388            Error::UnsupportedQuery(_) => false,
389            // A cancelled operation is deliberate, not a transient fault: re-running
390            // it would just be cancelled again. The caller decides whether to retry.
391            Error::Cancelled => false,
392        }
393    }
394
395    /// Get the error category
396    pub fn category(&self) -> ErrorCategory {
397        match self {
398            Error::Io(_) => ErrorCategory::System,
399            Error::Serialization { .. } => ErrorCategory::Data,
400            Error::Corruption(_) => ErrorCategory::Data,
401            Error::Schema(_) => ErrorCategory::Schema,
402            Error::CqlParse(_) => ErrorCategory::Query,
403            Error::QueryExecution(_) => ErrorCategory::Query,
404            Error::ResultTooLarge { .. } => ErrorCategory::Query,
405            Error::TypeConversion(_) => ErrorCategory::Data,
406            Error::Configuration(_) => ErrorCategory::Configuration,
407            Error::Storage(_) => ErrorCategory::Storage,
408            Error::Memory(_) => ErrorCategory::System,
409            Error::Concurrency(_) => ErrorCategory::Concurrency,
410            Error::NotFound(_) => ErrorCategory::NotFound,
411            Error::AlreadyExists(_) => ErrorCategory::Conflict,
412            Error::InvalidOperation(_) => ErrorCategory::Logic,
413            Error::ConstraintViolation(_) => ErrorCategory::Constraint,
414            Error::Transaction(_) => ErrorCategory::Transaction,
415            Error::Index(_) => ErrorCategory::Storage,
416            Error::Compaction(_) => ErrorCategory::Storage,
417
418            // New error types
419            Error::Table(_) => ErrorCategory::Schema,
420
421            // Write-dir lock conflict
422            Error::WriteDirLocked { .. } => ErrorCategory::Concurrency,
423
424            #[cfg(target_arch = "wasm32")]
425            Error::Wasm(_) => ErrorCategory::Platform,
426
427            Error::Internal(_) => ErrorCategory::Internal,
428            Error::Parse(_) => ErrorCategory::Data,
429            Error::InvalidInput(_) => ErrorCategory::Data,
430            Error::InvalidFormat(_) => ErrorCategory::Data,
431            Error::UnsupportedFormat(_) => ErrorCategory::Data,
432            Error::UnsupportedVersion { .. } => ErrorCategory::Data,
433            Error::InvalidPath(_) => ErrorCategory::System,
434            Error::InvalidState(_) => ErrorCategory::Logic,
435            Error::Timeout(_) => ErrorCategory::System,
436            Error::UnsupportedQuery(_) => ErrorCategory::Query,
437            Error::Cancelled => ErrorCategory::Cancelled,
438        }
439    }
440}
441
442/// Error categories for grouping related errors
443#[derive(Debug, Clone, Copy, PartialEq, Eq)]
444pub enum ErrorCategory {
445    /// System-level errors (I/O, memory, etc.)
446    System,
447    /// Data-related errors (corruption, serialization)
448    Data,
449    /// Schema-related errors
450    Schema,
451    /// Query-related errors (parsing, execution)
452    Query,
453    /// Configuration errors
454    Configuration,
455    /// Storage engine errors
456    Storage,
457    /// Concurrency-related errors
458    Concurrency,
459    /// Resource not found
460    NotFound,
461    /// Resource conflicts
462    Conflict,
463    /// Logic errors
464    Logic,
465    /// Constraint violations
466    Constraint,
467    /// Transaction errors
468    Transaction,
469    /// Platform-specific errors
470    Platform,
471    /// Internal errors
472    Internal,
473    /// A cooperative cancellation / abort (issue #2264). Distinct from
474    /// `System` (I/O, memory) so a cancelled operation is never mislabeled as
475    /// a transport/IO failure by a consumer that switches on category.
476    Cancelled,
477}
478
479impl fmt::Display for ErrorCategory {
480    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
481        let name = match self {
482            ErrorCategory::System => "System",
483            ErrorCategory::Data => "Data",
484            ErrorCategory::Schema => "Schema",
485            ErrorCategory::Query => "Query",
486            ErrorCategory::Configuration => "Configuration",
487            ErrorCategory::Storage => "Storage",
488            ErrorCategory::Concurrency => "Concurrency",
489            ErrorCategory::NotFound => "NotFound",
490            ErrorCategory::Conflict => "Conflict",
491            ErrorCategory::Logic => "Logic",
492            ErrorCategory::Constraint => "Constraint",
493            ErrorCategory::Transaction => "Transaction",
494            ErrorCategory::Platform => "Platform",
495            ErrorCategory::Internal => "Internal",
496            ErrorCategory::Cancelled => "Cancelled",
497        };
498        write!(f, "{}", name)
499    }
500}
501
502/// Convert from bincode errors
503impl From<bincode::Error> for Error {
504    fn from(err: bincode::Error) -> Self {
505        Error::Serialization {
506            message: err.to_string(),
507            source: Some(Box::new(err)),
508        }
509    }
510}
511
512/// Convert from serde_json errors
513impl From<serde_json::Error> for Error {
514    fn from(err: serde_json::Error) -> Self {
515        Error::Serialization {
516            message: err.to_string(),
517            source: Some(Box::new(err)),
518        }
519    }
520}
521
522/// Convert from nom errors
523impl<I> From<nom::Err<nom::error::Error<I>>> for Error
524where
525    I: std::fmt::Debug,
526{
527    fn from(err: nom::Err<nom::error::Error<I>>) -> Self {
528        Error::CqlParse(format!("Parse error: {:?}", err))
529    }
530}
531
532// Helper function to create custom parse error type
533pub type ParseResult<I, O> = nom::IResult<I, O, Error>;
534
535/// Custom error type for parsing operations
536#[derive(Debug, Clone)]
537pub struct ParseError {
538    pub message: String,
539}
540
541impl std::fmt::Display for ParseError {
542    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
543        write!(f, "{}", self.message)
544    }
545}
546
547impl std::error::Error for ParseError {}
548
549#[cfg(test)]
550mod tests {
551    use super::*;
552
553    #[test]
554    fn test_error_from_conversions() {
555        // Test bincode error conversion (covers line 399-400)
556        let io_err = std::io::Error::other("test error");
557        let bincode_err = bincode::Error::new(bincode::ErrorKind::Io(io_err));
558        let error = Error::from(bincode_err);
559        assert!(matches!(error, Error::Serialization { .. }));
560
561        // Test serde_json error conversion (covers line 406-407)
562        let json_err = serde_json::from_str::<serde_json::Value>("invalid json").unwrap_err();
563        let error = Error::from(json_err);
564        assert!(matches!(error, Error::Serialization { .. }));
565
566        // Test nom error conversion (covers line 416-417)
567        let nom_err = nom::Err::Error(nom::error::Error::new(
568            "test input",
569            nom::error::ErrorKind::Tag,
570        ));
571        let error = Error::from(nom_err);
572        assert!(matches!(error, Error::CqlParse(_)));
573    }
574
575    #[test]
576    fn test_parse_error_display() {
577        // Test ParseError Display implementation (covers line 431-432)
578        let parse_error = ParseError {
579            message: "test parse error".to_string(),
580        };
581        let display_str = format!("{}", parse_error);
582        assert_eq!(display_str, "test parse error");
583    }
584
585    #[test]
586    #[cfg(target_arch = "wasm32")]
587    fn test_wasm_error_creation() {
588        // Test WASM error creation (covers line 234-235)
589        let err = Error::wasm("WASM error");
590        assert!(matches!(err, Error::Wasm(_)));
591        assert!(!err.is_recoverable());
592        assert_eq!(err.category(), ErrorCategory::Platform);
593    }
594
595    #[test]
596    fn test_new_error_types_coverage() {
597        // Test Table error
598        let table_err = Error::Table("table error".to_string());
599        assert!(!table_err.is_recoverable());
600        assert_eq!(table_err.category(), ErrorCategory::Schema);
601    }
602
603    #[test]
604    fn test_error_creation() {
605        let err = Error::storage("test error");
606        assert!(matches!(err, Error::Storage(_)));
607        assert_eq!(err.to_string(), "Storage error: test error");
608    }
609
610    #[test]
611    fn test_error_categories() {
612        assert_eq!(Error::storage("test").category(), ErrorCategory::Storage);
613        assert_eq!(Error::schema("test").category(), ErrorCategory::Schema);
614        assert_eq!(Error::cql_parse("test").category(), ErrorCategory::Query);
615    }
616
617    #[test]
618    fn test_error_recoverability() {
619        assert!(Error::concurrency("test").is_recoverable());
620        assert!(!Error::corruption("test").is_recoverable());
621        assert!(!Error::schema("test").is_recoverable());
622    }
623
624    #[test]
625    fn test_all_error_constructors() {
626        // Test all error constructor methods for coverage
627        let _ = Error::serialization("test");
628        let _ = Error::corruption("test");
629        let _ = Error::schema("test");
630        let _ = Error::cql_parse("test");
631        let _ = Error::invalid_format("test");
632        let _ = Error::unsupported_format("test");
633        let _ = Error::invalid_path("test");
634        let _ = Error::invalid_state("test");
635        let _ = Error::query_execution("test");
636        let _ = Error::type_conversion("test");
637        let _ = Error::configuration("test");
638        let _ = Error::storage("test");
639        let _ = Error::memory("test");
640        let _ = Error::concurrency("test");
641        let _ = Error::not_found("test");
642        let _ = Error::already_exists("test");
643        let _ = Error::invalid_operation("test");
644        let _ = Error::constraint_violation("test");
645        let _ = Error::transaction("test");
646        let _ = Error::index("test");
647        let _ = Error::compaction("test");
648        let _ = Error::internal("test");
649        let _ = Error::invalid_input("test");
650        let _ = Error::parse("test");
651        let _ = Error::write_dir_locked("/tmp/test-dir");
652    }
653
654    #[test]
655    fn test_all_error_categories() {
656        // Test all error categories for coverage
657        assert_eq!(Error::serialization("test").category(), ErrorCategory::Data);
658        assert_eq!(Error::corruption("test").category(), ErrorCategory::Data);
659        assert_eq!(Error::cql_parse("test").category(), ErrorCategory::Query);
660        assert_eq!(
661            Error::invalid_format("test").category(),
662            ErrorCategory::Data
663        );
664        assert_eq!(
665            Error::unsupported_format("test").category(),
666            ErrorCategory::Data
667        );
668        assert_eq!(
669            Error::invalid_path("test").category(),
670            ErrorCategory::System
671        );
672        assert_eq!(
673            Error::invalid_state("test").category(),
674            ErrorCategory::Logic
675        );
676        assert_eq!(
677            Error::query_execution("test").category(),
678            ErrorCategory::Query
679        );
680        assert_eq!(
681            Error::type_conversion("test").category(),
682            ErrorCategory::Data
683        );
684        assert_eq!(
685            Error::configuration("test").category(),
686            ErrorCategory::Configuration
687        );
688        assert_eq!(Error::memory("test").category(), ErrorCategory::System);
689        assert_eq!(
690            Error::concurrency("test").category(),
691            ErrorCategory::Concurrency
692        );
693        assert_eq!(Error::not_found("test").category(), ErrorCategory::NotFound);
694        assert_eq!(
695            Error::already_exists("test").category(),
696            ErrorCategory::Conflict
697        );
698        assert_eq!(
699            Error::invalid_operation("test").category(),
700            ErrorCategory::Logic
701        );
702        assert_eq!(
703            Error::constraint_violation("test").category(),
704            ErrorCategory::Constraint
705        );
706        assert_eq!(
707            Error::transaction("test").category(),
708            ErrorCategory::Transaction
709        );
710        assert_eq!(Error::index("test").category(), ErrorCategory::Storage);
711        assert_eq!(Error::compaction("test").category(), ErrorCategory::Storage);
712        assert_eq!(Error::internal("test").category(), ErrorCategory::Internal);
713        assert_eq!(Error::invalid_input("test").category(), ErrorCategory::Data);
714        assert_eq!(Error::parse("test").category(), ErrorCategory::Data);
715        assert_eq!(
716            Error::write_dir_locked("/tmp/test").category(),
717            ErrorCategory::Concurrency
718        );
719        // Issue #2264: a cooperative cancellation must NEVER classify as
720        // `System` (which downstream bindings map to an I/O error code).
721        assert_eq!(Error::Cancelled.category(), ErrorCategory::Cancelled);
722    }
723
724    #[test]
725    fn test_all_error_recoverability() {
726        // Test recoverability for all error types
727        assert!(Error::memory("test").is_recoverable());
728        assert!(Error::storage("test").is_recoverable());
729        assert!(Error::transaction("test").is_recoverable());
730        assert!(Error::index("test").is_recoverable());
731        assert!(Error::compaction("test").is_recoverable());
732
733        assert!(!Error::serialization("test").is_recoverable());
734        assert!(!Error::cql_parse("test").is_recoverable());
735        assert!(!Error::invalid_format("test").is_recoverable());
736        assert!(!Error::unsupported_format("test").is_recoverable());
737        assert!(!Error::invalid_path("test").is_recoverable());
738        assert!(!Error::invalid_state("test").is_recoverable());
739        assert!(!Error::query_execution("test").is_recoverable());
740        assert!(!Error::type_conversion("test").is_recoverable());
741        assert!(!Error::configuration("test").is_recoverable());
742        assert!(!Error::not_found("test").is_recoverable());
743        assert!(!Error::already_exists("test").is_recoverable());
744        assert!(!Error::invalid_operation("test").is_recoverable());
745        assert!(!Error::constraint_violation("test").is_recoverable());
746        assert!(!Error::internal("test").is_recoverable());
747        assert!(!Error::invalid_input("test").is_recoverable());
748        assert!(!Error::parse("test").is_recoverable());
749        assert!(!Error::write_dir_locked("/tmp/test").is_recoverable());
750    }
751
752    #[test]
753    fn test_error_category_display() {
754        // Test ErrorCategory Display implementation
755        assert_eq!(ErrorCategory::System.to_string(), "System");
756        assert_eq!(ErrorCategory::Data.to_string(), "Data");
757        assert_eq!(ErrorCategory::Schema.to_string(), "Schema");
758        assert_eq!(ErrorCategory::Query.to_string(), "Query");
759        assert_eq!(ErrorCategory::Configuration.to_string(), "Configuration");
760        assert_eq!(ErrorCategory::Storage.to_string(), "Storage");
761        assert_eq!(ErrorCategory::Concurrency.to_string(), "Concurrency");
762        assert_eq!(ErrorCategory::NotFound.to_string(), "NotFound");
763        assert_eq!(ErrorCategory::Conflict.to_string(), "Conflict");
764        assert_eq!(ErrorCategory::Logic.to_string(), "Logic");
765        assert_eq!(ErrorCategory::Constraint.to_string(), "Constraint");
766        assert_eq!(ErrorCategory::Transaction.to_string(), "Transaction");
767        assert_eq!(ErrorCategory::Platform.to_string(), "Platform");
768        assert_eq!(ErrorCategory::Internal.to_string(), "Internal");
769        assert_eq!(ErrorCategory::Cancelled.to_string(), "Cancelled");
770    }
771
772    #[test]
773    fn test_error_from_io_error() {
774        // Test conversion from std::io::Error
775        let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
776        let cqlite_err: Error = io_err.into();
777        assert!(matches!(cqlite_err, Error::Io(_)));
778        assert_eq!(cqlite_err.category(), ErrorCategory::System);
779        assert!(cqlite_err.is_recoverable());
780    }
781
782    #[test]
783    fn test_result_type_alias() {
784        // Test the Result type alias
785        let success: Result<i32> = Ok(42);
786        let failure: Result<i32> = Err(Error::storage("test error"));
787
788        assert!(success.is_ok());
789        if let Ok(value) = success {
790            assert_eq!(value, 42);
791        }
792        assert!(failure.is_err());
793    }
794}