1use crate::task::{Bug, CarriesBug, Shutdown, TaskFailed};
5use crate::{
6 CovenMigrationError, DbError, FileError, KeyError, MigrationError, NetworkWatchError,
7 SettingsError, SqliteError, StoreId, StoreLockError, SyncError, WriteFailure,
8};
9use coven_database::OpenError;
10
11pub type CovenResult<T> = Result<T, CovenError>;
13
14#[derive(Debug, thiserror::Error)]
16pub enum CovenError {
17 #[error(transparent)]
19 Key(KeyError),
20 #[error("{operation}; rollback failed: {rollback}")]
23 Rollback {
24 operation: Box<CovenError>,
26 #[source]
28 rollback: SqliteError,
29 },
30 #[error("file cleanup failed: {failures:?}; write failed: {write}")]
32 FileCleanup {
33 write: Box<CovenError>,
35 failures: Vec<DbError>,
37 },
38 #[error(transparent)]
40 Database(#[from] DbError),
41 #[error(transparent)]
44 Sync(SyncError),
45 #[error(transparent)]
47 Migration(#[from] MigrationError),
48 #[error(transparent)]
50 CovenMigration(#[from] CovenMigrationError),
51 #[error(transparent)]
53 Settings(SettingsError),
54 #[error(transparent)]
56 File(FileError),
57 #[error(transparent)]
59 Lock(StoreLockError),
60 #[error("opening requires {field}")]
62 MissingConfiguration {
63 field: &'static str,
65 },
66 #[error("store {store}: this device's keychain entry is missing")]
69 DeviceKeyMissing {
70 store: StoreId,
72 },
73 #[error("store {store}: this device's entry and member keys are missing")]
77 MemberKeysMissing {
78 store: StoreId,
80 },
81 #[error("{}: the device is locked, and the store's files are protected until it unlocks", path.display())]
86 DeviceLocked {
87 path: std::path::PathBuf,
89 },
90 #[error(transparent)]
95 Shutdown(#[from] Shutdown),
96 #[error(transparent)]
100 Bug(#[from] Bug),
101 #[error(transparent)]
104 NetworkWatch(#[from] NetworkWatchError),
105}
106
107impl CarriesBug for CovenError {
108 fn bug(&self) -> Option<&Bug> {
109 match self {
110 Self::Bug(bug) => Some(bug),
111 Self::Database(error) => error.bug(),
112 Self::Sync(error) => error.bug(),
113 Self::Key(error) => error.bug(),
114 Self::Lock(error) => error.bug(),
115 Self::File(error) => error.bug(),
116 Self::Settings(error) => error.bug(),
117 Self::Rollback { operation, .. } => operation.bug(),
118 Self::FileCleanup { write, failures } => {
119 write.bug().or_else(|| failures.iter().find_map(DbError::bug))
120 }
121 Self::Migration(MigrationError::Failed { source, .. })
122 | Self::CovenMigration(CovenMigrationError::Failed { source }) => source.bug(),
123 _ => None,
124 }
125 }
126}
127
128impl From<KeyError> for CovenError {
129 fn from(error: KeyError) -> Self {
130 match error {
131 KeyError::Bug(bug) => Self::Bug(bug),
132 error => Self::Key(error),
133 }
134 }
135}
136
137impl From<SettingsError> for CovenError {
138 fn from(error: SettingsError) -> Self {
139 match error {
140 SettingsError::Bug(bug) => Self::Bug(bug),
141 error => Self::Settings(error),
142 }
143 }
144}
145
146impl From<FileError> for CovenError {
147 fn from(error: FileError) -> Self {
148 match error {
149 FileError::Bug(bug) => Self::Bug(bug),
150 error => Self::File(error),
151 }
152 }
153}
154
155impl From<StoreLockError> for CovenError {
156 fn from(error: StoreLockError) -> Self {
157 match error {
158 StoreLockError::Bug(bug) => Self::Bug(bug),
159 error => Self::Lock(error),
160 }
161 }
162}
163
164impl From<TaskFailed> for CovenError {
165 fn from(failed: TaskFailed) -> Self {
166 failed.into_error()
167 }
168}
169
170impl From<SqliteError> for CovenError {
171 fn from(error: SqliteError) -> Self {
172 Self::Database(error.into())
173 }
174}
175
176impl From<OpenError> for CovenError {
179 fn from(error: OpenError) -> Self {
180 match error {
181 OpenError::Database(error) => Self::Database(error),
182 OpenError::Migration(error) => Self::Migration(error),
183 OpenError::CovenMigration(error) => Self::CovenMigration(error),
184 OpenError::Settings(error) => Self::from(error),
185 OpenError::Lock(error) => Self::Lock(error),
186 OpenError::MissingConfiguration { field } => Self::MissingConfiguration { field },
187 OpenError::Bug(bug) => Self::Bug(bug),
188 }
189 }
190}
191
192impl From<SyncError> for CovenError {
196 fn from(error: SyncError) -> Self {
197 match error {
198 SyncError::Database(error) => Self::Database(error),
199 SyncError::Shutdown(shutdown) => Self::Shutdown(shutdown),
200 SyncError::Bug(bug) => Self::Bug(bug),
201 error => Self::Sync(error),
202 }
203 }
204}
205
206impl WriteFailure for CovenError {
210 fn with_rollback(self, rollback: SqliteError) -> Self {
211 match self {
212 Self::Database(error) => Self::Database(error.with_rollback(rollback)),
213 Self::Sync(error) => Self::Sync(error.with_rollback(rollback)),
214 error => Self::Rollback { operation: Box::new(error), rollback },
215 }
216 }
217 fn with_cleanup(write: Self, failures: Vec<DbError>) -> Self {
218 match write {
219 Self::Database(error) => Self::Database(DbError::with_cleanup(error, failures)),
220 Self::Sync(error) => Self::Sync(SyncError::with_cleanup(error, failures)),
221 error => Self::FileCleanup { write: Box::new(error), failures },
222 }
223 }
224}
225
226#[cfg(test)]
227#[path = "coven_error_tests.rs"]
228mod tests;