Skip to main content

cypherlite_core/
error.rs

1// CypherLiteError definitions
2
3/// All errors that can occur in CypherLite operations.
4#[derive(thiserror::Error, Debug)]
5pub enum CypherLiteError {
6    /// Wrapper for standard I/O errors.
7    #[error("I/O error: {0}")]
8    IoError(#[from] std::io::Error),
9
10    /// A database page failed integrity checks.
11    #[error("Corrupted page {page_id}: {reason}")]
12    CorruptedPage {
13        /// The page number that is corrupted.
14        page_id: u32,
15        /// Human-readable description of the corruption.
16        reason: String,
17    },
18
19    /// A write transaction could not be acquired because another is active.
20    #[error("Transaction conflict: write lock unavailable")]
21    TransactionConflict,
22
23    /// The buffer pool or disk is full.
24    #[error("Out of space: buffer pool or disk full")]
25    OutOfSpace,
26
27    /// The database file does not start with the expected magic bytes.
28    #[error("Invalid magic number")]
29    InvalidMagicNumber,
30
31    /// The database file format version is not supported.
32    #[error("Unsupported version: found {found}, supported {supported}")]
33    UnsupportedVersion {
34        /// The version found in the file.
35        found: u32,
36        /// The version this build supports.
37        supported: u32,
38    },
39
40    /// A checksum did not match the expected value.
41    #[error("Checksum mismatch: expected {expected}, found {found}")]
42    ChecksumMismatch {
43        /// The expected checksum value.
44        expected: u64,
45        /// The actual checksum value found.
46        found: u64,
47    },
48
49    /// Serialization or deserialization failed.
50    #[error("Serialization error: {0}")]
51    SerializationError(String),
52
53    /// The requested node does not exist.
54    #[error("Node not found: {0}")]
55    NodeNotFound(u64),
56
57    /// The requested edge does not exist.
58    #[error("Edge not found: {0}")]
59    EdgeNotFound(u64),
60
61    /// A Cypher query could not be parsed.
62    #[error("Parse error at line {line}, column {column}: {message}")]
63    ParseError {
64        /// The 1-based line number where the error occurred.
65        line: usize,
66        /// The 1-based column number where the error occurred.
67        column: usize,
68        /// Description of the parse error.
69        message: String,
70    },
71
72    /// A semantically invalid query was detected.
73    #[error("Semantic error: {0}")]
74    SemanticError(String),
75
76    /// An error occurred during query execution.
77    #[error("Execution error: {0}")]
78    ExecutionError(String),
79
80    /// The query uses syntax not yet implemented.
81    #[error("Unsupported syntax: {0}")]
82    UnsupportedSyntax(String),
83
84    /// A constraint (e.g. uniqueness) was violated.
85    #[error("Constraint violation: {0}")]
86    ConstraintViolation(String),
87
88    /// A datetime string could not be parsed.
89    #[error("Invalid datetime format: {0}")]
90    InvalidDateTimeFormat(String),
91
92    /// Attempt to write a system-managed property (prefixed with `_`).
93    #[error("System property is read-only: {0}")]
94    SystemPropertyReadOnly(String),
95
96    /// The database requires features not compiled into this binary.
97    #[error("Feature incompatible: database requires flags 0x{db_flags:08X}, compiled with 0x{compiled_flags:08X}")]
98    FeatureIncompatible {
99        /// The feature flags stored in the database header.
100        db_flags: u32,
101        /// The feature flags compiled into this binary.
102        compiled_flags: u32,
103    },
104
105    /// The requested subgraph does not exist.
106    #[cfg(feature = "subgraph")]
107    #[error("Subgraph not found: {0}")]
108    SubgraphNotFound(u64),
109
110    /// An operation requires subgraph support but the feature is not compiled.
111    #[cfg(feature = "subgraph")]
112    #[error("Feature requires subgraph support (compile with --features subgraph)")]
113    FeatureRequiresSubgraph,
114
115    /// The requested hyperedge does not exist.
116    #[cfg(feature = "hypergraph")]
117    #[error("Hyperedge not found: {0}")]
118    HyperEdgeNotFound(u64),
119
120    /// A generic plugin error occurred.
121    #[cfg(feature = "plugin")]
122    #[error("Plugin error: {0}")]
123    PluginError(String),
124
125    /// A requested custom function was not found.
126    #[cfg(feature = "plugin")]
127    #[error("Function not found: {0}")]
128    FunctionNotFound(String),
129
130    /// An unsupported index type was requested.
131    #[cfg(feature = "plugin")]
132    #[error("Unsupported index type: {0}")]
133    UnsupportedIndexType(String),
134
135    /// An unsupported serialization format was requested.
136    #[cfg(feature = "plugin")]
137    #[error("Unsupported format: {0}")]
138    UnsupportedFormat(String),
139
140    /// An error occurred during trigger execution.
141    #[cfg(feature = "plugin")]
142    #[error("Trigger error: {0}")]
143    TriggerError(String),
144}
145
146/// Convenience type alias for CypherLite operations.
147pub type Result<T> = std::result::Result<T, CypherLiteError>;
148
149#[cfg(test)]
150mod tests {
151    use super::*;
152
153    // REQ-PAGE-007: InvalidMagicNumber error exists
154    #[test]
155    fn test_invalid_magic_number_error() {
156        let err = CypherLiteError::InvalidMagicNumber;
157        assert_eq!(format!("{err}"), "Invalid magic number");
158    }
159
160    // REQ-PAGE-008: UnsupportedVersion error with found/supported
161    #[test]
162    fn test_unsupported_version_error() {
163        let err = CypherLiteError::UnsupportedVersion {
164            found: 99,
165            supported: 1,
166        };
167        assert_eq!(
168            format!("{err}"),
169            "Unsupported version: found 99, supported 1"
170        );
171    }
172
173    // REQ-WAL-007: Checksum mismatch error
174    #[test]
175    fn test_checksum_mismatch_error() {
176        let err = CypherLiteError::ChecksumMismatch {
177            expected: 100,
178            found: 200,
179        };
180        assert_eq!(
181            format!("{err}"),
182            "Checksum mismatch: expected 100, found 200"
183        );
184    }
185
186    // REQ-TX-010: Transaction conflict error
187    #[test]
188    fn test_transaction_conflict_error() {
189        let err = CypherLiteError::TransactionConflict;
190        assert_eq!(
191            format!("{err}"),
192            "Transaction conflict: write lock unavailable"
193        );
194    }
195
196    // REQ-BUF-006: Out of space error
197    #[test]
198    fn test_out_of_space_error() {
199        let err = CypherLiteError::OutOfSpace;
200        assert_eq!(format!("{err}"), "Out of space: buffer pool or disk full");
201    }
202
203    #[test]
204    fn test_corrupted_page_error() {
205        let err = CypherLiteError::CorruptedPage {
206            page_id: 5,
207            reason: "bad header".to_string(),
208        };
209        assert_eq!(format!("{err}"), "Corrupted page 5: bad header");
210    }
211
212    #[test]
213    fn test_io_error_conversion() {
214        let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file missing");
215        let err: CypherLiteError = io_err.into();
216        assert!(matches!(err, CypherLiteError::IoError(_)));
217        assert!(format!("{err}").contains("file missing"));
218    }
219
220    #[test]
221    fn test_error_is_send_sync() {
222        fn assert_send<T: Send>() {}
223        fn assert_sync<T: Sync>() {}
224        assert_send::<CypherLiteError>();
225        assert_sync::<CypherLiteError>();
226    }
227
228    #[test]
229    fn test_node_not_found_error() {
230        let err = CypherLiteError::NodeNotFound(42);
231        assert_eq!(format!("{err}"), "Node not found: 42");
232    }
233
234    #[test]
235    fn test_edge_not_found_error() {
236        let err = CypherLiteError::EdgeNotFound(99);
237        assert_eq!(format!("{err}"), "Edge not found: 99");
238    }
239
240    // REQ-QUERY-001: Parse error with location
241    #[test]
242    fn test_parse_error() {
243        let err = CypherLiteError::ParseError {
244            line: 3,
245            column: 10,
246            message: "unexpected token".to_string(),
247        };
248        assert_eq!(
249            format!("{err}"),
250            "Parse error at line 3, column 10: unexpected token"
251        );
252    }
253
254    // REQ-QUERY-002: Semantic error
255    #[test]
256    fn test_semantic_error() {
257        let err = CypherLiteError::SemanticError("unknown label".to_string());
258        assert_eq!(format!("{err}"), "Semantic error: unknown label");
259    }
260
261    // REQ-QUERY-003: Execution error
262    #[test]
263    fn test_execution_error() {
264        let err = CypherLiteError::ExecutionError("division by zero".to_string());
265        assert_eq!(format!("{err}"), "Execution error: division by zero");
266    }
267
268    // REQ-QUERY-004: Unsupported syntax
269    #[test]
270    fn test_unsupported_syntax_error() {
271        let err = CypherLiteError::UnsupportedSyntax("MERGE".to_string());
272        assert_eq!(format!("{err}"), "Unsupported syntax: MERGE");
273    }
274
275    // REQ-QUERY-005: Constraint violation
276    #[test]
277    fn test_constraint_violation_error() {
278        let err = CypherLiteError::ConstraintViolation("unique key violated".to_string());
279        assert_eq!(
280            format!("{err}"),
281            "Constraint violation: unique key violated"
282        );
283    }
284
285    // V-003: SystemPropertyReadOnly error
286    #[test]
287    fn test_system_property_read_only_error() {
288        let err = CypherLiteError::SystemPropertyReadOnly("_created_at".to_string());
289        assert_eq!(
290            format!("{err}"),
291            "System property is read-only: _created_at"
292        );
293    }
294
295    // U-002: InvalidDateTimeFormat error
296    #[test]
297    fn test_invalid_datetime_format_error() {
298        let err = CypherLiteError::InvalidDateTimeFormat("bad input".to_string());
299        assert_eq!(format!("{err}"), "Invalid datetime format: bad input");
300    }
301
302    // AA-T4: FeatureIncompatible error
303    #[test]
304    fn test_feature_incompatible_error() {
305        let err = CypherLiteError::FeatureIncompatible {
306            db_flags: 0x03,
307            compiled_flags: 0x01,
308        };
309        assert_eq!(
310            format!("{err}"),
311            "Feature incompatible: database requires flags 0x00000003, compiled with 0x00000001"
312        );
313    }
314
315    // ======================================================================
316    // GG-001: SubgraphNotFound error
317    // ======================================================================
318
319    #[cfg(feature = "subgraph")]
320    #[test]
321    fn test_subgraph_not_found_error() {
322        let err = CypherLiteError::SubgraphNotFound(42);
323        assert_eq!(format!("{err}"), "Subgraph not found: 42");
324    }
325
326    #[cfg(feature = "subgraph")]
327    #[test]
328    fn test_feature_requires_subgraph_error() {
329        let err = CypherLiteError::FeatureRequiresSubgraph;
330        assert_eq!(
331            format!("{err}"),
332            "Feature requires subgraph support (compile with --features subgraph)"
333        );
334    }
335}