1use std::{ffi::c_int, result};
2
3use serde::{Deserialize, Serialize};
4
5pub type Result<T> = result::Result<T, Error>;
7
8#[derive(Serialize, Deserialize, Debug, thiserror::Error, Clone, Copy, PartialEq, Eq)]
10pub enum Error {
11 #[error("key/data pair already exists")]
13 KeyExist,
14 #[error("no matching key/data pair found")]
16 NotFound,
17 #[error("the cursor is already at the end of data")]
19 NoData,
20 #[error("requested page not found")]
22 PageNotFound,
23 #[error("database is corrupted")]
25 Corrupted,
26 #[error("fatal environment error")]
28 Panic,
29 #[error("DB version mismatch")]
31 VersionMismatch,
32 #[error("file is not an MDBX file")]
34 Invalid,
35 #[error("environment map size limit reached")]
37 MapFull,
38 #[error("too many DBI-handles (maxdbs reached)")]
40 DbsFull,
41 #[error("too many readers (maxreaders reached)")]
43 ReadersFull,
44 #[error("transaction has too many dirty pages (i.e., the transaction is too big)")]
46 TxnFull,
47 #[error("cursor stack limit reached")]
49 CursorFull,
50 #[error("page has no more space")]
52 PageFull,
53 #[error("database engine was unable to extend mapping")]
63 UnableExtendMapSize,
64 #[error("environment or database is not compatible with the requested operation or flags")]
66 Incompatible,
67 #[error("invalid reuse of reader locktable slot")]
69 BadRslot,
70 #[error("transaction is not valid for requested operation")]
72 BadTxn,
73 #[error("invalid size or alignment of key or data for the target database")]
75 BadValSize,
76 #[error("the specified DBI-handle is invalid")]
78 BadDbi,
79 #[error("unexpected internal error")]
81 Problem,
82 #[error("another write transaction is running")]
84 Busy,
85 #[error("the specified key has more than one associated value")]
87 Multival,
88 #[error("wrong signature of a runtime object(s)")]
90 BadSignature,
91 #[error("database should be recovered, but cannot be done automatically since it's in read-only mode")]
94 WannaRecovery,
95 #[error("the given key value is mismatched to the current cursor position")]
97 KeyMismatch,
98 #[error("invalid parameter specified")]
100 DecodeError,
101 #[error("the environment opened in read-only")]
103 Access,
104 #[error("database is too large for the current system")]
106 TooLarge,
107 #[error("invalid parameter specified or active write transaction")]
111 DecodeErrorLenDiff,
112 #[error("nested transactions are not supported with WriteMap")]
116 NestedTransactionsUnsupportedWithWriteMap,
117 #[error("write transactions are not supported in read-only mode")]
120 WriteTransactionUnsupportedInReadOnlyMode,
121 #[error("read transaction has been timed out")]
123 ReadTransactionTimeout,
124 #[error("permission denied to setup database")]
126 Permission,
127 #[error("unknown error code: {0}")]
129 Other(i32),
130}
131
132impl Error {
133 pub const fn from_err_code(err_code: c_int) -> Self {
135 match err_code {
136 ffi::MDBX_KEYEXIST => Self::KeyExist,
137 ffi::MDBX_NOTFOUND => Self::NotFound,
138 ffi::MDBX_ENODATA => Self::NoData,
139 ffi::MDBX_PAGE_NOTFOUND => Self::PageNotFound,
140 ffi::MDBX_CORRUPTED => Self::Corrupted,
141 ffi::MDBX_PANIC => Self::Panic,
142 ffi::MDBX_VERSION_MISMATCH => Self::VersionMismatch,
143 ffi::MDBX_INVALID => Self::Invalid,
144 ffi::MDBX_MAP_FULL => Self::MapFull,
145 ffi::MDBX_DBS_FULL => Self::DbsFull,
146 ffi::MDBX_READERS_FULL => Self::ReadersFull,
147 ffi::MDBX_TXN_FULL => Self::TxnFull,
148 ffi::MDBX_CURSOR_FULL => Self::CursorFull,
149 ffi::MDBX_PAGE_FULL => Self::PageFull,
150 ffi::MDBX_UNABLE_EXTEND_MAPSIZE => Self::UnableExtendMapSize,
151 ffi::MDBX_INCOMPATIBLE => Self::Incompatible,
152 ffi::MDBX_BAD_RSLOT => Self::BadRslot,
153 ffi::MDBX_BAD_TXN => Self::BadTxn,
154 ffi::MDBX_BAD_VALSIZE => Self::BadValSize,
155 ffi::MDBX_BAD_DBI => Self::BadDbi,
156 ffi::MDBX_PROBLEM => Self::Problem,
157 ffi::MDBX_BUSY => Self::Busy,
158 ffi::MDBX_EMULTIVAL => Self::Multival,
159 ffi::MDBX_WANNA_RECOVERY => Self::WannaRecovery,
160 ffi::MDBX_EKEYMISMATCH => Self::KeyMismatch,
161 ffi::MDBX_EINVAL => Self::DecodeError,
162 ffi::MDBX_EACCESS => Self::Access,
163 ffi::MDBX_TOO_LARGE => Self::TooLarge,
164 ffi::MDBX_EBADSIGN => Self::BadSignature,
165 ffi::MDBX_EPERM => Self::Permission,
166 other => Self::Other(other),
167 }
168 }
169
170 pub const fn to_err_code(&self) -> i32 {
172 match self {
173 Self::KeyExist => ffi::MDBX_KEYEXIST,
174 Self::NotFound => ffi::MDBX_NOTFOUND,
175 Self::NoData => ffi::MDBX_ENODATA,
176 Self::PageNotFound => ffi::MDBX_PAGE_NOTFOUND,
177 Self::Corrupted => ffi::MDBX_CORRUPTED,
178 Self::Panic => ffi::MDBX_PANIC,
179 Self::VersionMismatch => ffi::MDBX_VERSION_MISMATCH,
180 Self::Invalid => ffi::MDBX_INVALID,
181 Self::MapFull => ffi::MDBX_MAP_FULL,
182 Self::DbsFull => ffi::MDBX_DBS_FULL,
183 Self::ReadersFull => ffi::MDBX_READERS_FULL,
184 Self::TxnFull => ffi::MDBX_TXN_FULL,
185 Self::CursorFull => ffi::MDBX_CURSOR_FULL,
186 Self::PageFull => ffi::MDBX_PAGE_FULL,
187 Self::UnableExtendMapSize => ffi::MDBX_UNABLE_EXTEND_MAPSIZE,
188 Self::Incompatible => ffi::MDBX_INCOMPATIBLE,
189 Self::BadRslot => ffi::MDBX_BAD_RSLOT,
190 Self::BadTxn => ffi::MDBX_BAD_TXN,
191 Self::BadValSize => ffi::MDBX_BAD_VALSIZE,
192 Self::BadDbi => ffi::MDBX_BAD_DBI,
193 Self::Problem => ffi::MDBX_PROBLEM,
194 Self::Busy => ffi::MDBX_BUSY,
195 Self::Multival => ffi::MDBX_EMULTIVAL,
196 Self::WannaRecovery => ffi::MDBX_WANNA_RECOVERY,
197 Self::KeyMismatch => ffi::MDBX_EKEYMISMATCH,
198 Self::DecodeErrorLenDiff | Self::DecodeError => ffi::MDBX_EINVAL,
199 Self::TooLarge => ffi::MDBX_TOO_LARGE,
200 Self::BadSignature => ffi::MDBX_EBADSIGN,
201 Self::Access
202 | Self::WriteTransactionUnsupportedInReadOnlyMode
203 | Self::NestedTransactionsUnsupportedWithWriteMap => ffi::MDBX_EACCESS,
204 Self::ReadTransactionTimeout => -96000, Self::Permission => ffi::MDBX_EPERM,
206 Self::Other(err_code) => *err_code,
207 }
208 }
209}
210
211impl From<Error> for i32 {
212 fn from(value: Error) -> Self {
213 value.to_err_code()
214 }
215}
216
217#[inline]
218pub(crate) const fn mdbx_result(err_code: c_int) -> Result<bool> {
219 match err_code {
220 ffi::MDBX_SUCCESS => Ok(false),
221 ffi::MDBX_RESULT_TRUE => Ok(true),
222 other => Err(Error::from_err_code(other)),
223 }
224}
225
226#[macro_export]
227macro_rules! mdbx_try_optional {
228 ($expr:expr) => {{
229 match $expr {
230 Err(Error::NotFound | Error::NoData) => return Ok(None),
231 Err(e) => return Err(e),
232 Ok(v) => v,
233 }
234 }};
235}
236
237#[cfg(test)]
238mod tests {
239 use super::*;
240
241 #[test]
242 fn test_description() {
243 assert_eq!(
244 "the environment opened in read-only",
245 Error::from_err_code(13).to_string()
246 );
247
248 assert_eq!("file is not an MDBX file", Error::Invalid.to_string());
249 }
250
251 #[test]
252 fn test_conversion() {
253 assert_eq!(Error::from_err_code(ffi::MDBX_KEYEXIST), Error::KeyExist);
254 }
255}