Skip to main content

nitrite/
errors.rs

1use backtrace::Backtrace;
2use serde::{de, ser};
3use std::error::Error;
4use std::fmt::{Debug, Display, Formatter};
5use std::result::Result;
6
7use crate::{atomic, Atomic};
8
9/// Error kinds for Nitrite operations
10///
11/// This enum represents all possible error types that can occur during Nitrite database operations.
12/// Each error kind describes a specific category of failure, enabling precise error handling.
13///
14/// # Examples
15///
16/// ```rust,ignore
17/// use nitrite::errors::{NitriteError, ErrorKind, NitriteResult};
18///
19/// fn example() -> NitriteResult<()> {
20///     Err(NitriteError::new("Index not found", ErrorKind::IndexNotFound))
21/// }
22/// ```
23#[derive(Debug, PartialEq, Eq, Clone)]
24pub enum ErrorKind {
25    // Filter Errors - actively used in pattern matching
26    /// Error during filter evaluation or construction
27    FilterError,
28    
29    // Indexing Errors - actively used in index operations
30    /// Generic indexing error
31    IndexingError,
32    /// Index does not exist
33    IndexNotFound,
34    /// Index already exists at the specified location
35    IndexAlreadyExists,
36    /// Failed to build or rebuild an index
37    IndexBuildFailed,
38    /// Index data is corrupted
39    IndexCorrupted,
40    /// Index type does not match operation
41    IndexTypeMismatch,
42    /// An indexing operation is already in progress
43    IndexingInProgress,
44    
45    // ID and Identity Errors - actively used in collection operations
46    /// The provided ID is invalid
47    InvalidId,
48    /// The entity is not identifiable
49    NotIdentifiable,
50    /// The requested resource was not found
51    NotFound,
52    
53    // Operation Errors - actively used for invalid/unsupported operations
54    /// The operation is not valid in the current context
55    InvalidOperation,
56    
57    // IO and Storage Errors - actively used in file/store operations
58    /// Generic IO error
59    IOError,
60    /// The disk is full
61    DiskFull,
62    /// The file was not found
63    FileNotFound,
64    /// Permission denied for file operation
65    PermissionDenied,
66    /// File data is corrupted
67    FileCorrupted,
68    /// Error accessing file
69    FileAccessError,
70    
71    // Data Encoding Errors - actively used in serialization/UTF-8 conversion
72    /// Error encoding or decoding data
73    EncodingError,
74    /// Error mapping object to/from document
75    ObjectMappingError,
76    
77    // Security Errors - actively used in authentication/authorization
78    /// Security-related error (authentication, authorization, etc.)
79    SecurityError,
80    
81    // Constraint Violation Errors - actively used in index uniqueness checks
82    /// A unique constraint was violated
83    UniqueConstraintViolation,
84    
85    // Validation Errors - actively used in field/data validation
86    /// Generic validation error
87    ValidationError,
88    /// Invalid data type for operation
89    InvalidDataType,
90    /// Invalid field name
91    InvalidFieldName,
92    /// A required field is missing
93    MissingRequiredField,
94    
95    // Collection/Repository Errors - actively used in collection lookups
96    /// Collection does not exist
97    CollectionNotFound,
98    /// Repository does not exist
99    RepositoryNotFound,
100    
101    // Event Errors - actively used in event bus operations
102    /// Error in event processing
103    EventError,
104    
105    // Plugin Errors - actively used in plugin loading
106    /// Generic plugin error
107    PluginError,
108    /// Failed to load a plugin
109    PluginLoadFailed,
110    
111    // Backend and Store Errors - actively used in store state management
112    /// Error from storage backend
113    BackendError,
114    /// Store has not been initialized
115    StoreNotInitialized,
116    /// Store has already been closed
117    StoreAlreadyClosed,
118    /// An atomic store transaction could not be committed because a concurrent
119    /// transaction conflicted with it (serializable-snapshot-isolation conflict).
120    /// The operation may be retried.
121    TransactionConflict,
122
123    // Migration Errors - actively used in migration operations
124    /// Error during schema migration
125    MigrationError,
126    
127    // Extension Errors - allows external crates to plug in their own error types
128    // The String contains the extension name/category (e.g., "spatial", "fulltext")
129    /// Error from an extension module (e.g., spatial, fulltext)
130    Extension(String),
131    
132    // Generic/Internal Errors - used as fallback
133    /// Internal error (usually indicates a bug)
134    InternalError,
135}
136
137impl Display for ErrorKind {
138    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
139        match self {
140            ErrorKind::FilterError => write!(f, "Filter error"),
141            ErrorKind::IndexingError => write!(f, "Indexing error"),
142            ErrorKind::IndexNotFound => write!(f, "Index not found"),
143            ErrorKind::IndexAlreadyExists => write!(f, "Index already exists"),
144            ErrorKind::IndexBuildFailed => write!(f, "Index build failed"),
145            ErrorKind::IndexCorrupted => write!(f, "Index corrupted"),
146            ErrorKind::IndexTypeMismatch => write!(f, "Index type mismatch"),
147            ErrorKind::IndexingInProgress => write!(f, "Indexing in progress"),
148            ErrorKind::InvalidId => write!(f, "Invalid ID"),
149            ErrorKind::NotIdentifiable => write!(f, "Not identifiable"),
150            ErrorKind::NotFound => write!(f, "Not found"),
151            ErrorKind::InvalidOperation => write!(f, "Invalid operation"),
152            ErrorKind::IOError => write!(f, "IO error"),
153            ErrorKind::DiskFull => write!(f, "Disk full"),
154            ErrorKind::FileNotFound => write!(f, "File not found"),
155            ErrorKind::PermissionDenied => write!(f, "Permission denied"),
156            ErrorKind::FileCorrupted => write!(f, "File corrupted"),
157            ErrorKind::FileAccessError => write!(f, "File access error"),
158            ErrorKind::EncodingError => write!(f, "Encoding error"),
159            ErrorKind::ObjectMappingError => write!(f, "Object mapping error"),
160            ErrorKind::SecurityError => write!(f, "Security error"),
161            ErrorKind::UniqueConstraintViolation => write!(f, "Unique constraint violation"),
162            ErrorKind::ValidationError => write!(f, "Validation error"),
163            ErrorKind::InvalidDataType => write!(f, "Invalid data type"),
164            ErrorKind::InvalidFieldName => write!(f, "Invalid field name"),
165            ErrorKind::MissingRequiredField => write!(f, "Missing required field"),
166            ErrorKind::CollectionNotFound => write!(f, "Collection not found"),
167            ErrorKind::RepositoryNotFound => write!(f, "Repository not found"),
168            ErrorKind::EventError => write!(f, "Event error"),
169            ErrorKind::PluginError => write!(f, "Plugin error"),
170            ErrorKind::PluginLoadFailed => write!(f, "Plugin load failed"),
171            ErrorKind::BackendError => write!(f, "Backend error"),
172            ErrorKind::StoreNotInitialized => write!(f, "Store not initialized"),
173            ErrorKind::StoreAlreadyClosed => write!(f, "Store already closed"),
174            ErrorKind::TransactionConflict => write!(f, "Transaction conflict"),
175            ErrorKind::MigrationError => write!(f, "Migration error"),
176            ErrorKind::Extension(name) => write!(f, "{} error", name),
177            ErrorKind::InternalError => write!(f, "Internal error"),
178        }
179    }
180}
181
182/// Custom Nitrite error type.
183///
184/// `NitriteError` encapsulates error information including the error message, kind, and optional cause.
185/// It supports error chaining and backtraces for debugging.
186///
187/// # Examples
188///
189/// ```rust,ignore
190/// use nitrite::errors::{NitriteError, ErrorKind};
191///
192/// // Create a simple error
193/// let err = NitriteError::new("Index not found", ErrorKind::IndexNotFound);
194///
195/// // Create an error with a cause
196/// let cause = NitriteError::new("IO failed", ErrorKind::IOError);
197/// let err = NitriteError::new_with_cause("Index build failed", ErrorKind::IndexBuildFailed, cause);
198/// ```
199///
200/// # Type alias
201///
202/// The `NitriteResult<T>` type alias is equivalent to `Result<T, NitriteError>` and is used
203/// throughout the codebase for operations that can fail.
204#[derive(Clone)]
205pub struct NitriteError {
206    message: String,
207    error_kind: ErrorKind,
208    cause: Option<Box<NitriteError>>,
209    backtrace: Atomic<Backtrace>,
210}
211
212impl NitriteError {
213    /// Creates a new `NitriteError` with the specified message and error kind.
214    ///
215    /// # Arguments
216    ///
217    /// * `message` - A description of the error
218    /// * `error_kind` - The category of error
219    ///
220    /// # Returns
221    ///
222    /// A new `NitriteError` instance.
223    pub fn new(message: &str, error_kind: ErrorKind) -> Self {
224        NitriteError {
225            message: message.to_string(),
226            error_kind,
227            cause: None,
228            backtrace: atomic(Backtrace::new()),
229        }
230    }
231
232    /// Creates a new `NitriteError` with a cause error.
233    ///
234    /// This creates an error chain where the cause error is preserved for debugging.
235    ///
236    /// # Arguments
237    ///
238    /// * `message` - A description of the error
239    /// * `error_type` - The category of error
240    /// * `cause` - The underlying error that caused this error
241    ///
242    /// # Returns
243    ///
244    /// A new `NitriteError` instance with the cause error attached.
245    pub fn new_with_cause(message: &str, error_type: ErrorKind, cause: NitriteError) -> Self {
246        NitriteError {
247            message: message.to_string(),
248            error_kind: error_type,
249            cause: Some(Box::new(cause)),
250            backtrace: atomic(Backtrace::new()),
251        }
252    }
253    
254    pub fn message(&self) -> &str {
255        &self.message
256    }
257    
258    pub fn kind(&self) -> &ErrorKind {
259        &self.error_kind
260    }
261    
262    pub fn cause(&self) -> Option<&NitriteError> {
263        self.cause.as_deref()
264    }
265}
266
267impl Display for NitriteError {
268    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
269        write!(f, "{}", self.message)
270    }
271}
272
273impl Debug for NitriteError {
274    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
275        // print error message with stack trace followed by cause
276        match &self.cause {
277            Some(cause) => write!(f, "{}\nCaused by: {:?}", self.message, cause),
278            None => write!(f, "{}\n{:?}", self.message, self.backtrace.read()),
279        }
280    }
281}
282
283impl Error for NitriteError {
284    fn source(&self) -> Option<&(dyn Error + 'static)> {
285        match &self.cause {
286            Some(cause) => Some(cause.as_ref()),
287            None => None,
288        }
289    }
290}
291
292/// A result type alias for Nitrite operations.
293///
294/// `NitriteResult<T>` is shorthand for `Result<T, NitriteError>`.
295/// All fallible Nitrite operations return this type.
296///
297/// # Examples
298///
299/// ```rust,ignore
300/// use nitrite::errors::NitriteResult;
301///
302/// fn find_collection(name: &str) -> NitriteResult<String> {
303///     // Return success
304///     Ok(name.to_string())
305///     // Or return error
306///     // Err(NitriteError::new("Collection not found", ErrorKind::CollectionNotFound))
307/// }
308/// ```
309pub type NitriteResult<T> = Result<T, NitriteError>;
310
311impl de::Error for NitriteError {
312    fn custom<T: Display>(msg: T) -> Self {
313        NitriteError::new(&msg.to_string(), ErrorKind::ObjectMappingError)
314    }
315}
316
317impl ser::Error for NitriteError {
318    fn custom<T: Display>(msg: T) -> Self {
319        NitriteError::new(&msg.to_string(), ErrorKind::ObjectMappingError)
320    }
321}
322
323// From trait implementations for automatic error conversion
324impl From<std::io::Error> for NitriteError {
325    fn from(err: std::io::Error) -> Self {
326        let error_kind = match err.kind() {
327            std::io::ErrorKind::NotFound => ErrorKind::FileNotFound,
328            std::io::ErrorKind::PermissionDenied => ErrorKind::PermissionDenied,
329            std::io::ErrorKind::AlreadyExists => ErrorKind::FileCorrupted,
330            _ => ErrorKind::IOError,
331        };
332        NitriteError::new(&format!("IO error: {}", err), error_kind)
333    }
334}
335
336impl From<std::string::FromUtf8Error> for NitriteError {
337    fn from(err: std::string::FromUtf8Error) -> Self {
338        NitriteError::new(
339            &format!("UTF-8 encoding error: {}", err),
340            ErrorKind::EncodingError,
341        )
342    }
343}
344
345impl From<std::fmt::Error> for NitriteError {
346    fn from(err: std::fmt::Error) -> Self {
347        NitriteError::new(
348            &format!("Formatting error: {}", err),
349            ErrorKind::InternalError,
350        )
351    }
352}
353
354impl From<std::num::ParseIntError> for NitriteError {
355    fn from(err: std::num::ParseIntError) -> Self {
356        NitriteError::new(
357            &format!("Integer parsing error: {}", err),
358            ErrorKind::InvalidDataType,
359        )
360    }
361}
362
363impl From<std::num::ParseFloatError> for NitriteError {
364    fn from(err: std::num::ParseFloatError) -> Self {
365        NitriteError::new(
366            &format!("Float parsing error: {}", err),
367            ErrorKind::InvalidDataType,
368        )
369    }
370}
371
372impl From<String> for NitriteError {
373    fn from(msg: String) -> Self {
374        NitriteError::new(&msg, ErrorKind::InternalError)
375    }
376}
377
378impl From<&str> for NitriteError {
379    fn from(msg: &str) -> Self {
380        NitriteError::new(msg, ErrorKind::InternalError)
381    }
382}
383
384#[cfg(test)]
385mod tests {
386    use super::*;
387
388    fn create_io_error() -> Box<dyn Error + Send + Sync> {
389        Box::new(std::io::Error::other("IO Error"))
390    }
391
392    #[test]
393    fn nitrite_error_new_creates_error() {
394        let error = NitriteError::new("An error occurred", ErrorKind::IOError);
395        assert_eq!(error.message, "An error occurred");
396        assert_eq!(error.error_kind, ErrorKind::IOError);
397        assert!(error.cause.is_none());
398    }
399
400    #[test]
401    fn nitrite_error_new_with_cause_creates_error() {
402        let cause = create_io_error();
403        let error = NitriteError::new_with_cause(
404            "An error occurred",
405            ErrorKind::IOError,
406            NitriteError::new(&cause.to_string(), ErrorKind::IOError),
407        );
408        assert_eq!(error.message, "An error occurred");
409        assert_eq!(error.error_kind, ErrorKind::IOError);
410        assert!(error.cause.is_some());
411    }
412
413    #[test]
414    fn nitrite_error_message_returns_message() {
415        let error = NitriteError::new("An error occurred", ErrorKind::IOError);
416        assert_eq!(error.message(), "An error occurred");
417    }
418
419    #[test]
420    fn nitrite_error_kind_returns_kind() {
421        let error = NitriteError::new("An error occurred", ErrorKind::IOError);
422        assert_eq!(error.kind(), &ErrorKind::IOError);
423    }
424
425    #[test]
426    fn nitrite_error_cause_returns_cause() {
427        let cause = create_io_error();
428        let error = NitriteError::new_with_cause(
429            "An error occurred",
430            ErrorKind::IOError,
431            NitriteError::new(&cause.to_string(), ErrorKind::IOError),
432        );
433        assert!(error.cause().is_some());
434    }
435
436    #[test]
437    fn nitrite_error_cause_returns_none_when_no_cause() {
438        let error = NitriteError::new("An error occurred", ErrorKind::IOError);
439        assert!(error.cause().is_none());
440    }
441
442    #[test]
443    fn nitrite_error_display_formats_correctly() {
444        let error = NitriteError::new("An error occurred", ErrorKind::IOError);
445        let formatted = format!("{}", error);
446        assert_eq!(formatted, "An error occurred");
447    }
448
449    #[test]
450    fn nitrite_error_debug_formats_correctly() {
451        let error = NitriteError::new("An error occurred", ErrorKind::IOError);
452        let formatted = format!("{:?}", error);
453        assert!(formatted.contains("An error occurred"));
454    }
455
456    #[test]
457    fn nitrite_error_debug_formats_with_cause() {
458        let cause = create_io_error();
459        let error = NitriteError::new_with_cause(
460            "An error occurred",
461            ErrorKind::IOError,
462            NitriteError::new(&cause.to_string(), ErrorKind::IOError),
463        );
464        let formatted = format!("{:?}", error);
465        assert!(formatted.contains("An error occurred"));
466        assert!(formatted.contains("Caused by:"));
467    }
468
469    #[test]
470    fn nitrite_error_source_returns_cause() {
471        let cause = create_io_error();
472        let error = NitriteError::new_with_cause(
473            "An error occurred",
474            ErrorKind::IOError,
475            NitriteError::new(&cause.to_string(), ErrorKind::IOError),
476        );
477        assert!(error.source().is_some());
478    }
479
480    #[test]
481    fn nitrite_error_source_returns_none_when_no_cause() {
482        let error = NitriteError::new("An error occurred", ErrorKind::IOError);
483        assert!(error.source().is_none());
484    }
485
486    #[test]
487    fn nitrite_error_ser_custom_creates_error() {
488        let error = NitriteError::new("Custom error", ErrorKind::ObjectMappingError);
489        assert_eq!(error.message(), "Custom error");
490        assert_eq!(error.kind(), &ErrorKind::ObjectMappingError);
491    }
492
493    // Test Filter Errors
494    #[test]
495    fn test_filter_errors() {
496        let filter_error = NitriteError::new("Invalid filter syntax", ErrorKind::FilterError);
497        assert_eq!(filter_error.kind(), &ErrorKind::FilterError);
498    }
499
500    // Test ID and Identity Errors
501    #[test]
502    fn test_id_errors() {
503        let invalid_id = NitriteError::new("Invalid ID format", ErrorKind::InvalidId);
504        assert_eq!(invalid_id.kind(), &ErrorKind::InvalidId);
505
506        let not_identifiable = NitriteError::new("Entity not identifiable", ErrorKind::NotIdentifiable);
507        assert_eq!(not_identifiable.kind(), &ErrorKind::NotIdentifiable);
508    }
509
510    // Test Indexing Errors
511    #[test]
512    fn test_indexing_errors() {
513        let index_error = NitriteError::new("Indexing failed", ErrorKind::IndexingError);
514        assert_eq!(index_error.kind(), &ErrorKind::IndexingError);
515
516        let not_found = NitriteError::new("Index not found", ErrorKind::IndexNotFound);
517        assert_eq!(not_found.kind(), &ErrorKind::IndexNotFound);
518
519        let exists = NitriteError::new("Index already exists", ErrorKind::IndexAlreadyExists);
520        assert_eq!(exists.kind(), &ErrorKind::IndexAlreadyExists);
521
522        let build_failed = NitriteError::new("Index build failed", ErrorKind::IndexBuildFailed);
523        assert_eq!(build_failed.kind(), &ErrorKind::IndexBuildFailed);
524
525        let corrupted = NitriteError::new("Index corrupted", ErrorKind::IndexCorrupted);
526        assert_eq!(corrupted.kind(), &ErrorKind::IndexCorrupted);
527
528        let type_mismatch = NitriteError::new("Index type mismatch", ErrorKind::IndexTypeMismatch);
529        assert_eq!(type_mismatch.kind(), &ErrorKind::IndexTypeMismatch);
530
531        let in_progress = NitriteError::new("Indexing in progress", ErrorKind::IndexingInProgress);
532        assert_eq!(in_progress.kind(), &ErrorKind::IndexingInProgress);
533    }
534
535    // Test IO and Storage Errors
536    #[test]
537    fn test_io_errors() {
538        let io_error = NitriteError::new("IO error", ErrorKind::IOError);
539        assert_eq!(io_error.kind(), &ErrorKind::IOError);
540
541        let disk_full = NitriteError::new("Disk full", ErrorKind::DiskFull);
542        assert_eq!(disk_full.kind(), &ErrorKind::DiskFull);
543
544        let file_not_found = NitriteError::new("File not found", ErrorKind::FileNotFound);
545        assert_eq!(file_not_found.kind(), &ErrorKind::FileNotFound);
546
547        let permission = NitriteError::new("Permission denied", ErrorKind::PermissionDenied);
548        assert_eq!(permission.kind(), &ErrorKind::PermissionDenied);
549
550        let corrupted = NitriteError::new("File corrupted", ErrorKind::FileCorrupted);
551        assert_eq!(corrupted.kind(), &ErrorKind::FileCorrupted);
552
553        let access = NitriteError::new("File access error", ErrorKind::FileAccessError);
554        assert_eq!(access.kind(), &ErrorKind::FileAccessError);
555    }
556
557    // Test Security Errors
558    #[test]
559    fn test_security_errors() {
560        let security = NitriteError::new("Security error", ErrorKind::SecurityError);
561        assert_eq!(security.kind(), &ErrorKind::SecurityError);
562    }
563
564    // Test Constraint Violation Errors
565    #[test]
566    fn test_constraint_errors() {
567        let unique = NitriteError::new("Unique constraint violated", ErrorKind::UniqueConstraintViolation);
568        assert_eq!(unique.kind(), &ErrorKind::UniqueConstraintViolation);
569    }
570
571    // Test Validation Errors
572    #[test]
573    fn test_validation_errors() {
574        let validation = NitriteError::new("Validation failed", ErrorKind::ValidationError);
575        assert_eq!(validation.kind(), &ErrorKind::ValidationError);
576
577        let invalid_type = NitriteError::new("Invalid data type", ErrorKind::InvalidDataType);
578        assert_eq!(invalid_type.kind(), &ErrorKind::InvalidDataType);
579
580        let invalid_name = NitriteError::new("Invalid field name", ErrorKind::InvalidFieldName);
581        assert_eq!(invalid_name.kind(), &ErrorKind::InvalidFieldName);
582
583        let missing = NitriteError::new("Missing required field", ErrorKind::MissingRequiredField);
584        assert_eq!(missing.kind(), &ErrorKind::MissingRequiredField);
585    }
586
587    // Test Collection/Repository Errors
588    #[test]
589    fn test_collection_repository_errors() {
590        let not_found = NitriteError::new("Collection not found", ErrorKind::CollectionNotFound);
591        assert_eq!(not_found.kind(), &ErrorKind::CollectionNotFound);
592
593        let repo_not_found = NitriteError::new("Repository not found", ErrorKind::RepositoryNotFound);
594        assert_eq!(repo_not_found.kind(), &ErrorKind::RepositoryNotFound);
595    }
596
597    // Test Event/Subscription Errors
598    #[test]
599    fn test_event_subscription_errors() {
600        let event = NitriteError::new("Event error", ErrorKind::EventError);
601        assert_eq!(event.kind(), &ErrorKind::EventError);
602    }
603
604    // Test Plugin Errors
605    #[test]
606    fn test_plugin_errors() {
607        let plugin = NitriteError::new("Plugin error", ErrorKind::PluginError);
608        assert_eq!(plugin.kind(), &ErrorKind::PluginError);
609
610        let load_failed = NitriteError::new("Plugin load failed", ErrorKind::PluginLoadFailed);
611        assert_eq!(load_failed.kind(), &ErrorKind::PluginLoadFailed);
612    }
613
614    // Test Backend/Store Errors
615    #[test]
616    fn test_backend_store_errors() {
617        let backend = NitriteError::new("Backend error", ErrorKind::BackendError);
618        assert_eq!(backend.kind(), &ErrorKind::BackendError);
619
620        let not_init = NitriteError::new("Store not initialized", ErrorKind::StoreNotInitialized);
621        assert_eq!(not_init.kind(), &ErrorKind::StoreNotInitialized);
622
623        let closed = NitriteError::new("Store already closed", ErrorKind::StoreAlreadyClosed);
624        assert_eq!(closed.kind(), &ErrorKind::StoreAlreadyClosed);
625    }
626
627    // Test Internal and Unknown Errors
628    #[test]
629    fn test_internal_errors() {
630        let internal = NitriteError::new("Internal error", ErrorKind::InternalError);
631        assert_eq!(internal.kind(), &ErrorKind::InternalError);
632    }
633
634    // Test Extension Errors - for external crates to plug in their own error types
635    #[test]
636    fn test_extension_errors() {
637        // Extensions can use the Extension variant with their own name
638        let spatial_ext = NitriteError::new("Spatial index error", ErrorKind::Extension("spatial".to_string()));
639        assert_eq!(spatial_ext.kind(), &ErrorKind::Extension("spatial".to_string()));
640        
641        let fulltext_ext = NitriteError::new("Full-text search error", ErrorKind::Extension("FullText".to_string()));
642        assert_eq!(fulltext_ext.kind(), &ErrorKind::Extension("FullText".to_string()));
643        
644        // Different extensions have different error kinds
645        assert_ne!(spatial_ext.kind(), fulltext_ext.kind());
646        
647        // Display should show the extension name
648        let display = format!("{}", ErrorKind::Extension("MyExtension".to_string()));
649        assert_eq!(display, "MyExtension error");
650    }
651
652    // Test error hierarchy and chaining
653    #[test]
654    fn test_error_chain_with_different_kinds() {
655        let root_cause = NitriteError::new("File not found", ErrorKind::FileNotFound);
656        let mid_level = NitriteError::new_with_cause(
657            "Failed to read store",
658            ErrorKind::IOError,
659            root_cause,
660        );
661        let top_level = NitriteError::new_with_cause(
662            "Cannot initialize database",
663            ErrorKind::BackendError,
664            mid_level,
665        );
666
667        assert_eq!(top_level.kind(), &ErrorKind::BackendError);
668        assert!(top_level.cause().is_some());
669
670        if let Some(cause_box) = top_level.cause() {
671            assert_eq!(cause_box.kind(), &ErrorKind::IOError);
672        }
673    }
674
675    // Test error comparison for all error kinds
676    #[test]
677    fn test_error_kind_equality() {
678        let error1 = NitriteError::new("Error 1", ErrorKind::IndexNotFound);
679        let error2 = NitriteError::new("Error 2", ErrorKind::IndexNotFound);
680        let error3 = NitriteError::new("Error 3", ErrorKind::IndexAlreadyExists);
681
682        assert_eq!(error1.kind(), error2.kind());
683        assert_ne!(error1.kind(), error3.kind());
684    }
685
686    // Test error message preservation across different error kinds
687    #[test]
688    fn test_error_message_preservation() {
689        let messages = vec![
690            ("Filter error message", ErrorKind::FilterError),
691            ("Index not found message", ErrorKind::IndexNotFound),
692            ("Disk full message", ErrorKind::DiskFull),
693            ("Object mapping error message", ErrorKind::ObjectMappingError),
694            ("Security error message", ErrorKind::SecurityError),
695        ];
696
697        for (msg, kind) in &messages {
698            let error = NitriteError::new(msg, kind.clone());
699            assert_eq!(error.message(), *msg);
700            assert_eq!(error.kind(), kind);
701        }
702    }
703
704    // Test From<std::io::Error>
705    #[test]
706    fn test_from_io_error_not_found() {
707        let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
708        let nitrite_err: NitriteError = io_err.into();
709        
710        assert_eq!(nitrite_err.kind(), &ErrorKind::FileNotFound);
711        assert!(nitrite_err.message().contains("IO error"));
712    }
713
714    #[test]
715    fn test_from_io_error_permission_denied() {
716        let io_err = std::io::Error::new(std::io::ErrorKind::PermissionDenied, "permission denied");
717        let nitrite_err: NitriteError = io_err.into();
718        
719        assert_eq!(nitrite_err.kind(), &ErrorKind::PermissionDenied);
720        assert!(nitrite_err.message().contains("IO error"));
721    }
722
723    #[test]
724    fn test_from_io_error_other() {
725        let io_err = std::io::Error::other("unknown io error");
726        let nitrite_err: NitriteError = io_err.into();
727        
728        assert_eq!(nitrite_err.kind(), &ErrorKind::IOError);
729        assert!(nitrite_err.message().contains("IO error"));
730    }
731
732    // Test From<std::string::FromUtf8Error>
733    #[test]
734    fn test_from_utf8_error() {
735        let invalid_utf8 = vec![0xFF, 0xFE];
736        let utf8_err = String::from_utf8(invalid_utf8).unwrap_err();
737        let nitrite_err: NitriteError = utf8_err.into();
738        
739        assert_eq!(nitrite_err.kind(), &ErrorKind::EncodingError);
740        assert!(nitrite_err.message().contains("UTF-8"));
741    }
742
743    // Test From<std::fmt::Error>
744    #[test]
745    fn test_from_fmt_error() {
746        use std::fmt::Write;
747        // Create a write that always fails
748        struct FailingWriter;
749        
750        impl Write for FailingWriter {
751            fn write_str(&mut self, _: &str) -> std::fmt::Result {
752                Err(std::fmt::Error)
753            }
754        }
755        
756        let fmt_err = write!(&mut FailingWriter, "test").unwrap_err();
757        let nitrite_err: NitriteError = fmt_err.into();
758        
759        assert_eq!(nitrite_err.kind(), &ErrorKind::InternalError);
760        assert!(nitrite_err.message().contains("Formatting"));
761    }
762
763    // Test From<std::num::ParseIntError>
764    #[test]
765    fn test_from_parse_int_error() {
766        let parse_err = "not_a_number".parse::<i32>().unwrap_err();
767        let nitrite_err: NitriteError = parse_err.into();
768        
769        assert_eq!(nitrite_err.kind(), &ErrorKind::InvalidDataType);
770        assert!(nitrite_err.message().contains("Integer parsing"));
771    }
772
773    // Test From<std::num::ParseFloatError>
774    #[test]
775    fn test_from_parse_float_error() {
776        let parse_err = "not_a_float".parse::<f64>().unwrap_err();
777        let nitrite_err: NitriteError = parse_err.into();
778        
779        assert_eq!(nitrite_err.kind(), &ErrorKind::InvalidDataType);
780        assert!(nitrite_err.message().contains("Float parsing"));
781    }
782
783    // Test From<String>
784    #[test]
785    fn test_from_string() {
786        let msg = String::from("test error message");
787        let nitrite_err: NitriteError = msg.into();
788        
789        assert_eq!(nitrite_err.kind(), &ErrorKind::InternalError);
790        assert_eq!(nitrite_err.message(), "test error message");
791    }
792
793    // Test From<&str>
794    #[test]
795    fn test_from_str() {
796        let msg = "test error message";
797        let nitrite_err: NitriteError = msg.into();
798        
799        assert_eq!(nitrite_err.kind(), &ErrorKind::InternalError);
800        assert_eq!(nitrite_err.message(), "test error message");
801    }
802
803    // Test chaining From conversions
804    #[test]
805    fn test_from_conversion_in_result_chain() {
806        fn operation_that_fails_with_io() -> NitriteResult<String> {
807            let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
808            Err(io_err.into())
809        }
810        
811        let result = operation_that_fails_with_io();
812        assert!(result.is_err());
813        
814        if let Err(err) = result {
815            assert_eq!(err.kind(), &ErrorKind::FileNotFound);
816        }
817    }
818
819    // Test multiple From conversions
820    #[test]
821    fn test_multiple_from_conversions() {
822        let io_err: NitriteError = std::io::Error::new(
823            std::io::ErrorKind::PermissionDenied,
824            "permission denied"
825        ).into();
826        assert_eq!(io_err.kind(), &ErrorKind::PermissionDenied);
827
828        let utf8_err: NitriteError = String::from_utf8(vec![0xFF]).unwrap_err().into();
829        assert_eq!(utf8_err.kind(), &ErrorKind::EncodingError);
830
831        let str_err: NitriteError = "string error".into();
832        assert_eq!(str_err.kind(), &ErrorKind::InternalError);
833    }
834
835    // Test ? operator with From trait
836    #[test]
837    fn test_question_mark_operator_with_from() {
838        fn parse_number_operation() -> NitriteResult<i32> {
839            let num: i32 = "12345".parse()?;
840            Ok(num)
841        }
842
843        let result = parse_number_operation();
844        assert!(result.is_ok());
845        assert_eq!(result.unwrap(), 12345);
846    }
847
848    #[test]
849    fn test_question_mark_operator_with_parse_error() {
850        fn parse_number_operation() -> NitriteResult<i32> {
851            let num: i32 = "not_a_number".parse()?;
852            Ok(num)
853        }
854
855        let result = parse_number_operation();
856        assert!(result.is_err());
857        
858        if let Err(err) = result {
859            assert_eq!(err.kind(), &ErrorKind::InvalidDataType);
860        }
861    }
862}