1use std::path::{Path, PathBuf};
2use thiserror::Error;
3
4pub type Result<T> = std::result::Result<T, LevelDbError>;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12#[non_exhaustive]
13pub enum ErrorKind {
14 Io,
16 Corruption,
18 InvalidArgument,
20 Unsupported,
22 Compression,
24 Cancelled,
26 ReadOnly,
28 AlreadyExists,
30 NotFound,
32 LockPoisoned,
34 Join,
36}
37
38#[derive(Debug, Error)]
40#[non_exhaustive]
41pub enum LevelDbError {
42 #[error("I/O error while {operation}{}: {source}", path_suffix(path.as_deref()))]
44 Io {
45 operation: &'static str,
47 path: Option<PathBuf>,
49 #[source]
51 source: std::io::Error,
52 },
53 #[error("corrupt database{}: {message}", path_suffix(path.as_deref()))]
55 Corruption {
56 path: Option<PathBuf>,
58 message: String,
60 },
61 #[error("invalid argument: {message}")]
63 InvalidArgument {
64 message: String,
66 },
67 #[error("unsupported feature {feature}: {message}")]
69 Unsupported {
70 feature: &'static str,
72 message: String,
74 },
75 #[error("compression error in {codec}: {message}")]
77 Compression {
78 codec: &'static str,
80 message: String,
82 },
83 #[error("scan was cancelled")]
85 Cancelled,
86 #[error("database is read-only")]
88 ReadOnly,
89 #[error("database already exists: {}", path.display())]
91 AlreadyExists {
92 path: PathBuf,
94 },
95 #[error("database not found: {}", path.display())]
97 NotFound {
98 path: PathBuf,
100 },
101 #[error("lock poisoned while {operation}")]
103 LockPoisoned {
104 operation: &'static str,
106 },
107 #[error("async runtime error: {message}")]
109 Join {
110 message: String,
112 },
113}
114
115impl LevelDbError {
116 #[must_use]
118 pub const fn kind(&self) -> ErrorKind {
119 match self {
120 Self::Io { .. } => ErrorKind::Io,
121 Self::Corruption { .. } => ErrorKind::Corruption,
122 Self::InvalidArgument { .. } => ErrorKind::InvalidArgument,
123 Self::Unsupported { .. } => ErrorKind::Unsupported,
124 Self::Compression { .. } => ErrorKind::Compression,
125 Self::Cancelled => ErrorKind::Cancelled,
126 Self::ReadOnly => ErrorKind::ReadOnly,
127 Self::AlreadyExists { .. } => ErrorKind::AlreadyExists,
128 Self::NotFound { .. } => ErrorKind::NotFound,
129 Self::LockPoisoned { .. } => ErrorKind::LockPoisoned,
130 Self::Join { .. } => ErrorKind::Join,
131 }
132 }
133
134 #[must_use]
136 pub fn path(&self) -> Option<&Path> {
137 match self {
138 Self::Io { path, .. } | Self::Corruption { path, .. } => path.as_deref(),
139 Self::AlreadyExists { path } | Self::NotFound { path } => Some(path),
140 Self::InvalidArgument { .. }
141 | Self::Unsupported { .. }
142 | Self::Compression { .. }
143 | Self::Cancelled
144 | Self::ReadOnly
145 | Self::LockPoisoned { .. }
146 | Self::Join { .. } => None,
147 }
148 }
149
150 pub(crate) fn io(
151 operation: &'static str,
152 path: impl Into<Option<PathBuf>>,
153 source: std::io::Error,
154 ) -> Self {
155 Self::Io {
156 operation,
157 path: path.into(),
158 source,
159 }
160 }
161
162 pub(crate) fn io_at(
163 operation: &'static str,
164 path: impl Into<PathBuf>,
165 source: std::io::Error,
166 ) -> Self {
167 Self::io(operation, Some(path.into()), source)
168 }
169
170 pub(crate) fn corruption(message: impl Into<String>) -> Self {
171 Self::Corruption {
172 path: None,
173 message: message.into(),
174 }
175 }
176
177 pub(crate) fn corruption_at(path: impl Into<PathBuf>, message: impl Into<String>) -> Self {
178 Self::Corruption {
179 path: Some(path.into()),
180 message: message.into(),
181 }
182 }
183
184 pub(crate) fn invalid_argument(message: impl Into<String>) -> Self {
185 Self::InvalidArgument {
186 message: message.into(),
187 }
188 }
189
190 #[allow(
191 dead_code,
192 reason = "used by codec functions when optional features are disabled"
193 )]
194 pub(crate) fn unsupported(feature: &'static str, message: impl Into<String>) -> Self {
195 Self::Unsupported {
196 feature,
197 message: message.into(),
198 }
199 }
200
201 pub(crate) fn compression(codec: &'static str, message: impl Into<String>) -> Self {
202 Self::Compression {
203 codec,
204 message: message.into(),
205 }
206 }
207
208 pub(crate) fn already_exists(path: impl Into<PathBuf>) -> Self {
209 Self::AlreadyExists { path: path.into() }
210 }
211
212 pub(crate) fn not_found(path: impl Into<PathBuf>) -> Self {
213 Self::NotFound { path: path.into() }
214 }
215
216 pub(crate) const fn lock_poisoned(operation: &'static str) -> Self {
217 Self::LockPoisoned { operation }
218 }
219
220 #[cfg(feature = "async")]
221 pub(crate) fn join(message: impl Into<String>) -> Self {
222 Self::Join {
223 message: message.into(),
224 }
225 }
226}
227
228impl From<std::io::Error> for LevelDbError {
229 fn from(source: std::io::Error) -> Self {
230 Self::io("perform filesystem I/O", None, source)
231 }
232}
233
234fn path_suffix(path: Option<&Path>) -> String {
235 path.map(|path| format!(" at {}", path.display()))
236 .unwrap_or_default()
237}