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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
/* 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;
use std::fmt;

/// Result enum used by all implementation functions in this crate.
/// These will be automagically turned into `WebExtStorageApiError` at the
/// FFI layer.
pub type Result<T> = std::result::Result<T, Error>;

#[derive(Debug, Clone, Copy)]
pub enum QuotaReason {
    TotalBytes,
    ItemBytes,
    MaxItems,
}

impl fmt::Display for QuotaReason {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            QuotaReason::ItemBytes => write!(f, "ItemBytes"),
            QuotaReason::MaxItems => write!(f, "MaxItems"),
            QuotaReason::TotalBytes => write!(f, "TotalBytes"),
        }
    }
}

#[derive(Debug, thiserror::Error)]
pub enum WebExtStorageApiError {
    #[error("Unexpected webext-storage error: {reason}")]
    UnexpectedError { reason: String },

    #[error("Error parsing JSON data: {reason}")]
    JsonError { reason: String },

    #[error("Quota exceeded: {reason}")]
    QuotaError { reason: QuotaReason },
}

#[derive(Debug, thiserror::Error)]
pub enum Error {
    #[error("Quota exceeded: {0:?}")]
    QuotaError(QuotaReason),

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

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

    #[error("A connection of this type is already open")]
    ConnectionAlreadyOpen,

    #[error("An invalid connection type was specified")]
    InvalidConnectionType,

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

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

    #[error("Tried to close connection on wrong StorageApi instance")]
    WrongApiForClose,

    // 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("UTF8 Error: {0}")]
    Utf8Error(#[from] std::str::Utf8Error),

    #[error("Error opening database: {0}")]
    OpenDatabaseError(#[from] sql_support::open_database::Error),

    // When trying to close a connection but we aren't the exclusive owner of the containing Arc<>
    #[error("Other shared references to this connection are alive")]
    OtherConnectionReferencesExist,

    #[error("The storage database has been closed")]
    DatabaseConnectionClosed,

    #[error("Sync Error: {0}")]
    SyncError(String),
}

impl GetErrorHandling for Error {
    type ExternalError = WebExtStorageApiError;

    fn get_error_handling(&self) -> ErrorHandling<Self::ExternalError> {
        match self {
            Error::QuotaError(reason) => {
                log::info!("webext-storage-quota-error");
                ErrorHandling::convert(WebExtStorageApiError::QuotaError { reason: *reason })
            }
            Error::JsonError(e) => {
                log::info!("webext-storage-json-error");
                ErrorHandling::convert(WebExtStorageApiError::JsonError {
                    reason: e.to_string(),
                })
            }
            _ => {
                log::info!("webext-storage-unexpected-error");
                ErrorHandling::convert(WebExtStorageApiError::UnexpectedError {
                    reason: self.to_string(),
                })
            }
        }
    }
}

impl From<Error> for WebExtStorageApiError {
    fn from(err: Error) -> WebExtStorageApiError {
        match err {
            Error::JsonError(e) => WebExtStorageApiError::JsonError {
                reason: e.to_string(),
            },
            Error::QuotaError(reason) => WebExtStorageApiError::QuotaError { reason },
            _ => WebExtStorageApiError::UnexpectedError {
                reason: err.to_string(),
            },
        }
    }
}

impl From<rusqlite::Error> for WebExtStorageApiError {
    fn from(value: rusqlite::Error) -> Self {
        WebExtStorageApiError::UnexpectedError {
            reason: value.to_string(),
        }
    }
}

impl From<serde_json::Error> for WebExtStorageApiError {
    fn from(value: serde_json::Error) -> Self {
        WebExtStorageApiError::JsonError {
            reason: value.to_string(),
        }
    }
}