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
181impl Error {
182    /// Create a serialization error
183    pub fn serialization(msg: impl Into<String>) -> Self {
184        Self::Serialization {
185            message: msg.into(),
186            source: None,
187        }
188    }
189
190    /// Create a corruption error
191    pub fn corruption(msg: impl Into<String>) -> Self {
192        Self::Corruption(msg.into())
193    }
194
195    /// Create a schema error
196    pub fn schema(msg: impl Into<String>) -> Self {
197        Self::Schema(msg.into())
198    }
199
200    /// Create a CQL parse error
201    pub fn cql_parse(msg: impl Into<String>) -> Self {
202        Self::CqlParse(msg.into())
203    }
204
205    /// Create an invalid format error
206    pub fn invalid_format(msg: impl Into<String>) -> Self {
207        Self::InvalidFormat(msg.into())
208    }
209
210    /// Create an unsupported format error
211    pub fn unsupported_format(msg: impl Into<String>) -> Self {
212        Self::UnsupportedFormat(msg.into())
213    }
214
215    /// Create an invalid path error
216    pub fn invalid_path(msg: impl Into<String>) -> Self {
217        Self::InvalidPath(msg.into())
218    }
219
220    /// Create an invalid state error
221    pub fn invalid_state(msg: impl Into<String>) -> Self {
222        Self::InvalidState(msg.into())
223    }
224
225    /// Create a query execution error
226    pub fn query_execution(msg: impl Into<String>) -> Self {
227        Self::QueryExecution(msg.into())
228    }
229
230    /// Create a type conversion error
231    pub fn type_conversion(msg: impl Into<String>) -> Self {
232        Self::TypeConversion(msg.into())
233    }
234
235    /// Create a configuration error
236    pub fn configuration(msg: impl Into<String>) -> Self {
237        Self::Configuration(msg.into())
238    }
239
240    /// Create a storage error
241    pub fn storage(msg: impl Into<String>) -> Self {
242        Self::Storage(msg.into())
243    }
244
245    /// Create a memory error
246    pub fn memory(msg: impl Into<String>) -> Self {
247        Self::Memory(msg.into())
248    }
249
250    /// Create a concurrency error
251    pub fn concurrency(msg: impl Into<String>) -> Self {
252        Self::Concurrency(msg.into())
253    }
254
255    /// Create a not found error
256    pub fn not_found(msg: impl Into<String>) -> Self {
257        Self::NotFound(msg.into())
258    }
259
260    /// Create an already exists error
261    pub fn already_exists(msg: impl Into<String>) -> Self {
262        Self::AlreadyExists(msg.into())
263    }
264
265    /// Create an invalid operation error
266    pub fn invalid_operation(msg: impl Into<String>) -> Self {
267        Self::InvalidOperation(msg.into())
268    }
269
270    /// Create a constraint violation error
271    pub fn constraint_violation(msg: impl Into<String>) -> Self {
272        Self::ConstraintViolation(msg.into())
273    }
274
275    /// Create a transaction error
276    pub fn transaction(msg: impl Into<String>) -> Self {
277        Self::Transaction(msg.into())
278    }
279
280    /// Create an index error
281    pub fn index(msg: impl Into<String>) -> Self {
282        Self::Index(msg.into())
283    }
284
285    /// Create a compaction error
286    pub fn compaction(msg: impl Into<String>) -> Self {
287        Self::Compaction(msg.into())
288    }
289
290    /// Create a WASM error
291    #[cfg(target_arch = "wasm32")]
292    pub fn wasm(msg: impl Into<String>) -> Self {
293        Self::Wasm(msg.into())
294    }
295
296    /// Create an internal error
297    pub fn internal(msg: impl Into<String>) -> Self {
298        Self::Internal(msg.into())
299    }
300
301    /// Create an invalid input error
302    pub fn invalid_input(msg: impl Into<String>) -> Self {
303        Self::InvalidInput(msg.into())
304    }
305
306    /// Create a parse error
307    pub fn parse(msg: impl Into<String>) -> Self {
308        Self::Parse(msg.into())
309    }
310
311    /// Create an unsupported query error
312    pub fn unsupported_query(msg: impl Into<String>) -> Self {
313        Self::UnsupportedQuery(msg.into())
314    }
315
316    /// Create a write-dir locked error
317    pub fn write_dir_locked(path: impl Into<String>) -> Self {
318        Self::WriteDirLocked { path: path.into() }
319    }
320
321    /// Create a table not found error
322    pub fn table_not_found(msg: impl Into<String>) -> Self {
323        Self::NotFound(format!("Table not found: {}", msg.into()))
324    }
325
326    /// Create an ambiguous table error
327    pub fn ambiguous_table(msg: impl Into<String>) -> Self {
328        Self::Table(format!("Ambiguous table reference: {}", msg.into()))
329    }
330
331    /// Check if this error is recoverable
332    pub fn is_recoverable(&self) -> bool {
333        match self {
334            // These errors are typically recoverable with retry
335            Error::Io(_) => true,
336            Error::Concurrency(_) => true,
337            Error::Memory(_) => true,
338
339            // These errors are typically not recoverable
340            Error::Corruption(_) => false,
341            Error::Schema(_) => false,
342            Error::CqlParse(_) => false,
343            Error::Configuration(_) => false,
344
345            // Context-dependent errors
346            Error::Storage(_) => true,
347            Error::QueryExecution(_) => false,
348            // Not recoverable by retry: the same query would re-materialize the
349            // same oversized result. The user must add LIMIT or stream.
350            Error::ResultTooLarge { .. } => false,
351            Error::TypeConversion(_) => false,
352            Error::NotFound(_) => false,
353            Error::AlreadyExists(_) => false,
354            Error::InvalidOperation(_) => false,
355            Error::ConstraintViolation(_) => false,
356            Error::Transaction(_) => true,
357            Error::Index(_) => true,
358            Error::Compaction(_) => true,
359
360            // New error types
361            Error::Table(_) => false,
362
363            // Write-dir lock conflict — not recoverable without releasing the lock
364            Error::WriteDirLocked { .. } => false,
365
366            #[cfg(target_arch = "wasm32")]
367            Error::Wasm(_) => false,
368
369            Error::Serialization { .. } => false,
370            Error::Internal(_) => false,
371            Error::Parse(_) => false,
372            Error::InvalidInput(_) => false,
373            Error::InvalidFormat(_) => false,
374            Error::UnsupportedFormat(_) => false,
375            Error::UnsupportedVersion { .. } => false,
376            Error::InvalidPath(_) => false,
377            Error::InvalidState(_) => false,
378            Error::Timeout(_) => false,
379            Error::UnsupportedQuery(_) => false,
380        }
381    }
382
383    /// Get the error category
384    pub fn category(&self) -> ErrorCategory {
385        match self {
386            Error::Io(_) => ErrorCategory::System,
387            Error::Serialization { .. } => ErrorCategory::Data,
388            Error::Corruption(_) => ErrorCategory::Data,
389            Error::Schema(_) => ErrorCategory::Schema,
390            Error::CqlParse(_) => ErrorCategory::Query,
391            Error::QueryExecution(_) => ErrorCategory::Query,
392            Error::ResultTooLarge { .. } => ErrorCategory::Query,
393            Error::TypeConversion(_) => ErrorCategory::Data,
394            Error::Configuration(_) => ErrorCategory::Configuration,
395            Error::Storage(_) => ErrorCategory::Storage,
396            Error::Memory(_) => ErrorCategory::System,
397            Error::Concurrency(_) => ErrorCategory::Concurrency,
398            Error::NotFound(_) => ErrorCategory::NotFound,
399            Error::AlreadyExists(_) => ErrorCategory::Conflict,
400            Error::InvalidOperation(_) => ErrorCategory::Logic,
401            Error::ConstraintViolation(_) => ErrorCategory::Constraint,
402            Error::Transaction(_) => ErrorCategory::Transaction,
403            Error::Index(_) => ErrorCategory::Storage,
404            Error::Compaction(_) => ErrorCategory::Storage,
405
406            // New error types
407            Error::Table(_) => ErrorCategory::Schema,
408
409            // Write-dir lock conflict
410            Error::WriteDirLocked { .. } => ErrorCategory::Concurrency,
411
412            #[cfg(target_arch = "wasm32")]
413            Error::Wasm(_) => ErrorCategory::Platform,
414
415            Error::Internal(_) => ErrorCategory::Internal,
416            Error::Parse(_) => ErrorCategory::Data,
417            Error::InvalidInput(_) => ErrorCategory::Data,
418            Error::InvalidFormat(_) => ErrorCategory::Data,
419            Error::UnsupportedFormat(_) => ErrorCategory::Data,
420            Error::UnsupportedVersion { .. } => ErrorCategory::Data,
421            Error::InvalidPath(_) => ErrorCategory::System,
422            Error::InvalidState(_) => ErrorCategory::Logic,
423            Error::Timeout(_) => ErrorCategory::System,
424            Error::UnsupportedQuery(_) => ErrorCategory::Query,
425        }
426    }
427}
428
429/// Error categories for grouping related errors
430#[derive(Debug, Clone, Copy, PartialEq, Eq)]
431pub enum ErrorCategory {
432    /// System-level errors (I/O, memory, etc.)
433    System,
434    /// Data-related errors (corruption, serialization)
435    Data,
436    /// Schema-related errors
437    Schema,
438    /// Query-related errors (parsing, execution)
439    Query,
440    /// Configuration errors
441    Configuration,
442    /// Storage engine errors
443    Storage,
444    /// Concurrency-related errors
445    Concurrency,
446    /// Resource not found
447    NotFound,
448    /// Resource conflicts
449    Conflict,
450    /// Logic errors
451    Logic,
452    /// Constraint violations
453    Constraint,
454    /// Transaction errors
455    Transaction,
456    /// Platform-specific errors
457    Platform,
458    /// Internal errors
459    Internal,
460}
461
462impl fmt::Display for ErrorCategory {
463    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
464        let name = match self {
465            ErrorCategory::System => "System",
466            ErrorCategory::Data => "Data",
467            ErrorCategory::Schema => "Schema",
468            ErrorCategory::Query => "Query",
469            ErrorCategory::Configuration => "Configuration",
470            ErrorCategory::Storage => "Storage",
471            ErrorCategory::Concurrency => "Concurrency",
472            ErrorCategory::NotFound => "NotFound",
473            ErrorCategory::Conflict => "Conflict",
474            ErrorCategory::Logic => "Logic",
475            ErrorCategory::Constraint => "Constraint",
476            ErrorCategory::Transaction => "Transaction",
477            ErrorCategory::Platform => "Platform",
478            ErrorCategory::Internal => "Internal",
479        };
480        write!(f, "{}", name)
481    }
482}
483
484/// Convert from bincode errors
485impl From<bincode::Error> for Error {
486    fn from(err: bincode::Error) -> Self {
487        Error::Serialization {
488            message: err.to_string(),
489            source: Some(Box::new(err)),
490        }
491    }
492}
493
494/// Convert from serde_json errors
495impl From<serde_json::Error> for Error {
496    fn from(err: serde_json::Error) -> Self {
497        Error::Serialization {
498            message: err.to_string(),
499            source: Some(Box::new(err)),
500        }
501    }
502}
503
504/// Convert from nom errors
505impl<I> From<nom::Err<nom::error::Error<I>>> for Error
506where
507    I: std::fmt::Debug,
508{
509    fn from(err: nom::Err<nom::error::Error<I>>) -> Self {
510        Error::CqlParse(format!("Parse error: {:?}", err))
511    }
512}
513
514// Helper function to create custom parse error type
515pub type ParseResult<I, O> = nom::IResult<I, O, Error>;
516
517/// Custom error type for parsing operations
518#[derive(Debug, Clone)]
519pub struct ParseError {
520    pub message: String,
521}
522
523impl std::fmt::Display for ParseError {
524    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
525        write!(f, "{}", self.message)
526    }
527}
528
529impl std::error::Error for ParseError {}
530
531#[cfg(test)]
532mod tests {
533    use super::*;
534
535    #[test]
536    fn test_error_from_conversions() {
537        // Test bincode error conversion (covers line 399-400)
538        let io_err = std::io::Error::other("test error");
539        let bincode_err = bincode::Error::new(bincode::ErrorKind::Io(io_err));
540        let error = Error::from(bincode_err);
541        assert!(matches!(error, Error::Serialization { .. }));
542
543        // Test serde_json error conversion (covers line 406-407)
544        let json_err = serde_json::from_str::<serde_json::Value>("invalid json").unwrap_err();
545        let error = Error::from(json_err);
546        assert!(matches!(error, Error::Serialization { .. }));
547
548        // Test nom error conversion (covers line 416-417)
549        let nom_err = nom::Err::Error(nom::error::Error::new(
550            "test input",
551            nom::error::ErrorKind::Tag,
552        ));
553        let error = Error::from(nom_err);
554        assert!(matches!(error, Error::CqlParse(_)));
555    }
556
557    #[test]
558    fn test_parse_error_display() {
559        // Test ParseError Display implementation (covers line 431-432)
560        let parse_error = ParseError {
561            message: "test parse error".to_string(),
562        };
563        let display_str = format!("{}", parse_error);
564        assert_eq!(display_str, "test parse error");
565    }
566
567    #[test]
568    #[cfg(target_arch = "wasm32")]
569    fn test_wasm_error_creation() {
570        // Test WASM error creation (covers line 234-235)
571        let err = Error::wasm("WASM error");
572        assert!(matches!(err, Error::Wasm(_)));
573        assert!(!err.is_recoverable());
574        assert_eq!(err.category(), ErrorCategory::Platform);
575    }
576
577    #[test]
578    fn test_new_error_types_coverage() {
579        // Test Table error
580        let table_err = Error::Table("table error".to_string());
581        assert!(!table_err.is_recoverable());
582        assert_eq!(table_err.category(), ErrorCategory::Schema);
583    }
584
585    #[test]
586    fn test_error_creation() {
587        let err = Error::storage("test error");
588        assert!(matches!(err, Error::Storage(_)));
589        assert_eq!(err.to_string(), "Storage error: test error");
590    }
591
592    #[test]
593    fn test_error_categories() {
594        assert_eq!(Error::storage("test").category(), ErrorCategory::Storage);
595        assert_eq!(Error::schema("test").category(), ErrorCategory::Schema);
596        assert_eq!(Error::cql_parse("test").category(), ErrorCategory::Query);
597    }
598
599    #[test]
600    fn test_error_recoverability() {
601        assert!(Error::concurrency("test").is_recoverable());
602        assert!(!Error::corruption("test").is_recoverable());
603        assert!(!Error::schema("test").is_recoverable());
604    }
605
606    #[test]
607    fn test_all_error_constructors() {
608        // Test all error constructor methods for coverage
609        let _ = Error::serialization("test");
610        let _ = Error::corruption("test");
611        let _ = Error::schema("test");
612        let _ = Error::cql_parse("test");
613        let _ = Error::invalid_format("test");
614        let _ = Error::unsupported_format("test");
615        let _ = Error::invalid_path("test");
616        let _ = Error::invalid_state("test");
617        let _ = Error::query_execution("test");
618        let _ = Error::type_conversion("test");
619        let _ = Error::configuration("test");
620        let _ = Error::storage("test");
621        let _ = Error::memory("test");
622        let _ = Error::concurrency("test");
623        let _ = Error::not_found("test");
624        let _ = Error::already_exists("test");
625        let _ = Error::invalid_operation("test");
626        let _ = Error::constraint_violation("test");
627        let _ = Error::transaction("test");
628        let _ = Error::index("test");
629        let _ = Error::compaction("test");
630        let _ = Error::internal("test");
631        let _ = Error::invalid_input("test");
632        let _ = Error::parse("test");
633        let _ = Error::write_dir_locked("/tmp/test-dir");
634    }
635
636    #[test]
637    fn test_all_error_categories() {
638        // Test all error categories for coverage
639        assert_eq!(Error::serialization("test").category(), ErrorCategory::Data);
640        assert_eq!(Error::corruption("test").category(), ErrorCategory::Data);
641        assert_eq!(Error::cql_parse("test").category(), ErrorCategory::Query);
642        assert_eq!(
643            Error::invalid_format("test").category(),
644            ErrorCategory::Data
645        );
646        assert_eq!(
647            Error::unsupported_format("test").category(),
648            ErrorCategory::Data
649        );
650        assert_eq!(
651            Error::invalid_path("test").category(),
652            ErrorCategory::System
653        );
654        assert_eq!(
655            Error::invalid_state("test").category(),
656            ErrorCategory::Logic
657        );
658        assert_eq!(
659            Error::query_execution("test").category(),
660            ErrorCategory::Query
661        );
662        assert_eq!(
663            Error::type_conversion("test").category(),
664            ErrorCategory::Data
665        );
666        assert_eq!(
667            Error::configuration("test").category(),
668            ErrorCategory::Configuration
669        );
670        assert_eq!(Error::memory("test").category(), ErrorCategory::System);
671        assert_eq!(
672            Error::concurrency("test").category(),
673            ErrorCategory::Concurrency
674        );
675        assert_eq!(Error::not_found("test").category(), ErrorCategory::NotFound);
676        assert_eq!(
677            Error::already_exists("test").category(),
678            ErrorCategory::Conflict
679        );
680        assert_eq!(
681            Error::invalid_operation("test").category(),
682            ErrorCategory::Logic
683        );
684        assert_eq!(
685            Error::constraint_violation("test").category(),
686            ErrorCategory::Constraint
687        );
688        assert_eq!(
689            Error::transaction("test").category(),
690            ErrorCategory::Transaction
691        );
692        assert_eq!(Error::index("test").category(), ErrorCategory::Storage);
693        assert_eq!(Error::compaction("test").category(), ErrorCategory::Storage);
694        assert_eq!(Error::internal("test").category(), ErrorCategory::Internal);
695        assert_eq!(Error::invalid_input("test").category(), ErrorCategory::Data);
696        assert_eq!(Error::parse("test").category(), ErrorCategory::Data);
697        assert_eq!(
698            Error::write_dir_locked("/tmp/test").category(),
699            ErrorCategory::Concurrency
700        );
701    }
702
703    #[test]
704    fn test_all_error_recoverability() {
705        // Test recoverability for all error types
706        assert!(Error::memory("test").is_recoverable());
707        assert!(Error::storage("test").is_recoverable());
708        assert!(Error::transaction("test").is_recoverable());
709        assert!(Error::index("test").is_recoverable());
710        assert!(Error::compaction("test").is_recoverable());
711
712        assert!(!Error::serialization("test").is_recoverable());
713        assert!(!Error::cql_parse("test").is_recoverable());
714        assert!(!Error::invalid_format("test").is_recoverable());
715        assert!(!Error::unsupported_format("test").is_recoverable());
716        assert!(!Error::invalid_path("test").is_recoverable());
717        assert!(!Error::invalid_state("test").is_recoverable());
718        assert!(!Error::query_execution("test").is_recoverable());
719        assert!(!Error::type_conversion("test").is_recoverable());
720        assert!(!Error::configuration("test").is_recoverable());
721        assert!(!Error::not_found("test").is_recoverable());
722        assert!(!Error::already_exists("test").is_recoverable());
723        assert!(!Error::invalid_operation("test").is_recoverable());
724        assert!(!Error::constraint_violation("test").is_recoverable());
725        assert!(!Error::internal("test").is_recoverable());
726        assert!(!Error::invalid_input("test").is_recoverable());
727        assert!(!Error::parse("test").is_recoverable());
728        assert!(!Error::write_dir_locked("/tmp/test").is_recoverable());
729    }
730
731    #[test]
732    fn test_error_category_display() {
733        // Test ErrorCategory Display implementation
734        assert_eq!(ErrorCategory::System.to_string(), "System");
735        assert_eq!(ErrorCategory::Data.to_string(), "Data");
736        assert_eq!(ErrorCategory::Schema.to_string(), "Schema");
737        assert_eq!(ErrorCategory::Query.to_string(), "Query");
738        assert_eq!(ErrorCategory::Configuration.to_string(), "Configuration");
739        assert_eq!(ErrorCategory::Storage.to_string(), "Storage");
740        assert_eq!(ErrorCategory::Concurrency.to_string(), "Concurrency");
741        assert_eq!(ErrorCategory::NotFound.to_string(), "NotFound");
742        assert_eq!(ErrorCategory::Conflict.to_string(), "Conflict");
743        assert_eq!(ErrorCategory::Logic.to_string(), "Logic");
744        assert_eq!(ErrorCategory::Constraint.to_string(), "Constraint");
745        assert_eq!(ErrorCategory::Transaction.to_string(), "Transaction");
746        assert_eq!(ErrorCategory::Platform.to_string(), "Platform");
747        assert_eq!(ErrorCategory::Internal.to_string(), "Internal");
748    }
749
750    #[test]
751    fn test_error_from_io_error() {
752        // Test conversion from std::io::Error
753        let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
754        let cqlite_err: Error = io_err.into();
755        assert!(matches!(cqlite_err, Error::Io(_)));
756        assert_eq!(cqlite_err.category(), ErrorCategory::System);
757        assert!(cqlite_err.is_recoverable());
758    }
759
760    #[test]
761    fn test_result_type_alias() {
762        // Test the Result type alias
763        let success: Result<i32> = Ok(42);
764        let failure: Result<i32> = Err(Error::storage("test error"));
765
766        assert!(success.is_ok());
767        if let Ok(value) = success {
768            assert_eq!(value, 42);
769        }
770        assert!(failure.is_err());
771    }
772}