1use sos_core::{AuthenticationError, Origin};
3use sos_core::{ErrorExt, VaultId};
4use sos_protocol::{transfer::CancelReason, AsConflict, ConflictError};
5use std::error::Error as StdError;
6use std::path::PathBuf;
7use thiserror::Error;
8
9#[derive(Debug, Error)]
11pub enum Error {
12 #[error("path {0} is not a directory")]
14 NotDirectory(PathBuf),
15
16 #[error("path {0} is not a file")]
18 NotFile(PathBuf),
19
20 #[error("file {0} already exists")]
22 FileExists(PathBuf),
23
24 #[error("folder {0} already exists")]
26 FolderExists(VaultId),
27
28 #[error("method did not return a value")]
31 NoReturnValue,
32
33 #[error("origin '{0}' not found")]
35 OriginNotFound(Origin),
36
37 #[error("cannot revoke access to this device")]
39 RevokeDeviceSelf,
40
41 #[error("failed to sync after revoking device, {0}")]
43 RevokeDeviceSync(Box<Error>),
44
45 #[error("failed to force update, {0}")]
47 ForceUpdate(Box<Error>),
48
49 #[deprecated]
51 #[error("invalid share url for device enrollment")]
52 InvalidShareUrl,
53
54 #[error(transparent)]
56 Conflict(#[from] ConflictError),
57
58 #[error(transparent)]
60 ParseInt(#[from] std::num::ParseIntError),
61
62 #[error(transparent)]
64 Io(#[from] std::io::Error),
65
66 #[error(transparent)]
68 Json(#[from] serde_json::Error),
69
70 #[error(transparent)]
72 TryFromSlice(#[from] std::array::TryFromSliceError),
73
74 #[error(transparent)]
76 Core(#[from] sos_core::Error),
77
78 #[error(transparent)]
80 Authentication(#[from] sos_core::AuthenticationError),
81
82 #[error(transparent)]
84 Backend(#[from] sos_backend::Error),
85
86 #[cfg(feature = "search")]
88 #[error(transparent)]
89 Search(#[from] sos_search::Error),
90
91 #[error(transparent)]
93 Vault(#[from] sos_vault::Error),
94
95 #[error(transparent)]
97 Signer(#[from] sos_signer::Error),
98
99 #[error(transparent)]
101 Sync(#[from] sos_sync::Error),
102
103 #[error(transparent)]
105 Storage(#[from] sos_client_storage::Error),
106
107 #[error(transparent)]
109 Password(#[from] sos_password::Error),
110
111 #[error(transparent)]
113 Login(#[from] sos_login::Error),
114
115 #[error(transparent)]
117 BackendStorage(#[from] sos_backend::StorageError),
118
119 #[error(transparent)]
121 Database(#[from] sos_database::Error),
122
123 #[error(transparent)]
125 Account(#[from] sos_account::Error),
126
127 #[error(transparent)]
129 UrlParse(#[from] url::ParseError),
130
131 #[error(transparent)]
133 Utf8(#[from] std::str::Utf8Error),
134
135 #[error(transparent)]
137 HttpStatus(#[from] http::status::InvalidStatusCode),
138
139 #[error(transparent)]
141 Uuid(#[from] uuid::Error),
142
143 #[error(transparent)]
145 Hex(#[from] hex::FromHexError),
146
147 #[error(transparent)]
149 Protocol(#[from] sos_protocol::Error),
150
151 #[error(transparent)]
153 RemoteSync(#[from] sos_remote_sync::Error),
154
155 #[error(transparent)]
157 #[cfg(feature = "migrate")]
158 Migrate(#[from] sos_migrate::Error),
159
160 #[error(transparent)]
162 Network(#[from] sos_protocol::NetworkError),
163}
164
165impl ErrorExt for Error {
166 fn is_secret_not_found(&self) -> bool {
167 matches!(
168 self,
169 Error::Account(sos_account::Error::Storage(
170 sos_client_storage::Error::SecretNotFound(_)
171 ))
172 )
173 }
174
175 fn is_forbidden(&self) -> bool {
176 matches!(
177 self,
178 Error::Account(sos_account::Error::Authentication(
179 AuthenticationError::NotAuthenticated
180 )) | Error::Account(sos_account::Error::Storage(
181 sos_client_storage::Error::Authentication(
182 AuthenticationError::NotAuthenticated
183 )
184 ))
185 )
186 }
187
188 fn is_permission_denied(&self) -> bool {
189 matches!(
190 self,
191 Error::Vault(sos_vault::Error::Authentication(
192 AuthenticationError::PasswordVerification
193 ))
194 )
195 }
196}
197
198impl Error {
199 pub fn cancellation_reason(&self) -> Option<&CancelReason> {
202 let source = source_error(self);
203 if let Some(err) = source.downcast_ref::<Error>() {
204 if let Error::Protocol(sos_protocol::Error::TransferCanceled(
205 reason,
206 )) = err
207 {
208 Some(reason)
209 } else {
210 None
211 }
212 } else {
213 None
214 }
215 }
216}
217
218pub(crate) fn source_error<'a>(
219 error: &'a (dyn StdError + 'static),
220) -> &'a (dyn StdError + 'static) {
221 let mut source = error;
222 while let Some(next_source) = source.source() {
223 source = next_source;
224 }
225 source
226}
227
228impl AsConflict for Error {
229 fn is_conflict(&self) -> bool {
230 matches!(self, Error::Conflict(_))
231 }
232
233 fn is_hard_conflict(&self) -> bool {
234 matches!(self, Error::Conflict(ConflictError::Hard))
235 }
236
237 fn take_conflict(self) -> Option<ConflictError> {
238 match self {
239 Self::Conflict(err) => Some(err),
240 _ => None,
241 }
242 }
243}