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