Skip to main content

lb_rs/model/
errors.rs

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