Skip to main content

oxigdal_offline/
error.rs

1//! Error types for offline data management
2
3use thiserror::Error;
4
5/// Result type for offline operations
6pub type Result<T> = core::result::Result<T, Error>;
7
8/// Errors that can occur during offline operations
9#[derive(Debug, Error)]
10pub enum Error {
11    /// Storage backend error
12    #[error("Storage error: {0}")]
13    Storage(String),
14
15    /// Sync queue error
16    #[error("Sync queue error: {0}")]
17    SyncQueue(String),
18
19    /// Conflict detected
20    #[error("Conflict detected: {0}")]
21    Conflict(String),
22
23    /// Merge error
24    #[error("Merge error: {0}")]
25    Merge(String),
26
27    /// Retry exhausted
28    #[error("Retry exhausted after {attempts} attempts: {message}")]
29    RetryExhausted {
30        /// Number of attempts made
31        attempts: usize,
32        /// Error message
33        message: String,
34    },
35
36    /// Network error
37    #[error("Network error: {0}")]
38    Network(String),
39
40    /// Serialization error
41    #[error("Serialization error: {0}")]
42    Serialization(String),
43
44    /// Deserialization error
45    #[error("Deserialization error: {0}")]
46    Deserialization(String),
47
48    /// Invalid operation
49    #[error("Invalid operation: {0}")]
50    InvalidOperation(String),
51
52    /// Record not found
53    #[error("Record not found: {0}")]
54    NotFound(String),
55
56    /// Version mismatch
57    #[error("Version mismatch: expected {expected}, got {actual}")]
58    VersionMismatch {
59        /// Expected version
60        expected: u64,
61        /// Actual version
62        actual: u64,
63    },
64
65    /// Capacity exceeded
66    #[error("Capacity exceeded: {0}")]
67    CapacityExceeded(String),
68
69    /// Configuration error
70    #[error("Configuration error: {0}")]
71    Config(String),
72
73    /// Database error
74    #[error("Database error: {0}")]
75    Database(String),
76
77    /// Lock error
78    #[error("Lock error: {0}")]
79    Lock(String),
80
81    /// Timeout error
82    #[error("Timeout error: {0}")]
83    Timeout(String),
84
85    /// Internal error
86    #[error("Internal error: {0}")]
87    Internal(String),
88}
89
90#[cfg(feature = "native")]
91impl From<rusqlite::Error> for Error {
92    fn from(err: rusqlite::Error) -> Self {
93        Error::Database(err.to_string())
94    }
95}
96
97#[cfg(feature = "wasm")]
98impl From<wasm_bindgen::JsValue> for Error {
99    fn from(err: wasm_bindgen::JsValue) -> Self {
100        Error::Storage(format!("WASM error: {err:?}"))
101    }
102}
103
104impl Error {
105    /// Create a storage error
106    pub fn storage(message: impl Into<String>) -> Self {
107        Self::Storage(message.into())
108    }
109
110    /// Create a sync queue error
111    pub fn sync_queue(message: impl Into<String>) -> Self {
112        Self::SyncQueue(message.into())
113    }
114
115    /// Create a conflict error
116    pub fn conflict(message: impl Into<String>) -> Self {
117        Self::Conflict(message.into())
118    }
119
120    /// Create a merge error
121    pub fn merge(message: impl Into<String>) -> Self {
122        Self::Merge(message.into())
123    }
124
125    /// Create a network error
126    pub fn network(message: impl Into<String>) -> Self {
127        Self::Network(message.into())
128    }
129
130    /// Create a serialization error
131    pub fn serialization(message: impl Into<String>) -> Self {
132        Self::Serialization(message.into())
133    }
134
135    /// Create a deserialization error
136    pub fn deserialization(message: impl Into<String>) -> Self {
137        Self::Deserialization(message.into())
138    }
139
140    /// Create an invalid operation error
141    pub fn invalid_operation(message: impl Into<String>) -> Self {
142        Self::InvalidOperation(message.into())
143    }
144
145    /// Create a not found error
146    pub fn not_found(message: impl Into<String>) -> Self {
147        Self::NotFound(message.into())
148    }
149
150    /// Create a version mismatch error
151    pub fn version_mismatch(expected: u64, actual: u64) -> Self {
152        Self::VersionMismatch { expected, actual }
153    }
154
155    /// Create a capacity exceeded error
156    pub fn capacity_exceeded(message: impl Into<String>) -> Self {
157        Self::CapacityExceeded(message.into())
158    }
159
160    /// Create a configuration error
161    pub fn config(message: impl Into<String>) -> Self {
162        Self::Config(message.into())
163    }
164
165    /// Create an internal error
166    pub fn internal(message: impl Into<String>) -> Self {
167        Self::Internal(message.into())
168    }
169
170    /// Create a not supported error
171    pub fn not_supported(message: impl Into<String>) -> Self {
172        Self::InvalidOperation(format!("Not supported: {}", message.into()))
173    }
174}
175
176#[cfg(test)]
177mod tests {
178    use super::*;
179
180    #[test]
181    fn test_error_display() {
182        let err = Error::Conflict("concurrent modification".to_string());
183        assert!(err.to_string().contains("Conflict detected"));
184        assert!(err.to_string().contains("concurrent modification"));
185    }
186
187    #[test]
188    fn test_version_mismatch() {
189        let err = Error::version_mismatch(5, 3);
190        assert!(matches!(
191            err,
192            Error::VersionMismatch {
193                expected: 5,
194                actual: 3
195            }
196        ));
197    }
198
199    #[test]
200    fn test_retry_exhausted() {
201        let err = Error::RetryExhausted {
202            attempts: 5,
203            message: "network timeout".to_string(),
204        };
205        assert!(err.to_string().contains("5 attempts"));
206    }
207}