lmdb/
error.rs

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