Skip to main content

glean_core/
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 https://mozilla.org/MPL/2.0/.
4
5use std::ffi::OsString;
6use std::fmt::{self, Display};
7use std::io;
8use std::result;
9
10use rkv::StoreError;
11
12use crate::database::sqlite::SchemaError;
13
14/// A specialized [`Result`] type for this crate's operations.
15///
16/// This is generally used to avoid writing out [`Error`] directly and
17/// is otherwise a direct mapping to [`Result`].
18///
19/// [`Result`]: https://doc.rust-lang.org/stable/std/result/enum.Result.html
20/// [`Error`]: std.struct.Error.html
21pub type Result<T, E = Error> = result::Result<T, E>;
22
23/// A list enumerating the categories of errors in this crate.
24///
25/// [`Error`]: https://doc.rust-lang.org/stable/std/error/trait.Error.html
26///
27/// This list is intended to grow over time and it is not recommended to
28/// exhaustively match against it.
29#[derive(Debug)]
30#[non_exhaustive]
31pub enum ErrorKind {
32    /// Lifetime conversion failed
33    Lifetime(i32),
34
35    /// IO error
36    IoError(io::Error),
37
38    /// IO error
39    Rkv(StoreError),
40
41    /// JSON error
42    Json(serde_json::error::Error),
43
44    /// TimeUnit conversion failed
45    TimeUnit(i32),
46
47    /// MemoryUnit conversion failed
48    MemoryUnit(i32),
49
50    /// HistogramType conversion failed
51    HistogramType(i32),
52
53    /// [`OsString`] conversion failed
54    OsString(OsString),
55
56    /// Unknown error
57    Utf8Error,
58
59    /// Glean initialization was attempted with an invalid configuration
60    InvalidConfig,
61
62    /// Glean not initialized
63    NotInitialized,
64
65    /// Ping request body size overflowed
66    PingBodyOverflow(usize),
67
68    /// Parsing a UUID from a string failed
69    UuidError(uuid::Error),
70
71    /// Database/SQLite error
72    SQLite(rusqlite::Error),
73
74    /// Schema error
75    Schema(SchemaError),
76}
77
78/// A specialized [`Error`] type for this crate's operations.
79///
80/// [`Error`]: https://doc.rust-lang.org/stable/std/error/trait.Error.html
81#[derive(Debug)]
82pub struct Error {
83    kind: ErrorKind,
84}
85
86impl Error {
87    /// Returns a new UTF-8 error
88    ///
89    /// This is exposed in order to expose conversion errors on the FFI layer.
90    pub fn utf8_error() -> Error {
91        Error {
92            kind: ErrorKind::Utf8Error,
93        }
94    }
95
96    /// Indicates an error that no requested global object is initialized
97    pub fn not_initialized() -> Error {
98        Error {
99            kind: ErrorKind::NotInitialized,
100        }
101    }
102
103    /// Returns the kind of the current error instance.
104    pub fn kind(&self) -> &ErrorKind {
105        &self.kind
106    }
107}
108
109impl std::error::Error for Error {}
110
111impl Display for Error {
112    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113        use ErrorKind::*;
114        match self.kind() {
115            Lifetime(l) => write!(f, "Lifetime conversion from {} failed", l),
116            IoError(e) => write!(f, "An I/O error occurred: {}", e),
117            Rkv(e) => write!(f, "An Rkv error occurred: {}", e),
118            Json(e) => write!(f, "A JSON error occurred: {}", e),
119            TimeUnit(t) => write!(f, "TimeUnit conversion from {} failed", t),
120            MemoryUnit(m) => write!(f, "MemoryUnit conversion from {} failed", m),
121            HistogramType(h) => write!(f, "HistogramType conversion from {} failed", h),
122            OsString(s) => write!(f, "OsString conversion from {:?} failed", s),
123            Utf8Error => write!(f, "Invalid UTF-8 byte sequence in string"),
124            InvalidConfig => write!(f, "Invalid Glean configuration provided"),
125            NotInitialized => write!(f, "Global Glean object missing"),
126            PingBodyOverflow(s) => write!(
127                f,
128                "Ping request body size exceeded maximum size allowed: {}kB.",
129                s / 1024
130            ),
131            UuidError(e) => write!(f, "Failed to parse UUID: {}", e),
132            SQLite(e) => write!(f, "SQLite error: {}", e),
133            Schema(e) => write!(f, "Schema error: {}", e),
134        }
135    }
136}
137
138impl From<ErrorKind> for Error {
139    fn from(kind: ErrorKind) -> Error {
140        Error { kind }
141    }
142}
143
144impl From<io::Error> for Error {
145    fn from(error: io::Error) -> Error {
146        Error {
147            kind: ErrorKind::IoError(error),
148        }
149    }
150}
151
152impl From<StoreError> for Error {
153    fn from(error: StoreError) -> Error {
154        Error {
155            kind: ErrorKind::Rkv(error),
156        }
157    }
158}
159
160impl From<serde_json::error::Error> for Error {
161    fn from(error: serde_json::error::Error) -> Error {
162        Error {
163            kind: ErrorKind::Json(error),
164        }
165    }
166}
167
168impl From<rusqlite::Error> for Error {
169    fn from(error: rusqlite::Error) -> Error {
170        Error {
171            kind: ErrorKind::SQLite(error),
172        }
173    }
174}
175
176impl From<SchemaError> for Error {
177    fn from(error: SchemaError) -> Error {
178        match error {
179            SchemaError::Sqlite(err) => Error {
180                kind: ErrorKind::SQLite(err),
181            },
182            err => Error {
183                kind: ErrorKind::Schema(err),
184            },
185        }
186    }
187}
188
189impl From<OsString> for Error {
190    fn from(error: OsString) -> Error {
191        Error {
192            kind: ErrorKind::OsString(error),
193        }
194    }
195}
196
197/// To satisfy integer conversion done by the macros on the FFI side, we need to be able to turn
198/// something infallible into an error.
199/// This will never actually be reached, as an integer-to-integer conversion is infallible.
200impl From<std::convert::Infallible> for Error {
201    fn from(_: std::convert::Infallible) -> Error {
202        unreachable!()
203    }
204}
205
206impl From<uuid::Error> for Error {
207    fn from(error: uuid::Error) -> Self {
208        Error {
209            kind: ErrorKind::UuidError(error),
210        }
211    }
212}
213
214#[derive(Debug)]
215pub enum ClientIdFileError {
216    /// The file could not be found.
217    NotFound,
218    /// Can't access the file due to permissions
219    PermissionDenied,
220    /// Another io error happened
221    IoError(io::Error),
222    /// Parsing the content into a UUID failed
223    ParseError(uuid::Error),
224}
225
226impl Display for ClientIdFileError {
227    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
228        use ClientIdFileError::*;
229        match self {
230            NotFound => write!(f, "File not found"),
231            PermissionDenied => write!(
232                f,
233                "The operation lacked the necessary privileges to complete."
234            ),
235            IoError(e) => write!(f, "IO error occurred: {e}"),
236            ParseError(e) => write!(f, "Parse error occurred: {e}"),
237        }
238    }
239}
240
241impl From<io::Error> for ClientIdFileError {
242    fn from(error: io::Error) -> Self {
243        match error.kind() {
244            io::ErrorKind::NotFound => ClientIdFileError::NotFound,
245            io::ErrorKind::PermissionDenied => ClientIdFileError::PermissionDenied,
246            _ => ClientIdFileError::IoError(error),
247        }
248    }
249}
250
251impl From<uuid::Error> for ClientIdFileError {
252    fn from(error: uuid::Error) -> Self {
253        ClientIdFileError::ParseError(error)
254    }
255}