1use std::ffi::OsString;
6pub type Result<T> = std::result::Result<T, Error>;
7pub type ApiResult<T> = std::result::Result<T, LoginsApiError>;
9
10pub use error_support::{breadcrumb, handle_error, report_error};
11pub use error_support::{debug, error, info, trace, warn};
12
13use error_support::{ErrorHandling, GetErrorHandling};
14use jwcrypto::JwCryptoError;
15
16#[derive(Debug, thiserror::Error)]
18pub enum LoginsApiError {
19 #[error("NSS not initialized")]
20 NSSUninitialized,
21
22 #[error("NSS error during authentication: {reason}")]
23 NSSAuthenticationError { reason: String },
24
25 #[error("error during authentication: {reason}")]
26 AuthenticationError { reason: String },
27
28 #[error("authentication cancelled")]
29 AuthenticationCanceled,
30
31 #[error("Invalid login: {reason}")]
32 InvalidRecord { reason: String },
33
34 #[error("No record with guid exists (when one was required): {reason:?}")]
35 NoSuchRecord { reason: String },
36
37 #[error("Encryption key is missing.")]
38 MissingKey,
39
40 #[error("Encryption key is not valid.")]
41 InvalidKey,
42
43 #[error("encryption failed: {reason}")]
44 EncryptionFailed { reason: String },
45
46 #[error("decryption failed: {reason}")]
47 DecryptionFailed { reason: String },
48
49 #[error("{reason}")]
50 Interrupted { reason: String },
51
52 #[error("Unexpected Error: {reason}")]
53 UnexpectedLoginsApiError { reason: String },
54}
55
56#[derive(Debug, thiserror::Error)]
60pub enum Error {
61 #[error("Database is closed")]
62 DatabaseClosed,
63
64 #[error("Malformed incoming record")]
65 MalformedIncomingRecord,
66
67 #[error("Invalid login: {0}")]
68 InvalidLogin(#[from] InvalidLogin),
69
70 #[error("The `sync_status` column in DB has an illegal value: {0}")]
71 BadSyncStatus(u8),
72
73 #[error("No record with guid exists (when one was required): {0:?}")]
74 NoSuchRecord(String),
75
76 #[error("The logins tables are not empty")]
78 NonEmptyTable,
79
80 #[error("encryption failed: {0:?}")]
81 EncryptionFailed(String),
82
83 #[error("decryption failed: {0:?}")]
84 DecryptionFailed(String),
85
86 #[error("Error parsing JSON data: {0}")]
87 JsonError(#[from] serde_json::Error),
88
89 #[error("Error executing SQL: {0}")]
90 SqlError(#[from] rusqlite::Error),
91
92 #[error("Error parsing URL: {0}")]
93 UrlParseError(#[from] url::ParseError),
94
95 #[error("Invalid path: {0:?}")]
96 InvalidPath(OsString),
97
98 #[error("CryptoError({0})")]
99 CryptoError(#[from] JwCryptoError),
100
101 #[error("{0}")]
102 Interrupted(#[from] interrupt_support::Interrupted),
103
104 #[error("IOError: {0}")]
105 IOError(#[from] std::io::Error),
106
107 #[error("Migration Error: {0}")]
108 MigrationError(String),
109
110 #[error("IncompatibleVersion: {0}")]
111 IncompatibleVersion(i64),
112}
113
114#[derive(Debug, thiserror::Error)]
116pub enum InvalidLogin {
117 #[error("Origin is empty")]
119 EmptyOrigin,
120 #[error("Password is empty")]
121 EmptyPassword,
122 #[error("Login already exists")]
123 DuplicateLogin,
124 #[error("Both `formActionOrigin` and `httpRealm` are present")]
125 BothTargets,
126 #[error("Neither `formActionOrigin` or `httpRealm` are present")]
127 NoTarget,
128 #[error("Login has illegal origin: {reason}")]
131 IllegalOrigin { reason: String },
132 #[error("Login has illegal field: {field_info}")]
133 IllegalFieldValue { field_info: String },
134}
135
136impl GetErrorHandling for Error {
139 type ExternalError = LoginsApiError;
140
141 fn get_error_handling(&self) -> ErrorHandling<Self::ExternalError> {
142 match self {
143 Self::InvalidLogin(why) => ErrorHandling::convert(LoginsApiError::InvalidRecord {
144 reason: why.to_string(),
145 }),
146 Self::MalformedIncomingRecord => {
147 ErrorHandling::convert(LoginsApiError::InvalidRecord {
148 reason: "invalid incoming record".to_string(),
149 })
150 }
151 Self::NoSuchRecord(guid) => ErrorHandling::convert(LoginsApiError::NoSuchRecord {
153 reason: guid.to_string(),
154 }),
155 Self::NonEmptyTable => {
159 ErrorHandling::convert(LoginsApiError::UnexpectedLoginsApiError {
160 reason: "must be an empty DB to migrate".to_string(),
161 })
162 .report_error("logins-migration")
163 }
164 Self::Interrupted(_) => ErrorHandling::convert(LoginsApiError::Interrupted {
165 reason: self.to_string(),
166 }),
167 Error::SqlError(rusqlite::Error::SqliteFailure(err, _)) => match err.code {
168 rusqlite::ErrorCode::DatabaseCorrupt => {
169 ErrorHandling::convert(LoginsApiError::UnexpectedLoginsApiError {
170 reason: self.to_string(),
171 })
172 .report_error("logins-db-corrupt")
173 }
174 rusqlite::ErrorCode::DiskFull => {
175 ErrorHandling::convert(LoginsApiError::UnexpectedLoginsApiError {
176 reason: self.to_string(),
177 })
178 .report_error("logins-db-disk-full")
179 }
180 _ => ErrorHandling::convert(LoginsApiError::UnexpectedLoginsApiError {
181 reason: self.to_string(),
182 })
183 .report_error("logins-unexpected"),
184 },
185 _ => ErrorHandling::convert(LoginsApiError::UnexpectedLoginsApiError {
191 reason: self.to_string(),
192 })
193 .report_error("logins-unexpected"),
194 }
195 }
196}
197
198impl From<uniffi::UnexpectedUniFFICallbackError> for LoginsApiError {
199 fn from(error: uniffi::UnexpectedUniFFICallbackError) -> Self {
200 LoginsApiError::UnexpectedLoginsApiError {
201 reason: error.to_string(),
202 }
203 }
204}