1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

use error_support::{ErrorHandling, GetErrorHandling};
use interrupt_support::Interrupted;

/// Result enum for the public API
pub type ApiResult<T> = std::result::Result<T, AutofillApiError>;

/// Result enum for internal functions
pub type Result<T> = std::result::Result<T, Error>;

// Errors we return via the public interface.
#[derive(Debug, thiserror::Error)]
pub enum AutofillApiError {
    #[error("Error executing SQL: {reason}")]
    SqlError { reason: String },

    #[error("Operation interrupted")]
    InterruptedError,

    #[error("Crypto Error: {reason}")]
    CryptoError { reason: String },

    #[error("No record with guid exists: {guid}")]
    NoSuchRecord { guid: String },

    #[error("Unexpected Error: {reason}")]
    UnexpectedAutofillApiError { reason: String },
}

#[derive(Debug, thiserror::Error)]
pub enum Error {
    #[error("Error opening database: {0}")]
    OpenDatabaseError(#[from] sql_support::open_database::Error),

    #[error("Error executing SQL: {0}")]
    SqlError(#[from] rusqlite::Error),

    #[error("IO error: {0}")]
    IoError(#[from] std::io::Error),

    #[error("Operation interrupted")]
    InterruptedError(#[from] Interrupted),

    // This will happen if you provide something absurd like
    // "/" or "" as your database path. For more subtley broken paths,
    // we'll likely return an IoError.
    #[error("Illegal database path: {0:?}")]
    IllegalDatabasePath(std::path::PathBuf),

    #[error("JSON Error: {0}")]
    JsonError(#[from] serde_json::Error),

    #[error("Invalid sync payload: {0}")]
    InvalidSyncPayload(String),

    #[error("Crypto Error: {0}")]
    CryptoError(#[from] jwcrypto::EncryptorDecryptorError),

    #[error("Missing local encryption key")]
    MissingEncryptionKey,

    #[error("No record with guid exists: {0}")]
    NoSuchRecord(String),
}

// Define how our internal errors are handled and converted to external errors
// See `support/error/README.md` for how this works, especially the warning about PII.
impl GetErrorHandling for Error {
    type ExternalError = AutofillApiError;

    fn get_error_handling(&self) -> ErrorHandling<Self::ExternalError> {
        match self {
            Self::OpenDatabaseError(e) => ErrorHandling::convert(AutofillApiError::SqlError {
                reason: e.to_string(),
            })
            .report_error("autofill-open-database-error"),

            Self::SqlError(e) => ErrorHandling::convert(AutofillApiError::SqlError {
                reason: e.to_string(),
            })
            .report_error("autofill-sql-error"),

            Self::IoError(e) => {
                ErrorHandling::convert(AutofillApiError::UnexpectedAutofillApiError {
                    reason: e.to_string(),
                })
                .report_error("autofill-io-error")
            }

            Self::InterruptedError(_) => ErrorHandling::convert(AutofillApiError::InterruptedError),

            Self::IllegalDatabasePath(path) => ErrorHandling::convert(AutofillApiError::SqlError {
                reason: format!("Path not found: {}", path.to_string_lossy()),
            })
            .report_error("autofill-illegal-database-path"),

            Self::JsonError(e) => {
                ErrorHandling::convert(AutofillApiError::UnexpectedAutofillApiError {
                    reason: e.to_string(),
                })
                .report_error("autofill-json-error")
            }

            Self::InvalidSyncPayload(reason) => {
                ErrorHandling::convert(AutofillApiError::UnexpectedAutofillApiError {
                    reason: reason.clone(),
                })
                .report_error("autofill-invalid-sync-payload")
            }

            Self::CryptoError(e) => ErrorHandling::convert(AutofillApiError::CryptoError {
                reason: e.to_string(),
            })
            .report_error("autofill-crypto-error"),

            Self::MissingEncryptionKey => ErrorHandling::convert(AutofillApiError::CryptoError {
                reason: "Missing encryption key".to_string(),
            })
            .report_error("autofill-missing-encryption-key"),

            Self::NoSuchRecord(guid) => {
                ErrorHandling::convert(AutofillApiError::NoSuchRecord { guid: guid.clone() })
                    .log_warning()
            }
        }
    }
}