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