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::{OpenError, 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<OpenError> for Error {
177    fn from(error: OpenError) -> Error {
178        match error {
179            OpenError::IncompatibleVersion(v) => Error {
180                kind: ErrorKind::Schema(SchemaError::UnsupportedSchemaVersion(v)),
181            },
182            OpenError::Corrupt => Error {
183                kind: ErrorKind::NotInitialized,
184            },
185            OpenError::SqlError(error) => error.into(),
186            OpenError::RecoveryError(error) => error.into(),
187        }
188    }
189}
190
191impl From<OsString> for Error {
192    fn from(error: OsString) -> Error {
193        Error {
194            kind: ErrorKind::OsString(error),
195        }
196    }
197}
198
199/// To satisfy integer conversion done by the macros on the FFI side, we need to be able to turn
200/// something infallible into an error.
201/// This will never actually be reached, as an integer-to-integer conversion is infallible.
202impl From<std::convert::Infallible> for Error {
203    fn from(_: std::convert::Infallible) -> Error {
204        unreachable!()
205    }
206}
207
208impl From<uuid::Error> for Error {
209    fn from(error: uuid::Error) -> Self {
210        Error {
211            kind: ErrorKind::UuidError(error),
212        }
213    }
214}
215
216#[derive(Debug)]
217pub enum ClientIdFileError {
218    /// The file could not be found.
219    NotFound,
220    /// Can't access the file due to permissions
221    PermissionDenied,
222    /// Another io error happened
223    IoError(io::Error),
224    /// Parsing the content into a UUID failed
225    ParseError(uuid::Error),
226}
227
228impl Display for ClientIdFileError {
229    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
230        use ClientIdFileError::*;
231        match self {
232            NotFound => write!(f, "File not found"),
233            PermissionDenied => write!(
234                f,
235                "The operation lacked the necessary privileges to complete."
236            ),
237            IoError(e) => write!(f, "IO error occurred: {e}"),
238            ParseError(e) => write!(f, "Parse error occurred: {e}"),
239        }
240    }
241}
242
243impl From<io::Error> for ClientIdFileError {
244    fn from(error: io::Error) -> Self {
245        match error.kind() {
246            io::ErrorKind::NotFound => ClientIdFileError::NotFound,
247            io::ErrorKind::PermissionDenied => ClientIdFileError::PermissionDenied,
248            _ => ClientIdFileError::IoError(error),
249        }
250    }
251}
252
253impl From<uuid::Error> for ClientIdFileError {
254    fn from(error: uuid::Error) -> Self {
255        ClientIdFileError::ParseError(error)
256    }
257}