1use crate::{
4 CryptoError, FileError, KeyError, OAuthError, StorageCheckError, StorageError, StoreLockError,
5 SyncError,
6};
7
8#[derive(Debug, thiserror::Error)]
10pub enum StorageSetupError {
11 #[error(transparent)]
13 Check(StorageCheckError),
14 #[error("storage location change is unsupported")]
16 LocationChangeUnsupported,
17 #[error(transparent)]
19 Storage(StorageError),
20 #[error(transparent)]
22 OAuth(#[from] OAuthError),
23 #[error("secure storage: {0}")]
25 SecureStorage(KeyError),
26 #[error("this device or its member was removed")]
28 Removed,
29 #[error("the store's owner must sign in with the account that holds it")]
33 StoreOwner,
34 #[error("an update is required: {0:?}")]
36 UpdateRequired(coven_format::RequiredUpdate),
37 #[error("storage setup was cancelled and undone")]
40 Cancelled,
41 #[error("a storage setup is still finishing")]
44 SetupRunning,
45 #[error("storage setup: {0}")]
47 Internal(#[source] Box<dyn std::error::Error + Send + Sync>),
48 #[error(transparent)]
51 Shutdown(#[from] coven_foundation::task::Shutdown),
52 #[error(transparent)]
55 Bug(#[from] coven_foundation::task::Bug),
56}
57
58impl coven_foundation::task::CarriesBug for StorageSetupError {
59 fn bug(&self) -> Option<&coven_foundation::task::Bug> {
60 match self {
61 Self::Bug(bug) => Some(bug),
62 Self::SecureStorage(error) => error.bug(),
63 Self::Storage(error) => error.bug(),
64 Self::Check(error) => error.bug(),
65 Self::OAuth(error) => error.bug(),
66 _ => None,
67 }
68 }
69}
70
71impl From<StorageError> for StorageSetupError {
72 fn from(error: StorageError) -> Self {
73 match error {
74 StorageError::Bug(bug) => Self::Bug(bug),
75 error => Self::Storage(error),
76 }
77 }
78}
79
80impl From<StorageCheckError> for StorageSetupError {
83 fn from(error: StorageCheckError) -> Self {
84 match error {
85 StorageCheckError::Storage(error) => Self::from(error),
86 error => Self::Check(error),
87 }
88 }
89}
90
91impl From<KeyError> for StorageSetupError {
92 fn from(error: KeyError) -> Self {
93 match error {
94 KeyError::Bug(bug) => Self::Bug(bug),
95 error => Self::SecureStorage(error),
96 }
97 }
98}
99
100impl From<coven_foundation::task::TaskFailed> for StorageSetupError {
101 fn from(failed: coven_foundation::task::TaskFailed) -> Self {
102 failed.into_error()
103 }
104}
105
106#[derive(Debug, thiserror::Error)]
108pub enum StoreCreationError {
109 #[error(transparent)]
111 Directory(coven_foundation::files::BootstrapDirectoryError),
112 #[error(transparent)]
114 Store(crate::CovenError),
115 #[error("{}: the device is locked, and the store's files are protected until it unlocks", path.display())]
119 DeviceLocked {
120 path: std::path::PathBuf,
122 },
123 #[error(transparent)]
125 Identity(crate::IdentityError),
126 #[error(transparent)]
128 Sync(SyncError),
129 #[error("{operation}; cleanup failed: {cleanup}")]
131 Cleanup {
132 operation: Box<StoreCreationError>,
134 #[source]
136 cleanup: Box<StoreCreationError>,
137 },
138 #[error("store {store} was published but its directory sync failed: {source}")]
140 PublishedButNotSynced {
141 store: crate::StoreId,
143 #[source]
145 source: Box<StoreCreationError>,
146 },
147 #[error(transparent)]
153 Shutdown(#[from] crate::task::Shutdown),
154 #[error(transparent)]
157 Bug(#[from] crate::task::Bug),
158}
159
160impl From<coven_foundation::files::BootstrapDirectoryError> for StoreCreationError {
161 fn from(error: coven_foundation::files::BootstrapDirectoryError) -> Self {
162 if let coven_foundation::files::BootstrapDirectoryError::Bug(bug) = error {
163 return Self::Bug(bug);
164 }
165 match error.device_locked() {
166 Some(path) => Self::DeviceLocked { path: path.to_owned() },
167 None => Self::Directory(error),
168 }
169 }
170}
171
172impl From<crate::CovenError> for StoreCreationError {
173 fn from(error: crate::CovenError) -> Self {
174 match error {
175 crate::CovenError::DeviceLocked { path } => Self::DeviceLocked { path },
176 crate::CovenError::Bug(bug) => Self::Bug(bug),
177 error => Self::Store(error),
178 }
179 }
180}
181
182impl From<coven_database::OpenError> for StoreCreationError {
184 fn from(error: coven_database::OpenError) -> Self {
185 crate::CovenError::from(error).into()
186 }
187}
188
189impl From<crate::IdentityError> for StoreCreationError {
190 fn from(error: crate::IdentityError) -> Self {
191 match error {
192 crate::IdentityError::Bug(bug) => Self::Bug(bug),
193 error => Self::Identity(error),
194 }
195 }
196}
197
198impl From<SyncError> for StoreCreationError {
199 fn from(error: SyncError) -> Self {
200 match error {
201 SyncError::Bug(bug) => Self::Bug(bug),
202 error => Self::Sync(error),
203 }
204 }
205}
206
207impl crate::task::CarriesBug for StoreCreationError {
208 fn bug(&self) -> Option<&crate::task::Bug> {
209 match self {
210 Self::Bug(bug) => Some(bug),
211 Self::Directory(error) => error.bug(),
212 Self::Store(error) => error.bug(),
213 Self::Identity(error) => error.bug(),
214 Self::Sync(error) => error.bug(),
215 Self::Cleanup { operation, cleanup } => operation.bug().or_else(|| cleanup.bug()),
216 Self::PublishedButNotSynced { source, .. } => source.bug(),
217 _ => None,
218 }
219 }
220}
221
222impl From<crate::task::TaskFailed> for StoreCreationError {
223 fn from(failed: crate::task::TaskFailed) -> Self {
224 failed.into_error()
225 }
226}
227
228impl StoreCreationError {
229 pub(crate) fn with_cleanup(self, cleanup: Self) -> Self {
230 Self::Cleanup { operation: Box::new(self), cleanup: Box::new(cleanup) }
231 }
232}
233
234#[derive(Debug, thiserror::Error)]
236pub enum StoreDeletionError {
237 #[error(transparent)]
239 Lock(StoreLockError),
240 #[error("{} writes and {} files wait to upload", .0.writes, .0.files)]
242 Unsent(coven_database::UnsentWork),
243 #[error(transparent)]
247 Sync(crate::SyncError),
248 #[error("sign in to {0:?} to delete the store from this device")]
254 SignInRequired(crate::CloudProvider),
255 #[error("the store is being deleted from this device; finishing it failed: {0}")]
260 Unfinished(#[source] crate::SyncError),
261 #[error("closing the store failed after this device left it: {0}")]
266 Closing(#[source] crate::SyncError),
267 #[error(transparent)]
269 Open(crate::CovenError),
270 #[error("no deletion of store {0} is under way")]
273 NoUnfinishedDeletion(crate::StoreId),
274 #[error(transparent)]
276 Key(KeyError),
277 #[error(transparent)]
279 File(FileError),
280 #[error(transparent)]
283 Shutdown(#[from] crate::task::Shutdown),
284 #[error(transparent)]
287 Bug(#[from] crate::task::Bug),
288}
289
290impl crate::task::CarriesBug for StoreDeletionError {
291 fn bug(&self) -> Option<&crate::task::Bug> {
292 match self {
293 Self::Bug(bug) => Some(bug),
294 Self::Lock(error) => error.bug(),
295 Self::Sync(error) | Self::Closing(error) | Self::Unfinished(error) => error.bug(),
296 Self::Open(error) => error.bug(),
297 Self::Key(error) => error.bug(),
298 Self::File(error) => error.bug(),
299 _ => None,
300 }
301 }
302}
303
304impl From<crate::SyncError> for StoreDeletionError {
305 fn from(error: crate::SyncError) -> Self {
306 match error {
307 crate::SyncError::Bug(bug) => Self::Bug(bug),
308 error => Self::Sync(error),
309 }
310 }
311}
312
313impl From<KeyError> for StoreDeletionError {
314 fn from(error: KeyError) -> Self {
315 match error {
316 KeyError::Bug(bug) => Self::Bug(bug),
317 error => Self::Key(error),
318 }
319 }
320}
321
322impl From<FileError> for StoreDeletionError {
323 fn from(error: FileError) -> Self {
324 match error {
325 FileError::Bug(bug) => Self::Bug(bug),
326 error => Self::File(error),
327 }
328 }
329}
330
331impl From<StoreLockError> for StoreDeletionError {
332 fn from(error: StoreLockError) -> Self {
333 match error {
334 StoreLockError::Bug(bug) => Self::Bug(bug),
335 error => Self::Lock(error),
336 }
337 }
338}
339
340impl From<crate::task::TaskFailed> for StoreDeletionError {
341 fn from(failed: crate::task::TaskFailed) -> Self {
342 failed.into_error()
343 }
344}
345
346#[derive(Debug, thiserror::Error)]
348pub enum ReadOnlyOpenError {
349 #[error(transparent)]
351 Local(crate::CovenError),
352 #[error(transparent)]
354 Storage(crate::SyncError),
355 #[error(transparent)]
357 Bug(#[from] crate::task::Bug),
358}
359
360impl From<crate::CovenError> for ReadOnlyOpenError {
361 fn from(error: crate::CovenError) -> Self {
362 match error {
363 crate::CovenError::Bug(bug) => Self::Bug(bug),
364 error => Self::Local(error),
365 }
366 }
367}
368
369impl From<crate::DbError> for ReadOnlyOpenError {
371 fn from(error: crate::DbError) -> Self {
372 crate::CovenError::from(error).into()
373 }
374}
375
376impl From<coven_database::OpenError> for ReadOnlyOpenError {
377 fn from(error: coven_database::OpenError) -> Self {
378 crate::CovenError::from(error).into()
379 }
380}
381
382impl From<crate::SyncError> for ReadOnlyOpenError {
383 fn from(error: crate::SyncError) -> Self {
384 match error {
385 crate::SyncError::Bug(bug) => Self::Bug(bug),
386 error => Self::Storage(error),
387 }
388 }
389}
390
391impl crate::task::CarriesBug for ReadOnlyOpenError {
392 fn bug(&self) -> Option<&crate::task::Bug> {
393 match self {
394 Self::Bug(bug) => Some(bug),
395 Self::Local(error) => error.bug(),
396 Self::Storage(error) => error.bug(),
397 }
398 }
399}
400
401#[derive(Debug, thiserror::Error)]
403pub enum StoreKeyUnlockError {
404 #[error("no storage configured")]
406 NoStorage,
407 #[error(transparent)]
409 Storage(StorageError),
410 #[error(transparent)]
416 Crypto(CryptoError),
417 #[error(transparent)]
419 SecureStorage(KeyError),
420 #[error("this device or its member was removed")]
422 Removed,
423 #[error("an update is required: {0:?}")]
425 UpdateRequired(crate::RequiredUpdate),
426 #[error("opening store keys: {0}")]
428 Other(#[source] Box<dyn std::error::Error + Send + Sync>),
429 #[error(transparent)]
432 Shutdown(#[from] crate::task::Shutdown),
433 #[error(transparent)]
436 Bug(#[from] crate::task::Bug),
437}
438
439impl crate::task::CarriesBug for StoreKeyUnlockError {
440 fn bug(&self) -> Option<&crate::task::Bug> {
441 match self {
442 Self::Bug(bug) => Some(bug),
443 Self::Storage(error) => error.bug(),
444 Self::SecureStorage(error) => error.bug(),
445 _ => None,
446 }
447 }
448}
449
450impl From<StorageError> for StoreKeyUnlockError {
451 fn from(error: StorageError) -> Self {
452 match error {
453 StorageError::Bug(bug) => Self::Bug(bug),
454 error => Self::Storage(error),
455 }
456 }
457}
458
459impl From<KeyError> for StoreKeyUnlockError {
460 fn from(error: KeyError) -> Self {
461 match error {
462 KeyError::Bug(bug) => Self::Bug(bug),
463 error => Self::SecureStorage(error),
464 }
465 }
466}
467
468impl From<crate::task::TaskFailed> for StoreKeyUnlockError {
469 fn from(failed: crate::task::TaskFailed) -> Self {
470 failed.into_error()
471 }
472}
473
474enum Stopped {
477 Removed,
478 UpdateRequired(crate::RequiredUpdate),
479}
480
481fn stopped(error: &SyncError) -> Option<Stopped> {
482 let reason = match error {
483 SyncError::Removed | SyncError::NotAdmitted => return Some(Stopped::Removed),
486 SyncError::UpdateRequired(update) => return Some(Stopped::UpdateRequired(*update)),
487 SyncError::Pending(record) => &record.reason,
488 _ => return None,
489 };
490 match reason {
491 crate::PendingReason::Removed | crate::PendingReason::NotAdmitted => Some(Stopped::Removed),
492 crate::PendingReason::UpdateRequired(update) => Some(Stopped::UpdateRequired(*update)),
493 _ => None,
494 }
495}
496
497pub(crate) fn setup_error(error: SyncError) -> StorageSetupError {
498 match stopped(&error) {
499 Some(Stopped::Removed) => return StorageSetupError::Removed,
500 Some(Stopped::UpdateRequired(update)) => return StorageSetupError::UpdateRequired(update),
501 None => {}
502 }
503 match error {
504 SyncError::SecureStorage(error) => error.into(),
505 SyncError::Storage(error) => error.into(),
506 SyncError::StoreOwner => StorageSetupError::StoreOwner,
507 SyncError::Shutdown(shutdown) => StorageSetupError::Shutdown(shutdown),
508 SyncError::Bug(bug) => StorageSetupError::Bug(bug),
509 error => StorageSetupError::Internal(Box::new(error)),
510 }
511}
512
513pub(crate) fn unlock_error(error: SyncError) -> StoreKeyUnlockError {
514 match stopped(&error) {
515 Some(Stopped::Removed) => return StoreKeyUnlockError::Removed,
516 Some(Stopped::UpdateRequired(update)) => {
517 return StoreKeyUnlockError::UpdateRequired(update)
518 }
519 None => {}
520 }
521 match error {
522 SyncError::NoStorage => StoreKeyUnlockError::NoStorage,
523 SyncError::SecureStorage(error) => error.into(),
524 SyncError::Crypto(error) => StoreKeyUnlockError::Crypto(error),
525 SyncError::Storage(error) => error.into(),
526 SyncError::Shutdown(shutdown) => StoreKeyUnlockError::Shutdown(shutdown),
527 SyncError::Bug(bug) => StoreKeyUnlockError::Bug(bug),
528 error => StoreKeyUnlockError::Other(Box::new(error)),
529 }
530}
531
532#[cfg(test)]
533#[path = "error_tests.rs"]
534mod tests;