1use thiserror::Error;
4
5pub type Result<T> = core::result::Result<T, Error>;
7
8#[derive(Debug, Error)]
10pub enum Error {
11 #[error("Storage error: {0}")]
13 Storage(String),
14
15 #[error("Sync queue error: {0}")]
17 SyncQueue(String),
18
19 #[error("Conflict detected: {0}")]
21 Conflict(String),
22
23 #[error("Merge error: {0}")]
25 Merge(String),
26
27 #[error("Retry exhausted after {attempts} attempts: {message}")]
29 RetryExhausted {
30 attempts: usize,
32 message: String,
34 },
35
36 #[error("Network error: {0}")]
38 Network(String),
39
40 #[error("Serialization error: {0}")]
42 Serialization(String),
43
44 #[error("Deserialization error: {0}")]
46 Deserialization(String),
47
48 #[error("Invalid operation: {0}")]
50 InvalidOperation(String),
51
52 #[error("Record not found: {0}")]
54 NotFound(String),
55
56 #[error("Version mismatch: expected {expected}, got {actual}")]
58 VersionMismatch {
59 expected: u64,
61 actual: u64,
63 },
64
65 #[error("Capacity exceeded: {0}")]
67 CapacityExceeded(String),
68
69 #[error("Configuration error: {0}")]
71 Config(String),
72
73 #[error("Database error: {0}")]
75 Database(String),
76
77 #[error("Lock error: {0}")]
79 Lock(String),
80
81 #[error("Timeout error: {0}")]
83 Timeout(String),
84
85 #[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 pub fn storage(message: impl Into<String>) -> Self {
107 Self::Storage(message.into())
108 }
109
110 pub fn sync_queue(message: impl Into<String>) -> Self {
112 Self::SyncQueue(message.into())
113 }
114
115 pub fn conflict(message: impl Into<String>) -> Self {
117 Self::Conflict(message.into())
118 }
119
120 pub fn merge(message: impl Into<String>) -> Self {
122 Self::Merge(message.into())
123 }
124
125 pub fn network(message: impl Into<String>) -> Self {
127 Self::Network(message.into())
128 }
129
130 pub fn serialization(message: impl Into<String>) -> Self {
132 Self::Serialization(message.into())
133 }
134
135 pub fn deserialization(message: impl Into<String>) -> Self {
137 Self::Deserialization(message.into())
138 }
139
140 pub fn invalid_operation(message: impl Into<String>) -> Self {
142 Self::InvalidOperation(message.into())
143 }
144
145 pub fn not_found(message: impl Into<String>) -> Self {
147 Self::NotFound(message.into())
148 }
149
150 pub fn version_mismatch(expected: u64, actual: u64) -> Self {
152 Self::VersionMismatch { expected, actual }
153 }
154
155 pub fn capacity_exceeded(message: impl Into<String>) -> Self {
157 Self::CapacityExceeded(message.into())
158 }
159
160 pub fn config(message: impl Into<String>) -> Self {
162 Self::Config(message.into())
163 }
164
165 pub fn internal(message: impl Into<String>) -> Self {
167 Self::Internal(message.into())
168 }
169
170 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}