1use std::backtrace::Backtrace;
2use std::fmt::Display;
3use std::fmt::{self, Formatter};
4use std::io;
5use std::panic::Location;
6use std::sync::PoisonError;
7
8use hmac::crypto_mac::MacError;
9use serde::ser::SerializeStruct;
10use serde::{Serialize, Serializer};
11use uuid::Uuid;
12
13use crate::io::network::ApiError;
14use crate::model::ValidationFailure;
15
16use super::api;
17
18pub type LbResult<T> = Result<T, LbErr>;
19
20#[derive(Debug)]
21pub struct LbErr {
22 pub kind: LbErrKind,
23 pub backtrace: Option<Backtrace>,
24}
25
26impl Display for LbErr {
30 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
31 write!(f, "{}", self.kind)
32 }
33}
34
35impl Display for LbErrKind {
42 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
43 match self {
44 LbErrKind::AccountExists => write!(f, "An account already exists"),
45 LbErrKind::AccountNonexistent => write!(f, "You need an account to do that"),
46 LbErrKind::AccountStringCorrupted => write!(f, "That account key is invalid"),
47 LbErrKind::AlreadyCanceled => write!(f, "Your subscription has already been cancelled"),
48 LbErrKind::AlreadyPremium => write!(f, "Your account is already premium"),
49 LbErrKind::AppStoreAccountAlreadyLinked => {
50 write!(f, "Your account is already linked to the App Store")
51 }
52 LbErrKind::CannotCancelSubscriptionForAppStore => {
53 write!(f, "You cannot cancel an app store subscription from here")
54 }
55 LbErrKind::CardDecline => write!(f, "Your card was declined"),
56 LbErrKind::CardExpired => write!(f, "Your card is expired"),
57 LbErrKind::CardInsufficientFunds => write!(f, "This card has insufficient funds"),
58 LbErrKind::CardInvalidCvc => write!(f, "Your CVC is invalid"),
59 LbErrKind::CardInvalidExpMonth => write!(f, "Your expiration month is invalid"),
60 LbErrKind::CardInvalidExpYear => write!(f, "Your expiration year is invalid"),
61 LbErrKind::CardInvalidNumber => write!(f, "Your card number is invalid"),
62 LbErrKind::CardNotSupported => write!(f, "That card is not supported by stripe"),
63 LbErrKind::ClientUpdateRequired => {
64 write!(f, "You must update your Lockbook to do that")
65 }
66 LbErrKind::CurrentUsageIsMoreThanNewTier => {
67 write!(f, "You need to delete some files before downgrading your usage")
68 }
69 LbErrKind::DiskPathInvalid => write!(f, "That disk path is invalid"),
70 LbErrKind::DiskPathTaken => write!(f, "That disk path is not available"),
71 LbErrKind::DrawingInvalid => write!(f, "That drawing is invalid"),
72 LbErrKind::ExistingRequestPending => {
73 write!(f, "Existing billing request in progress, please wait and try again")
74 }
75 LbErrKind::FileNameContainsSlash => write!(f, "A file name cannot contain slashes"),
76 LbErrKind::FileNameTooLong => write!(f, "That file name is too long"),
77 LbErrKind::FileNameEmpty => write!(f, "A file name cannot be empty"),
78 LbErrKind::FileNonexistent => write!(f, "That file does not exist"),
79 LbErrKind::FileNotDocument => write!(f, "That file is not a document"),
80 LbErrKind::FileParentNonexistent => write!(f, "Could not find that file parent"),
81 LbErrKind::InsufficientPermission => {
82 write!(f, "You don't have the permission to do that")
83 }
84 LbErrKind::InvalidPurchaseToken => write!(f, "That purchase token is invalid"),
85 LbErrKind::InvalidAuthDetails => {
86 write!(f, "Our server failed to authenticate your request, please try again")
87 }
88 LbErrKind::KeyPhraseInvalid => {
89 write!(f, "Your private key phrase is wrong")
90 }
91 LbErrKind::NotPremium => write!(f, "You do not have a premium subscription"),
92 LbErrKind::UsageIsOverDataCap => {
93 write!(f, "You're out of space")
94 }
95 LbErrKind::UsageIsOverFreeTierDataCap => {
96 write!(f, "You're out of space, you can purchase additional space")
97 }
98 LbErrKind::OldCardDoesNotExist => write!(f, "No existing card found"),
99 LbErrKind::PathContainsEmptyFileName => {
100 write!(f, "That path contains an empty file name")
101 }
102 LbErrKind::RootModificationInvalid => write!(f, "You cannot modify your root"),
103 LbErrKind::RootNonexistent => write!(f, "Could not find your root file"),
104 LbErrKind::ServerDisabled => write!(
105 f,
106 "The server is not accepting this action at the moment, please try again later"
107 ),
108 LbErrKind::ServerUnreachable => write!(f, "Could not reach server"),
109 LbErrKind::ShareAlreadyExists => write!(f, "That share already exists"),
110 LbErrKind::ShareNonexistent => write!(f, "That share does not exist"),
111 LbErrKind::TryAgain => write!(f, "Please try again"),
112 LbErrKind::UsernameInvalid => write!(f, "That username is invalid"),
113 LbErrKind::UsernameNotFound => write!(f, "That username is not found"),
114 LbErrKind::UsernamePublicKeyMismatch => {
115 write!(f, "That username doesn't match that public key")
116 }
117 LbErrKind::UsernameTaken => write!(f, "That username is not available"),
118 LbErrKind::Unexpected(msg) => write!(f, "Unexpected error: {msg}"),
119 LbErrKind::AlreadySyncing => {
120 write!(f, "A sync is already in progress, cannot begin another sync at this time!")
121 }
122 LbErrKind::ReReadRequired => {
123 write!(f, "This document changed since you last read it, please re-read it!")
124 }
125 LbErrKind::Validation(validation_failure) => match validation_failure {
126 ValidationFailure::Cycle(_) => write!(f, "Cannot move a folder into itself"),
127 ValidationFailure::NonFolderWithChildren(_) => {
128 write!(f, "A document or a link was treated as a folder.")
129 }
130 ValidationFailure::PathConflict(_) => {
131 write!(f, "A file already exists at that path.")
132 }
133 ValidationFailure::DeletedFileUpdated(id) => {
134 write!(f, "this file has been deleted {id}")
135 }
136 ValidationFailure::FileNameTooLong(_) => write!(f, "this filename is too long!"),
137 ValidationFailure::OwnedLink(_) => {
138 write!(f, "you cannot have a link to a file you own")
139 }
140 ValidationFailure::BrokenLink(_) => write!(f, "that link target does not exist!"),
141 ValidationFailure::DuplicateLink { .. } => {
142 write!(f, "you already have a link to that file")
143 }
144 ValidationFailure::SharedLink { .. } => {
145 write!(f, "you cannot place a link inside a shared folder!")
146 }
147 _ => write!(f, "unexpected validation failure: {validation_failure:?}"),
148 },
149 LbErrKind::Diff(diff_error) => {
150 write!(f, "unexpected diff error: {diff_error:?}")
151 }
152 LbErrKind::Sign(sign_error) => {
153 write!(f, "unexpected signing error: {sign_error:?}")
154 }
155 LbErrKind::Crypto(crypto_error) => {
156 write!(f, "unexpected crypto error: {crypto_error:?}")
157 }
158 }
159 }
160}
161
162impl From<LbErrKind> for LbErr {
163 fn from(kind: LbErrKind) -> Self {
164 Self { kind, backtrace: Some(Backtrace::force_capture()) }
165 }
166}
167
168pub trait Unexpected<T> {
169 fn map_unexpected(self) -> LbResult<T>;
170}
171
172impl<T, E: std::fmt::Debug> Unexpected<T> for Result<T, E> {
173 #[track_caller]
174 fn map_unexpected(self) -> LbResult<T> {
175 let location = Location::caller();
176 self.map_err(|err| {
177 LbErrKind::Unexpected(format!(
178 "unexpected error at {}:{} {err:?}",
179 location.file(),
180 location.line(),
181 ))
182 .into()
183 })
184 }
185}
186
187#[derive(Debug)]
188pub struct UnexpectedError {
189 pub msg: String,
190 pub backtrace: Option<Backtrace>,
191}
192
193impl UnexpectedError {
194 pub fn new(s: impl ToString) -> Self {
195 Self { msg: s.to_string(), backtrace: Some(Backtrace::force_capture()) }
196 }
197}
198
199impl fmt::Display for UnexpectedError {
200 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
201 write!(f, "unexpected error: {}", self.msg)
202 }
203}
204
205impl From<LbErr> for UnexpectedError {
206 fn from(err: LbErr) -> Self {
207 Self { msg: format!("{:?}", err.kind), backtrace: err.backtrace }
208 }
209}
210
211impl<T> From<PoisonError<T>> for UnexpectedError {
212 fn from(err: PoisonError<T>) -> Self {
213 Self::new(format!("{:#?}", err))
214 }
215}
216
217impl From<crossbeam::channel::RecvError> for UnexpectedError {
218 fn from(err: crossbeam::channel::RecvError) -> Self {
219 Self::new(format!("{:#?}", err))
220 }
221}
222
223impl From<crossbeam::channel::RecvTimeoutError> for UnexpectedError {
224 fn from(err: crossbeam::channel::RecvTimeoutError) -> Self {
225 Self::new(format!("{:#?}", err))
226 }
227}
228
229impl<T> From<crossbeam::channel::SendError<T>> for UnexpectedError {
230 fn from(err: crossbeam::channel::SendError<T>) -> Self {
231 Self::new(format!("{:#?}", err))
232 }
233}
234
235impl Serialize for UnexpectedError {
236 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
237 where
238 S: Serializer,
239 {
240 let mut state = serializer.serialize_struct("UnexpectedError", 2)?;
241 state.serialize_field("tag", "Unexpected")?;
242 state.serialize_field("content", &self.msg)?;
243 state.end()
244 }
245}
246
247#[macro_export]
248macro_rules! unexpected_only {
249 ($base:literal $(, $args:tt )*) => {{
250 debug!($base $(, $args )*);
251 debug!("{:?}", std::backtrace::Backtrace::force_capture());
252 debug!($base $(, $args )*);
253 UnexpectedError::new(format!($base $(, $args )*))
254 }};
255}
256
257#[derive(Debug, Clone, PartialEq, Eq)]
258pub enum LbErrKind {
259 AccountExists,
260 AccountNonexistent,
261 AccountStringCorrupted,
262 AlreadyCanceled,
263 AlreadyPremium,
264 AppStoreAccountAlreadyLinked,
265 AlreadySyncing,
266 CannotCancelSubscriptionForAppStore,
268 CardDecline,
269 CardExpired,
270 CardInsufficientFunds,
271 CardInvalidCvc,
272 CardInvalidExpMonth,
273 CardInvalidExpYear,
274 CardInvalidNumber,
275 CardNotSupported,
276 ClientUpdateRequired,
277 CurrentUsageIsMoreThanNewTier,
278 DiskPathInvalid,
279 DiskPathTaken,
280 DrawingInvalid,
281 ExistingRequestPending,
282 FileNameContainsSlash,
284 FileNameTooLong,
286 FileNameEmpty,
287 FileNonexistent,
288 FileNotDocument,
289 FileParentNonexistent,
290 InsufficientPermission,
291 InvalidPurchaseToken,
292 InvalidAuthDetails,
293 KeyPhraseInvalid,
294 NotPremium,
295 UsageIsOverDataCap,
296 UsageIsOverFreeTierDataCap,
297 OldCardDoesNotExist,
298 PathContainsEmptyFileName,
299 RootModificationInvalid,
300 RootNonexistent,
301 ServerDisabled,
302 ServerUnreachable,
303 ShareAlreadyExists,
304 ShareNonexistent,
305 TryAgain,
306 UsernameInvalid,
308 UsernameNotFound,
309 UsernamePublicKeyMismatch,
310 UsernameTaken,
311 ReReadRequired,
312 Diff(DiffError),
313
314 Validation(ValidationFailure),
316 Sign(SignError),
317 Crypto(CryptoError),
318
319 Unexpected(String),
324}
325
326#[derive(Debug, Clone, PartialEq, Eq)]
327pub enum DiffError {
328 OldVersionIncorrect,
329 OldFileNotFound,
330 OldVersionRequired,
331 DiffMalformed,
332 HmacModificationInvalid,
333}
334
335#[derive(Debug, Clone, PartialEq, Eq)]
336pub enum SignError {
337 SignatureInvalid,
338 SignatureParseError(libsecp256k1::Error),
340 WrongPublicKey,
341 SignatureInTheFuture(u64),
342 SignatureExpired(u64),
343}
344
345#[derive(Debug, Clone, PartialEq, Eq)]
346pub enum CryptoError {
347 Decryption(aead::Error),
348 HmacVerification(MacError),
349}
350
351impl From<bincode::Error> for LbErr {
352 fn from(err: bincode::Error) -> Self {
353 core_err_unexpected(err).into()
354 }
355}
356
357pub fn core_err_unexpected<T: fmt::Debug>(err: T) -> LbErrKind {
358 LbErrKind::Unexpected(format!("{:?}", err))
359}
360
361pub fn unexpected<T: fmt::Debug>(err: T) -> LbErr {
363 LbErrKind::Unexpected(format!("{:?}", err)).into()
364}
365
366impl From<db_rs::DbError> for LbErr {
367 fn from(err: db_rs::DbError) -> Self {
368 core_err_unexpected(err).into()
369 }
370}
371
372impl<G> From<PoisonError<G>> for LbErr {
373 fn from(err: PoisonError<G>) -> Self {
374 core_err_unexpected(err).into()
375 }
376}
377
378impl From<io::Error> for LbErr {
379 fn from(e: io::Error) -> Self {
380 match e.kind() {
381 io::ErrorKind::NotFound
382 | io::ErrorKind::PermissionDenied
383 | io::ErrorKind::InvalidInput => LbErrKind::DiskPathInvalid,
384 io::ErrorKind::AlreadyExists => LbErrKind::DiskPathTaken,
385 _ => core_err_unexpected(e),
386 }
387 .into()
388 }
389}
390
391impl From<serde_json::Error> for LbErr {
392 fn from(err: serde_json::Error) -> Self {
393 LbErrKind::Unexpected(format!("{err}")).into()
394 }
395}
396
397impl From<ApiError<api::NewAccountError>> for LbErr {
398 fn from(err: ApiError<api::NewAccountError>) -> Self {
399 match err {
400 ApiError::SendFailed(_) => LbErrKind::ServerUnreachable,
401 ApiError::ClientUpdateRequired => LbErrKind::ClientUpdateRequired,
402 ApiError::Endpoint(api::NewAccountError::UsernameTaken) => LbErrKind::UsernameTaken,
403 ApiError::Endpoint(api::NewAccountError::InvalidUsername) => LbErrKind::UsernameInvalid,
404 ApiError::Endpoint(api::NewAccountError::Disabled) => LbErrKind::ServerDisabled,
405 e => core_err_unexpected(e),
406 }
407 .into()
408 }
409}
410
411impl From<ApiError<api::GetPublicKeyError>> for LbErr {
412 fn from(err: ApiError<api::GetPublicKeyError>) -> Self {
413 match err {
414 ApiError::SendFailed(_) => LbErrKind::ServerUnreachable,
415 ApiError::ClientUpdateRequired => LbErrKind::ClientUpdateRequired,
416 ApiError::Endpoint(api::GetPublicKeyError::UserNotFound) => {
417 LbErrKind::AccountNonexistent
418 }
419 e => core_err_unexpected(e),
420 }
421 .into()
422 }
423}
424
425impl From<ApiError<api::GetUsernameError>> for LbErr {
426 fn from(err: ApiError<api::GetUsernameError>) -> Self {
427 match err {
428 ApiError::SendFailed(_) => LbErrKind::ServerUnreachable,
429 ApiError::ClientUpdateRequired => LbErrKind::ClientUpdateRequired,
430 ApiError::Endpoint(api::GetUsernameError::UserNotFound) => {
431 LbErrKind::AccountNonexistent
432 }
433 e => core_err_unexpected(e),
434 }
435 .into()
436 }
437}
438
439impl From<ApiError<api::GetFileIdsError>> for LbErr {
440 fn from(e: ApiError<api::GetFileIdsError>) -> Self {
441 match e {
442 ApiError::SendFailed(_) => LbErrKind::ServerUnreachable,
443 ApiError::ClientUpdateRequired => LbErrKind::ClientUpdateRequired,
444 e => core_err_unexpected(e),
445 }
446 .into()
447 }
448}
449
450impl From<ApiError<api::GetUpdatesError>> for LbErr {
451 fn from(e: ApiError<api::GetUpdatesError>) -> Self {
452 match e {
453 ApiError::SendFailed(_) => LbErrKind::ServerUnreachable,
454 ApiError::ClientUpdateRequired => LbErrKind::ClientUpdateRequired,
455 e => core_err_unexpected(e),
456 }
457 .into()
458 }
459}
460
461impl From<ApiError<api::GetDocumentError>> for LbErr {
462 fn from(e: ApiError<api::GetDocumentError>) -> Self {
463 match e {
464 ApiError::SendFailed(_) => LbErrKind::ServerUnreachable,
465 ApiError::ClientUpdateRequired => LbErrKind::ClientUpdateRequired,
466 e => core_err_unexpected(e),
467 }
468 .into()
469 }
470}
471
472impl From<ApiError<api::UpsertError>> for LbErr {
473 fn from(e: ApiError<api::UpsertError>) -> Self {
474 match e {
475 ApiError::SendFailed(_) => LbErrKind::ServerUnreachable,
476 ApiError::Endpoint(api::UpsertError::UsageIsOverDataCap) => {
477 LbErrKind::UsageIsOverDataCap
478 }
479 ApiError::ClientUpdateRequired => LbErrKind::ClientUpdateRequired,
480 e => core_err_unexpected(e),
481 }
482 .into()
483 }
484}
485
486impl From<ApiError<api::ChangeDocError>> for LbErr {
487 fn from(e: ApiError<api::ChangeDocError>) -> Self {
488 match e {
489 ApiError::SendFailed(_) => LbErrKind::ServerUnreachable,
490 ApiError::Endpoint(api::ChangeDocError::UsageIsOverDataCap) => {
491 LbErrKind::UsageIsOverDataCap
492 }
493 ApiError::ClientUpdateRequired => LbErrKind::ClientUpdateRequired,
494 e => core_err_unexpected(e),
495 }
496 .into()
497 }
498}
499
500impl From<ApiError<api::GetUsageError>> for LbErr {
501 fn from(e: ApiError<api::GetUsageError>) -> Self {
502 match e {
503 ApiError::SendFailed(_) => LbErrKind::ServerUnreachable,
504 ApiError::ClientUpdateRequired => LbErrKind::ClientUpdateRequired,
505 e => core_err_unexpected(e),
506 }
507 .into()
508 }
509}
510
511#[derive(Debug, Clone, Serialize)]
512pub enum Warning {
513 EmptyFile(Uuid),
514 InvalidUTF8(Uuid),
515}
516
517impl fmt::Display for Warning {
518 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
519 match self {
520 Self::EmptyFile(id) => write!(f, "empty file: {}", id),
521 Self::InvalidUTF8(id) => write!(f, "invalid utf8 in file: {}", id),
522 }
523 }
524}