use std::ffi::OsString;
pub type Result<T> = std::result::Result<T, Error>;
pub type ApiResult<T> = std::result::Result<T, LoginsApiError>;
pub use error_support::{breadcrumb, handle_error, report_error};
use error_support::{ErrorHandling, GetErrorHandling};
use jwcrypto::EncryptorDecryptorError;
use sync15::Error as Sync15Error;
#[derive(Debug, thiserror::Error)]
pub enum LoginsApiError {
#[error("Invalid login: {reason}")]
InvalidRecord { reason: String },
#[error("No record with guid exists (when one was required): {reason:?}")]
NoSuchRecord { reason: String },
#[error("Encryption key is in the correct format, but is not the correct key.")]
IncorrectKey,
#[error("{reason}")]
Interrupted { reason: String },
#[error("SyncAuthInvalid error {reason}")]
SyncAuthInvalid { reason: String },
#[error("Unexpected Error: {reason}")]
UnexpectedLoginsApiError { reason: String },
}
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("Malformed incoming record")]
MalformedIncomingRecord,
#[error("Invalid login: {0}")]
InvalidLogin(#[from] InvalidLogin),
#[error("The `sync_status` column in DB has an illegal value: {0}")]
BadSyncStatus(u8),
#[error("No record with guid exists (when one was required): {0:?}")]
NoSuchRecord(String),
#[error("The logins tables are not empty")]
NonEmptyTable,
#[error("local encryption key not set")]
EncryptionKeyMissing,
#[error("Error synchronizing: {0}")]
SyncAdapterError(#[from] sync15::Error),
#[error("Error parsing JSON data: {0}")]
JsonError(#[from] serde_json::Error),
#[error("Error executing SQL: {0}")]
SqlError(#[from] rusqlite::Error),
#[error("Error parsing URL: {0}")]
UrlParseError(#[from] url::ParseError),
#[error("Invalid path: {0:?}")]
InvalidPath(OsString),
#[error("CryptoError({0})")]
CryptoError(#[from] EncryptorDecryptorError),
#[error("{0}")]
Interrupted(#[from] interrupt_support::Interrupted),
#[error("IOError: {0}")]
IOError(#[from] std::io::Error),
#[error("Migration Error: {0}")]
MigrationError(String),
}
#[derive(Debug, thiserror::Error)]
pub enum InvalidLogin {
#[error("Origin is empty")]
EmptyOrigin,
#[error("Password is empty")]
EmptyPassword,
#[error("Login already exists")]
DuplicateLogin,
#[error("Both `formActionOrigin` and `httpRealm` are present")]
BothTargets,
#[error("Neither `formActionOrigin` or `httpRealm` are present")]
NoTarget,
#[error("Login has illegal origin")]
IllegalOrigin,
#[error("Login has illegal field: {field_info}")]
IllegalFieldValue { field_info: String },
}
impl GetErrorHandling for Error {
type ExternalError = LoginsApiError;
fn get_error_handling(&self) -> ErrorHandling<Self::ExternalError> {
match self {
Self::InvalidLogin(why) => ErrorHandling::convert(LoginsApiError::InvalidRecord {
reason: why.to_string(),
}),
Self::MalformedIncomingRecord => {
ErrorHandling::convert(LoginsApiError::InvalidRecord {
reason: "invalid incoming record".to_string(),
})
}
Self::NoSuchRecord(guid) => ErrorHandling::convert(LoginsApiError::NoSuchRecord {
reason: guid.to_string(),
}),
Self::NonEmptyTable => {
ErrorHandling::convert(LoginsApiError::UnexpectedLoginsApiError {
reason: "must be an empty DB to migrate".to_string(),
})
.report_error("logins-migration")
}
Self::CryptoError { .. } => ErrorHandling::convert(LoginsApiError::IncorrectKey)
.report_error("logins-crypto-error"),
Self::Interrupted(_) => ErrorHandling::convert(LoginsApiError::Interrupted {
reason: self.to_string(),
}),
Self::SyncAdapterError(e) => match e {
Sync15Error::TokenserverHttpError(401) | Sync15Error::BadKeyLength(..) => {
ErrorHandling::convert(LoginsApiError::SyncAuthInvalid {
reason: e.to_string(),
})
.log_warning()
}
Sync15Error::RequestError(_) => {
ErrorHandling::convert(LoginsApiError::UnexpectedLoginsApiError {
reason: e.to_string(),
})
.log_warning()
}
_ => ErrorHandling::convert(LoginsApiError::UnexpectedLoginsApiError {
reason: self.to_string(),
})
.report_error("logins-sync"),
},
_ => ErrorHandling::convert(LoginsApiError::UnexpectedLoginsApiError {
reason: self.to_string(),
})
.report_error("logins-unexpected"),
}
}
}