aingle_lmdb/
error.rs

1use libc::c_int;
2use std::ffi::CStr;
3use std::os::raw::c_char;
4use std::{
5    fmt,
6    result,
7    str,
8};
9
10use crate::ffi;
11
12/// An LMDB error kind.
13#[derive(Debug, Eq, PartialEq, Copy, Clone)]
14pub enum Error {
15    /// key/data pair already exists.
16    KeyExist,
17    /// key/data pair not found (EOF).
18    NotFound,
19    /// Requested page not found - this usually indicates corruption.
20    PageNotFound,
21    /// Located page was wrong type.
22    Corrupted,
23    /// Update of meta page failed or environment had fatal error.
24    Panic,
25    /// Environment version mismatch.
26    VersionMismatch,
27    /// File is not a valid LMDB file.
28    Invalid,
29    /// Environment mapsize reached.
30    MapFull,
31    /// Environment maxdbs reached.
32    DbsFull,
33    /// Environment maxreaders reached.
34    ReadersFull,
35    /// Too many TLS keys in use - Windows only.
36    TlsFull,
37    /// Txn has too many dirty pages.
38    TxnFull,
39    /// Cursor stack too deep - internal error.
40    CursorFull,
41    /// Page has not enough space - internal error.
42    PageFull,
43    /// Database contents grew beyond environment mapsize.
44    MapResized,
45    /// MDB_Incompatible: Operation and DB incompatible, or DB flags changed.
46    Incompatible,
47    /// Invalid reuse of reader locktable slot.
48    BadRslot,
49    /// Transaction cannot recover - it must be aborted.
50    BadTxn,
51    /// Unsupported size of key/DB name/data, or wrong DUP_FIXED size.
52    BadValSize,
53    /// The specified DBI was changed unexpectedly.
54    BadDbi,
55    /// Other error.
56    Other(c_int),
57}
58
59impl Error {
60    /// Converts a raw error code to an `Error`.
61    pub fn from_err_code(err_code: c_int) -> Error {
62        match err_code {
63            ffi::MDB_KEYEXIST => Error::KeyExist,
64            ffi::MDB_NOTFOUND => Error::NotFound,
65            ffi::MDB_PAGE_NOTFOUND => Error::PageNotFound,
66            ffi::MDB_CORRUPTED => Error::Corrupted,
67            ffi::MDB_PANIC => Error::Panic,
68            ffi::MDB_VERSION_MISMATCH => Error::VersionMismatch,
69            ffi::MDB_INVALID => Error::Invalid,
70            ffi::MDB_MAP_FULL => Error::MapFull,
71            ffi::MDB_DBS_FULL => Error::DbsFull,
72            ffi::MDB_READERS_FULL => Error::ReadersFull,
73            ffi::MDB_TLS_FULL => Error::TlsFull,
74            ffi::MDB_TXN_FULL => Error::TxnFull,
75            ffi::MDB_CURSOR_FULL => Error::CursorFull,
76            ffi::MDB_PAGE_FULL => Error::PageFull,
77            ffi::MDB_MAP_RESIZED => Error::MapResized,
78            ffi::MDB_INCOMPATIBLE => Error::Incompatible,
79            ffi::MDB_BAD_RSLOT => Error::BadRslot,
80            ffi::MDB_BAD_TXN => Error::BadTxn,
81            ffi::MDB_BAD_VALSIZE => Error::BadValSize,
82            ffi::MDB_BAD_DBI => Error::BadDbi,
83            other => Error::Other(other),
84        }
85    }
86
87    /// Converts an `Error` to the raw error code.
88    #[allow(clippy::trivially_copy_pass_by_ref)]
89    pub fn to_err_code(&self) -> c_int {
90        match *self {
91            Error::KeyExist => ffi::MDB_KEYEXIST,
92            Error::NotFound => ffi::MDB_NOTFOUND,
93            Error::PageNotFound => ffi::MDB_PAGE_NOTFOUND,
94            Error::Corrupted => ffi::MDB_CORRUPTED,
95            Error::Panic => ffi::MDB_PANIC,
96            Error::VersionMismatch => ffi::MDB_VERSION_MISMATCH,
97            Error::Invalid => ffi::MDB_INVALID,
98            Error::MapFull => ffi::MDB_MAP_FULL,
99            Error::DbsFull => ffi::MDB_DBS_FULL,
100            Error::ReadersFull => ffi::MDB_READERS_FULL,
101            Error::TlsFull => ffi::MDB_TLS_FULL,
102            Error::TxnFull => ffi::MDB_TXN_FULL,
103            Error::CursorFull => ffi::MDB_CURSOR_FULL,
104            Error::PageFull => ffi::MDB_PAGE_FULL,
105            Error::MapResized => ffi::MDB_MAP_RESIZED,
106            Error::Incompatible => ffi::MDB_INCOMPATIBLE,
107            Error::BadRslot => ffi::MDB_BAD_RSLOT,
108            Error::BadTxn => ffi::MDB_BAD_TXN,
109            Error::BadValSize => ffi::MDB_BAD_VALSIZE,
110            Error::BadDbi => ffi::MDB_BAD_DBI,
111            Error::Other(err_code) => err_code,
112        }
113    }
114}
115
116impl fmt::Display for Error {
117    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
118        let result = unsafe {
119            // This is safe since the error messages returned from mdb_strerror are static.
120            let err: *const c_char = ffi::mdb_strerror(self.to_err_code()) as *const c_char;
121            str::from_utf8_unchecked(CStr::from_ptr(err).to_bytes())
122        };
123        write!(fmt, "{}", result)
124    }
125}
126
127impl std::error::Error for Error {}
128
129/// An LMDB result.
130pub type Result<T> = result::Result<T, Error>;
131
132pub fn lmdb_result(err_code: c_int) -> Result<()> {
133    if err_code == ffi::MDB_SUCCESS {
134        Ok(())
135    } else {
136        Err(Error::from_err_code(err_code))
137    }
138}
139
140#[cfg(test)]
141mod test {
142
143    use super::*;
144
145    #[test]
146    fn test_description() {
147        assert_eq!("Permission denied", Error::from_err_code(13).to_string());
148        assert_eq!("MDB_NOTFOUND: No matching key/data pair found", Error::NotFound.to_string());
149    }
150}