1
use std::{
2
    array::TryFromSliceError,
3
    convert::Infallible,
4
    fmt::{Debug, Display},
5
};
6

            
7
use backtrace::Backtrace;
8
use parking_lot::{Mutex, MutexGuard};
9
use thiserror::Error;
10

            
11
use crate::AbortError;
12

            
13
/// An error from Nebari as well as an associated backtrace.
14
pub struct Error {
15
    /// The error that occurred.
16
    pub kind: ErrorKind,
17

            
18
    backtrace: Mutex<Backtrace>,
19
}
20

            
21
impl Error {
22
3
    pub(crate) fn data_integrity(error: impl Into<Self>) -> Self {
23
3
        Self {
24
3
            kind: ErrorKind::DataIntegrity(Box::new(error.into())),
25
3
            backtrace: Mutex::new(Backtrace::new_unresolved()),
26
3
        }
27
3
    }
28

            
29
    /// Returns the backtrace of where this error was created.
30
    pub fn backtrace(&self) -> MutexGuard<'_, Backtrace> {
31
        let mut backtrace = self.backtrace.lock();
32
        backtrace.resolve();
33
        backtrace
34
    }
35

            
36
    fn format_backtrace_frames(&self) -> Vec<String> {
37
        let mut backtrace = self.backtrace.lock();
38
        backtrace.resolve();
39
        backtrace
40
            .frames()
41
            .iter()
42
            .filter_map(|frame| frame.symbols().first())
43
            .enumerate()
44
            .map(|(index, symbol)| {
45
                let mut line = format!("{index}: ");
46
                if let Some(name) = symbol.name() {
47
                    line.push_str(&name.to_string());
48
                    line.push(' ');
49
                } else if let Some(addr) = symbol.addr() {
50
                    line.push_str(&format!("{:x}", addr as usize));
51
                    line.push(' ');
52
                } else {
53
                    // Give up on formatting this one.
54
                    line.push_str(&format!("{symbol:?}"));
55
                    return line;
56
                }
57

            
58
                if let Some(file) = symbol.filename() {
59
                    if let Some(file) = file.to_str() {
60
                        line.push_str("at ");
61
                        line.push_str(file);
62
                    } else {
63
                        line.push_str(&format!("at {file:?}"));
64
                    }
65

            
66
                    if let Some(lineno) = symbol.lineno() {
67
                        line.push(':');
68
                        line.push_str(&lineno.to_string());
69
                        if let Some(col) = symbol.colno() {
70
                            line.push(':');
71
                            line.push_str(&col.to_string());
72
                        }
73
                    }
74
                }
75
                line
76
            })
77
            .collect()
78
    }
79
}
80

            
81
impl std::error::Error for Error {
82
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
83
        self.kind.source()
84
    }
85
}
86

            
87
impl Display for Error {
88
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89
        Display::fmt(&self.kind, f)?;
90

            
91
        #[cfg(debug_assertions)]
92
        {
93
            f.write_str("\nstack backtrace:")?;
94

            
95
            for (index, frame) in self.format_backtrace_frames().into_iter().enumerate() {
96
                write!(f, "{index}: {frame}")?;
97
            }
98
        }
99

            
100
        Ok(())
101
    }
102
}
103

            
104
impl Debug for Error {
105
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106
        let frames = self.format_backtrace_frames();
107
        f.debug_struct("Error")
108
            .field("kind", &self.kind)
109
            .field("backtrace", &&frames[..])
110
            .finish()
111
    }
112
}
113

            
114
impl From<ErrorKind> for Error {
115
29
    fn from(kind: ErrorKind) -> Self {
116
29
        Self {
117
29
            kind,
118
29
            backtrace: Mutex::new(Backtrace::new_unresolved()),
119
29
        }
120
29
    }
121
}
122

            
123
impl From<AbortError<Infallible>> for Error {
124
    fn from(ae: AbortError<Infallible>) -> Self {
125
        ae.infallible()
126
    }
127
}
128

            
129
impl From<std::io::Error> for Error {
130
17
    fn from(err: std::io::Error) -> Self {
131
17
        Self {
132
17
            kind: ErrorKind::from(err),
133
17
            backtrace: Mutex::new(Backtrace::new_unresolved()),
134
17
        }
135
17
    }
136
}
137

            
138
impl From<&'static str> for Error {
139
    fn from(message: &'static str) -> Self {
140
        Self {
141
            kind: ErrorKind::message(message),
142
            backtrace: Mutex::new(Backtrace::new_unresolved()),
143
        }
144
    }
145
}
146

            
147
impl From<flume::RecvError> for Error {
148
    fn from(_err: flume::RecvError) -> Self {
149
        Self {
150
            kind: ErrorKind::Internal(InternalError::InternalCommunication),
151
            backtrace: Mutex::new(Backtrace::new_unresolved()),
152
        }
153
    }
154
}
155

            
156
impl<T> From<flume::SendError<T>> for Error {
157
    fn from(_err: flume::SendError<T>) -> Self {
158
        Self {
159
            kind: ErrorKind::Internal(InternalError::InternalCommunication),
160
            backtrace: Mutex::new(Backtrace::new_unresolved()),
161
        }
162
    }
163
}
164

            
165
impl From<String> for Error {
166
65569
    fn from(message: String) -> Self {
167
65569
        Self {
168
65569
            kind: ErrorKind::message(message),
169
65569
            backtrace: Mutex::new(Backtrace::new_unresolved()),
170
65569
        }
171
65569
    }
172
}
173

            
174
impl From<TryFromSliceError> for Error {
175
    fn from(_: TryFromSliceError) -> Self {
176
        Self {
177
            kind: ErrorKind::Internal(InternalError::IncorrectByteLength),
178
            backtrace: Mutex::new(Backtrace::new_unresolved()),
179
        }
180
    }
181
}
182

            
183
/// An error from Nebari.
184
#[derive(Debug, Error)]
185
#[error(transparent)]
186
pub enum ErrorKind {
187
    /// An error has occurred. The string contains human-readable error message.
188
    /// This error is only used in situations where a user is not expected to be
189
    /// able to recover automatically from the error.
190
    #[error("{0}")]
191
    Message(String),
192
    /// An error occurred while performing IO.
193
    #[error("io error: {0}")]
194
    Io(#[from] std::io::Error),
195
    /// An unrecoverable data integrity error was encountered.
196
    #[error("an unrecoverable error with the data on disk has been found: {0}")]
197
    DataIntegrity(Box<Error>),
198
    /// An invalid tree name was provided.
199
    ///
200
    /// Valid characters are:
201
    ///
202
    /// - `'a'..='z'`
203
    /// - `'A'..='Z'`
204
    /// - `'0'..='9'`
205
    /// - `'-'` (Hyphen)
206
    /// - `'_'` (Underscore)
207
    /// - `'.'` (Period)
208
    #[error("tree name not valid")]
209
    InvalidTreeName,
210
    /// A key was too large.
211
    #[error("key too large")]
212
    KeyTooLarge,
213
    /// A value was too large.
214
    #[error("value too large")]
215
    ValueTooLarge,
216
    /// A multi-key operation did not have its keys ordered.
217
    #[error("multi-key operation did not have its keys ordered")]
218
    KeysNotOrdered,
219
    /// An internal error occurred. These errors are not intended to be
220
    /// recoverable and represent some internal error condition.
221
    #[error("an internal error occurred: {0}")]
222
    Internal(InternalError),
223
    /// The underlying tree file has been compacted, and the request cannot
224
    /// be completed. Reopen the file and try again.
225
    #[error("the file has been compacted. reopen the file and try again")]
226
    TreeCompacted,
227
    /// An error ocurred in the vault.
228
    #[error("a vault error occurred: {0}")]
229
    Vault(Box<dyn SendSyncError>),
230
    /// An transaction was pushed to the log out of order.
231
    #[error("transaction pushed out of order")]
232
    TransactionPushedOutOfOrder,
233
}
234

            
235
pub trait SendSyncError: std::error::Error + Send + Sync + 'static {}
236
impl<T> SendSyncError for T where T: std::error::Error + Send + Sync + 'static {}
237

            
238
impl ErrorKind {
239
    /// Returns a new [`Error::Message`] instance with the message provided.
240
65582
    pub(crate) fn message<S: Display>(message: S) -> Self {
241
65582
        Self::Message(message.to_string())
242
65582
    }
243

            
244
65566
    pub(crate) fn data_integrity(error: impl Into<Error>) -> Self {
245
65566
        Self::DataIntegrity(Box::new(error.into()))
246
65566
    }
247

            
248
    /// Returns true if this error represents an
249
    /// [`std::io::ErrorKind::NotFound`].
250
    #[must_use]
251
2
    pub fn is_file_not_found(&self) -> bool {
252
        matches!(self, Self::Io(err) if err.kind() == std::io::ErrorKind::NotFound)
253
2
    }
254
}
255

            
256
impl From<&'static str> for ErrorKind {
257
    fn from(message: &'static str) -> Self {
258
        Self::message(message)
259
    }
260
}
261

            
262
impl From<flume::RecvError> for ErrorKind {
263
    fn from(_err: flume::RecvError) -> Self {
264
        Self::Internal(InternalError::InternalCommunication)
265
    }
266
}
267

            
268
impl<T> From<flume::SendError<T>> for ErrorKind {
269
    fn from(_err: flume::SendError<T>) -> Self {
270
        Self::Internal(InternalError::InternalCommunication)
271
    }
272
}
273

            
274
impl From<String> for ErrorKind {
275
    fn from(message: String) -> Self {
276
        Self::message(message)
277
    }
278
}
279

            
280
/// An internal database error.
281
#[derive(Debug, Error)]
282
pub enum InternalError {
283
    /// A b-tree header was too large.
284
    #[error("the b-tree header is too large")]
285
    HeaderTooLarge,
286
    /// The transaction manager is no longer running.
287
    #[error("the transaction manager has stopped")]
288
    TransactionManagerStopped,
289
    /// An internal error communicating over a channel has ocurred.
290
    #[error("an error on an internal channel has occurred")]
291
    InternalCommunication,
292
    /// An unexpected byte length was encountered.
293
    #[error("an unexpected byte length was encountered")]
294
    IncorrectByteLength,
295
}