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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
use std::path::{Path, PathBuf};
use thiserror::Error;
/// Crate-wide result type.
pub type Result<T> = std::result::Result<T, LevelDbError>;
/// Stable category for a [`LevelDbError`].
///
/// Prefer matching this enum or [`LevelDbError::path`] in application code
/// instead of parsing the human-readable [`std::fmt::Display`] output.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum ErrorKind {
/// Filesystem or operating-system I/O failed.
Io,
/// On-disk bytes were malformed, truncated, or failed validation.
Corruption,
/// Caller-supplied options or inputs were invalid.
InvalidArgument,
/// A requested codec or behavior is disabled or not implemented.
Unsupported,
/// Compression or decompression failed.
Compression,
/// A scan observed a caller-supplied cancellation flag.
Cancelled,
/// A mutating operation was requested from a read-only handle.
ReadOnly,
/// Opening failed because the target already existed.
AlreadyExists,
/// A database directory or required metadata file was missing.
NotFound,
/// An internal synchronization primitive was poisoned.
LockPoisoned,
/// The optional async wrapper failed to join a blocking task.
Join,
}
/// Errors returned while opening, reading, writing, or repairing a database.
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum LevelDbError {
/// Filesystem or OS I/O failure.
#[error("I/O error while {operation}{}: {source}", path_suffix(path.as_deref()))]
Io {
/// Operation being performed when the error occurred.
operation: &'static str,
/// Filesystem path involved in the operation, when known.
path: Option<PathBuf>,
/// Underlying operating-system error.
#[source]
source: std::io::Error,
},
/// On-disk data was malformed or failed validation.
#[error("corrupt database{}: {message}", path_suffix(path.as_deref()))]
Corruption {
/// Path to the file whose bytes failed validation, when known.
path: Option<PathBuf>,
/// Human-readable corruption reason.
message: String,
},
/// Caller supplied invalid options or input.
#[error("invalid argument: {message}")]
InvalidArgument {
/// Human-readable validation failure.
message: String,
},
/// The requested feature is disabled or not implemented.
#[error("unsupported feature {feature}: {message}")]
Unsupported {
/// Feature, codec, or behavior that is unavailable.
feature: &'static str,
/// Human-readable explanation.
message: String,
},
/// Compression or decompression failed.
#[error("compression error in {codec}: {message}")]
Compression {
/// Codec or table compression family being processed.
codec: &'static str,
/// Human-readable codec error.
message: String,
},
/// Scan cancelled through [`crate::ScanCancelFlag`].
#[error("scan was cancelled")]
Cancelled,
/// A mutating operation was requested on a read-only database.
#[error("database is read-only")]
ReadOnly,
/// `OpenOptions::error_if_exists` rejected an existing database.
#[error("database already exists: {}", path.display())]
AlreadyExists {
/// Existing database directory.
path: PathBuf,
},
/// The requested database or required metadata file was missing.
#[error("database not found: {}", path.display())]
NotFound {
/// Missing database directory or metadata file.
path: PathBuf,
},
/// An internal lock was poisoned.
#[error("lock poisoned while {operation}")]
LockPoisoned {
/// Locking operation that observed poisoning.
operation: &'static str,
},
/// A blocking task failed to join in the optional async wrapper.
#[error("async runtime error: {message}")]
Join {
/// Human-readable join failure.
message: String,
},
}
impl LevelDbError {
/// Returns the stable category of this error.
#[must_use]
pub const fn kind(&self) -> ErrorKind {
match self {
Self::Io { .. } => ErrorKind::Io,
Self::Corruption { .. } => ErrorKind::Corruption,
Self::InvalidArgument { .. } => ErrorKind::InvalidArgument,
Self::Unsupported { .. } => ErrorKind::Unsupported,
Self::Compression { .. } => ErrorKind::Compression,
Self::Cancelled => ErrorKind::Cancelled,
Self::ReadOnly => ErrorKind::ReadOnly,
Self::AlreadyExists { .. } => ErrorKind::AlreadyExists,
Self::NotFound { .. } => ErrorKind::NotFound,
Self::LockPoisoned { .. } => ErrorKind::LockPoisoned,
Self::Join { .. } => ErrorKind::Join,
}
}
/// Returns the filesystem path associated with this error, when known.
#[must_use]
pub fn path(&self) -> Option<&Path> {
match self {
Self::Io { path, .. } | Self::Corruption { path, .. } => path.as_deref(),
Self::AlreadyExists { path } | Self::NotFound { path } => Some(path),
Self::InvalidArgument { .. }
| Self::Unsupported { .. }
| Self::Compression { .. }
| Self::Cancelled
| Self::ReadOnly
| Self::LockPoisoned { .. }
| Self::Join { .. } => None,
}
}
pub(crate) fn io(
operation: &'static str,
path: impl Into<Option<PathBuf>>,
source: std::io::Error,
) -> Self {
Self::Io {
operation,
path: path.into(),
source,
}
}
pub(crate) fn io_at(
operation: &'static str,
path: impl Into<PathBuf>,
source: std::io::Error,
) -> Self {
Self::io(operation, Some(path.into()), source)
}
pub(crate) fn corruption(message: impl Into<String>) -> Self {
Self::Corruption {
path: None,
message: message.into(),
}
}
pub(crate) fn corruption_at(path: impl Into<PathBuf>, message: impl Into<String>) -> Self {
Self::Corruption {
path: Some(path.into()),
message: message.into(),
}
}
pub(crate) fn invalid_argument(message: impl Into<String>) -> Self {
Self::InvalidArgument {
message: message.into(),
}
}
#[allow(
dead_code,
reason = "used by codec functions when optional features are disabled"
)]
pub(crate) fn unsupported(feature: &'static str, message: impl Into<String>) -> Self {
Self::Unsupported {
feature,
message: message.into(),
}
}
pub(crate) fn compression(codec: &'static str, message: impl Into<String>) -> Self {
Self::Compression {
codec,
message: message.into(),
}
}
pub(crate) fn already_exists(path: impl Into<PathBuf>) -> Self {
Self::AlreadyExists { path: path.into() }
}
pub(crate) fn not_found(path: impl Into<PathBuf>) -> Self {
Self::NotFound { path: path.into() }
}
pub(crate) const fn lock_poisoned(operation: &'static str) -> Self {
Self::LockPoisoned { operation }
}
#[cfg(feature = "async")]
pub(crate) fn join(message: impl Into<String>) -> Self {
Self::Join {
message: message.into(),
}
}
}
impl From<std::io::Error> for LevelDbError {
fn from(source: std::io::Error) -> Self {
Self::io("perform filesystem I/O", None, source)
}
}
fn path_suffix(path: Option<&Path>) -> String {
path.map(|path| format!(" at {}", path.display()))
.unwrap_or_default()
}