autofill/
error.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2* License, v. 2.0. If a copy of the MPL was not distributed with this
3* file, You can obtain one at http://mozilla.org/MPL/2.0/.
4*/
5
6use error_support::{ErrorHandling, GetErrorHandling};
7use interrupt_support::Interrupted;
8
9/// Result enum for the public API
10pub type ApiResult<T> = std::result::Result<T, AutofillApiError>;
11
12/// Result enum for internal functions
13pub type Result<T> = std::result::Result<T, Error>;
14
15// Errors we return via the public interface.
16#[derive(Debug, thiserror::Error)]
17pub enum AutofillApiError {
18    #[error("Error executing SQL: {reason}")]
19    SqlError { reason: String },
20
21    #[error("Operation interrupted")]
22    InterruptedError,
23
24    #[error("Crypto Error: {reason}")]
25    CryptoError { reason: String },
26
27    #[error("No record with guid exists: {guid}")]
28    NoSuchRecord { guid: String },
29
30    #[error("Unexpected Error: {reason}")]
31    UnexpectedAutofillApiError { reason: String },
32}
33
34#[derive(Debug, thiserror::Error)]
35pub enum Error {
36    #[error("Error opening database: {0}")]
37    OpenDatabaseError(#[from] sql_support::open_database::Error),
38
39    #[error("Error executing SQL: {0}")]
40    SqlError(#[from] rusqlite::Error),
41
42    #[error("IO error: {0}")]
43    IoError(#[from] std::io::Error),
44
45    #[error("Operation interrupted")]
46    InterruptedError(#[from] Interrupted),
47
48    // This will happen if you provide something absurd like
49    // "/" or "" as your database path. For more subtley broken paths,
50    // we'll likely return an IoError.
51    #[error("Illegal database path: {0:?}")]
52    IllegalDatabasePath(std::path::PathBuf),
53
54    #[error("JSON Error: {0}")]
55    JsonError(#[from] serde_json::Error),
56
57    #[error("Invalid sync payload: {0}")]
58    InvalidSyncPayload(String),
59
60    #[error("Crypto Error: {0}")]
61    CryptoError(#[from] jwcrypto::JwCryptoError),
62
63    #[error("Missing local encryption key")]
64    MissingEncryptionKey,
65
66    #[error("No record with guid exists: {0}")]
67    NoSuchRecord(String),
68}
69
70// Define how our internal errors are handled and converted to external errors
71// See `support/error/README.md` for how this works, especially the warning about PII.
72impl GetErrorHandling for Error {
73    type ExternalError = AutofillApiError;
74
75    fn get_error_handling(&self) -> ErrorHandling<Self::ExternalError> {
76        match self {
77            Self::OpenDatabaseError(e) => ErrorHandling::convert(AutofillApiError::SqlError {
78                reason: e.to_string(),
79            })
80            .report_error("autofill-open-database-error"),
81
82            Self::SqlError(e) => ErrorHandling::convert(AutofillApiError::SqlError {
83                reason: e.to_string(),
84            })
85            .report_error("autofill-sql-error"),
86
87            Self::IoError(e) => {
88                ErrorHandling::convert(AutofillApiError::UnexpectedAutofillApiError {
89                    reason: e.to_string(),
90                })
91                .report_error("autofill-io-error")
92            }
93
94            Self::InterruptedError(_) => ErrorHandling::convert(AutofillApiError::InterruptedError),
95
96            Self::IllegalDatabasePath(path) => ErrorHandling::convert(AutofillApiError::SqlError {
97                reason: format!("Path not found: {}", path.to_string_lossy()),
98            })
99            .report_error("autofill-illegal-database-path"),
100
101            Self::JsonError(e) => {
102                ErrorHandling::convert(AutofillApiError::UnexpectedAutofillApiError {
103                    reason: e.to_string(),
104                })
105                .report_error("autofill-json-error")
106            }
107
108            Self::InvalidSyncPayload(reason) => {
109                ErrorHandling::convert(AutofillApiError::UnexpectedAutofillApiError {
110                    reason: reason.clone(),
111                })
112                .report_error("autofill-invalid-sync-payload")
113            }
114
115            Self::CryptoError(e) => ErrorHandling::convert(AutofillApiError::CryptoError {
116                reason: e.to_string(),
117            })
118            .report_error("autofill-crypto-error"),
119
120            Self::MissingEncryptionKey => ErrorHandling::convert(AutofillApiError::CryptoError {
121                reason: "Missing encryption key".to_string(),
122            })
123            .report_error("autofill-missing-encryption-key"),
124
125            Self::NoSuchRecord(guid) => {
126                ErrorHandling::convert(AutofillApiError::NoSuchRecord { guid: guid.clone() })
127                    .log_warning()
128            }
129        }
130    }
131}