Development Documentation (main branch) - For stable release docs, see docs.rs/eidetica
Skip to main content

eidetica/sync/
error.rs

1//! Error types for the synchronization module.
2
3use thiserror::Error;
4
5use super::peer_types::Address;
6use crate::{auth::Permission, entry::ID};
7
8/// Errors that can occur during synchronization operations.
9#[derive(Debug, Error)]
10#[non_exhaustive]
11pub enum SyncError {
12    /// No transport has been enabled for network operations.
13    #[error("No transport enabled. Call enable_http_transport() first")]
14    NoTransportEnabled,
15
16    /// Sync has not been enabled on the Instance.
17    #[error("Sync is not enabled on this Instance. Call Instance::enable_sync() first")]
18    SyncNotEnabled,
19
20    /// Attempted to start a server when one is already running.
21    #[error("Server already running on {address}")]
22    ServerAlreadyRunning { address: String },
23
24    /// Attempted to stop a server when none is running.
25    #[error("Server not running")]
26    ServerNotRunning,
27
28    /// Unexpected response type received from peer.
29    #[error("Unexpected response type: expected {expected}, got {actual}")]
30    UnexpectedResponse {
31        expected: &'static str,
32        actual: String,
33    },
34
35    /// Network communication error.
36    #[error("Network error: {0}")]
37    Network(String),
38
39    /// Command channel send error.
40    #[error("Failed to send command to background sync: {0}")]
41    CommandSendError(String),
42
43    /// Transport initialization error.
44    #[error("Failed to initialize transport: {0}")]
45    TransportInit(String),
46
47    /// Runtime creation error for async operations.
48    #[error("Failed to create async runtime: {0}")]
49    RuntimeCreation(String),
50
51    /// Server bind error.
52    #[error("Failed to bind server to {address}: {reason}")]
53    ServerBind { address: String, reason: String },
54
55    /// Client connection error.
56    #[error("Failed to connect to {address}: {reason}")]
57    ConnectionFailed { address: String, reason: String },
58
59    /// Device key not found in backend storage.
60    #[error("Device key '{key_name}' not found in backend storage")]
61    DeviceKeyNotFound { key_name: String },
62
63    /// Transport type not supported by this transport implementation.
64    #[error("Transport type '{transport_type}' not supported")]
65    UnsupportedTransport { transport_type: String },
66
67    /// Invalid address format.
68    #[error("Invalid address: {0}")]
69    InvalidAddress(String),
70
71    /// Peer not found.
72    #[error("Peer not found: {0}")]
73    PeerNotFound(String),
74
75    /// Peer already exists.
76    #[error("Peer already exists: {0}")]
77    PeerAlreadyExists(String),
78
79    /// Serialization error.
80    #[error("Serialization error: {0}")]
81    SerializationError(String),
82
83    /// Protocol version mismatch.
84    #[error("Protocol version mismatch: expected {expected}, received {received}")]
85    ProtocolMismatch { expected: u32, received: u32 },
86
87    /// Handshake failed.
88    #[error("Handshake failed: {0}")]
89    HandshakeFailed(String),
90
91    /// Entry not found in backend storage.
92    #[error("Entry not found: {0}")]
93    EntryNotFound(ID),
94
95    /// Invalid entry received (validation failed).
96    #[error("Invalid entry: {0}")]
97    InvalidEntry(String),
98
99    /// Sync protocol error.
100    #[error("Sync protocol error: {0}")]
101    SyncProtocolError(String),
102
103    /// Backend storage error.
104    #[error("Backend error: {0}")]
105    BackendError(String),
106
107    /// Bootstrap request not found.
108    #[error("Bootstrap request not found: {0}")]
109    RequestNotFound(String),
110
111    /// Bootstrap request already exists.
112    #[error("Bootstrap request already exists: {0}")]
113    RequestAlreadyExists(String),
114
115    /// Invalid bootstrap request state.
116    #[error(
117        "Invalid request state for '{request_id}': expected {expected_status}, found {current_status}"
118    )]
119    InvalidRequestState {
120        request_id: String,
121        current_status: String,
122        expected_status: String,
123    },
124
125    /// Invalid data format in stored bootstrap request.
126    #[error("Invalid data: {0}")]
127    InvalidData(String),
128
129    /// Insufficient permission for the requested operation.
130    #[error(
131        "Insufficient permission for request '{request_id}': required {required_permission}, but key has {actual_permission:?}"
132    )]
133    InsufficientPermission {
134        request_id: String,
135        required_permission: String,
136        actual_permission: Permission,
137    },
138
139    /// A request that would be served data carried no proof of key possession.
140    #[error("Authentication required to read database '{0}'")]
141    AuthenticationRequired(String),
142
143    /// The proof of key possession did not hold up.
144    #[error("Authentication failed: {0}")]
145    AuthenticationFailed(String),
146
147    /// The caller proved its key, but that key has no read access.
148    #[error("Permission denied: {0}")]
149    PermissionDenied(String),
150
151    /// Invalid public key provided.
152    #[error("Invalid public key: {reason}")]
153    InvalidPublicKey { reason: String },
154
155    /// Invalid key name provided.
156    #[error("Invalid key name: {reason}")]
157    InvalidKeyName { reason: String },
158
159    /// Instance has been dropped and is no longer available.
160    #[error("Instance has been dropped")]
161    InstanceDropped,
162
163    /// Bootstrap request is pending manual approval.
164    #[error("Bootstrap request pending approval (request_id: {request_id}): {message}")]
165    BootstrapPending { request_id: String, message: String },
166
167    /// Transport configuration type mismatch.
168    #[error("Transport config type mismatch for '{name}': expected '{expected}', found '{found}'")]
169    TransportTypeMismatch {
170        name: String,
171        expected: String,
172        found: String,
173    },
174
175    /// Transport not found by name.
176    #[error("Transport not found: {name}")]
177    TransportNotFound { name: String },
178
179    /// No transport can handle the given address.
180    #[error("No transport can handle address: {address:?}")]
181    NoTransportForAddress { address: Address },
182
183    /// Multiple transport operations failed.
184    #[error("Multiple transport errors: {}", errors.join(", "))]
185    MultipleTransportErrors { errors: Vec<String> },
186}
187
188impl SyncError {
189    /// Check if this is a configuration error: the sync stack isn't ready
190    /// (sync not attached or no transport registered).
191    pub fn is_configuration_error(&self) -> bool {
192        matches!(
193            self,
194            SyncError::NoTransportEnabled | SyncError::SyncNotEnabled
195        )
196    }
197
198    /// Check if this is a server lifecycle error.
199    pub fn is_server_error(&self) -> bool {
200        matches!(
201            self,
202            SyncError::ServerAlreadyRunning { .. }
203                | SyncError::ServerNotRunning
204                | SyncError::ServerBind { .. }
205        )
206    }
207
208    /// Check if this is a network/connection error.
209    pub fn is_network_error(&self) -> bool {
210        matches!(
211            self,
212            SyncError::Network(_) | SyncError::ConnectionFailed { .. }
213        )
214    }
215
216    /// Check if this is a protocol error (unexpected response).
217    pub fn is_protocol_error(&self) -> bool {
218        matches!(self, SyncError::UnexpectedResponse { .. })
219    }
220
221    /// Check if this is a not found error.
222    pub fn is_not_found(&self) -> bool {
223        matches!(
224            self,
225            SyncError::PeerNotFound(_) | SyncError::EntryNotFound(_)
226        )
227    }
228
229    /// Check if this is a validation error.
230    pub fn is_validation_error(&self) -> bool {
231        matches!(
232            self,
233            SyncError::InvalidEntry(_)
234                | SyncError::InvalidPublicKey { .. }
235                | SyncError::InvalidKeyName { .. }
236        )
237    }
238
239    /// Check if this is a backend error.
240    pub fn is_backend_error(&self) -> bool {
241        matches!(self, SyncError::BackendError(_))
242    }
243}