lmdb_zero/
error.rs

1// Copyright 2016 FullContact, Inc
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9//! Error values and types returned by LMDB and this wrapper.
10
11use std::error::Error as StdError;
12use std::ffi::{CStr, NulError};
13use std::fmt;
14use std::result;
15use libc::c_int;
16
17use ffi;
18use ffi2;
19
20/// key/data pair already exists
21pub const KEYEXIST: c_int = ffi::MDB_KEYEXIST;
22/// key/data pair not found (EOF)
23pub const NOTFOUND: c_int = ffi::MDB_NOTFOUND;
24/// Requested page not found - this usually indicates corruption
25pub const PAGE_NOTFOUND: c_int = ffi::MDB_PAGE_NOTFOUND;
26/// Located page was wrong type
27pub const CORRUPTED: c_int = ffi::MDB_CORRUPTED;
28/// Update of meta page failed or environment had fatal error
29pub const PANIC: c_int = ffi::MDB_PANIC;
30/// Environment version mismatch
31pub const VERSION_MISMATCH: c_int = ffi::MDB_VERSION_MISMATCH;
32/// File is not a valid LMDB file
33pub const INVALID: c_int = ffi::MDB_INVALID;
34/// Environment mapsize reached
35pub const MAP_FULL: c_int = ffi::MDB_MAP_FULL;
36/// Environment maxdbs reached
37pub const DBS_FULL: c_int = ffi::MDB_DBS_FULL;
38/// Environment maxreaders reached
39pub const READERS_FULL: c_int = ffi::MDB_READERS_FULL;
40/// Too many TLS keys in use - Windows only
41pub const TLS_FULL: c_int = ffi::MDB_TLS_FULL;
42/// Txn has too many dirty pages
43pub const TXN_FULL: c_int = ffi::MDB_TXN_FULL;
44/// Cursor stack too deep - internal error
45pub const CURSOR_FULL: c_int = ffi::MDB_CURSOR_FULL;
46/// Page has not enough space - internal error
47pub const PAGE_FULL: c_int = ffi::MDB_PAGE_FULL;
48/// Database contents grew beyond environment mapsize
49pub const MAP_RESIZED: c_int = ffi::MDB_MAP_RESIZED;
50/// Operation and DB incompatible, or DB type changed. This can mean:
51///
52/// - The operation expects an `DUPSORT` / `DUPFIXED` database.
53/// - Opening a named DB when the unnamed DB has `DUPSORT` / `INTEGERKEY`.
54/// - Accessing a data record as a database, or vice versa.
55/// - The database was dropped and recreated with different flags.
56pub const INCOMPATIBLE: c_int = ffi::MDB_INCOMPATIBLE;
57/// Invalid reuse of reader locktable slot
58pub const BAD_RSLOT: c_int = ffi::MDB_BAD_RSLOT;
59/// Transaction must abort, has a child, or is invalid
60pub const BAD_TXN: c_int = ffi::MDB_BAD_TXN;
61/// Unsupported size of key/DB name/data, or wrong `DUPFIXED` size
62pub const BAD_VALSIZE: c_int = ffi::MDB_BAD_VALSIZE;
63/// The specified DBI was changed unexpectedly
64pub const BAD_DBI: c_int = ffi2::MDB_BAD_DBI;
65
66/// Error type returned by LMDB.
67#[derive(Clone,PartialEq,Eq,Hash)]
68pub enum Error {
69    /// A basic error code returned by LMDB.
70    ///
71    /// The code is generally expected to be a constant defined in the `errors`
72    /// module if negative, or a raw platform error code if positive.
73    Code(c_int),
74    /// A string path was given which contains a `NUL` byte.
75    NulStr,
76    /// An attempt was made to open a database which is already open.
77    Reopened,
78    /// An attempt was made to use two items together which cannot be used
79    /// together.
80    ///
81    /// For example, trying to use a cursor from one transaction to access data
82    /// in another.
83    Mismatch,
84    /// A value conversion was rejected. A message explaining why is included.
85    ValRejected(String),
86    // Prevent external code from exhaustively matching on this enum.
87    #[doc(hidden)]
88    _NonExhaustive
89}
90
91/// Result type returned for all calls that can fail.
92pub type Result<T> = result::Result<T, Error>;
93
94impl Error {
95    fn strerror(&self) -> &'static str {
96        match *self {
97            Error::NulStr => "NUL byte in path",
98            Error::Reopened => "Attempt to reopen database",
99            Error::Mismatch =>
100                "Items from different env/database used together",
101            Error::ValRejected(..) =>
102                "Value conversion failed",
103            Error::_NonExhaustive => "Error::_NonExhaustive",
104            Error::Code(code) => unsafe {
105                let raw = ffi::mdb_strerror(code);
106                if raw.is_null() {
107                    "(null)"
108                } else {
109                    CStr::from_ptr(raw).to_str().unwrap_or("(unknown)")
110                }
111            },
112        }
113    }
114}
115
116impl fmt::Debug for Error {
117    fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
118        match *self {
119            Error::NulStr =>
120                write!(f, "Error::NulStr"),
121            Error::Reopened =>
122                write!(f, "Error::Reopened"),
123            Error::Mismatch =>
124                write!(f, "Error::Mismatch"),
125            Error::ValRejected(ref why) =>
126                write!(f, "Error::ValRejected({:?})", why),
127            Error::Code(code) =>
128                write!(f, "Error::Code({}, '{}')", code, self.strerror()),
129            Error::_NonExhaustive =>
130                write!(f, "Error::_NonExhaustive"),
131        }
132    }
133}
134
135impl fmt::Display for Error {
136    fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
137        match *self {
138            Error::ValRejected(ref why) =>
139                write!(f, "Value conversion failed: {}", why),
140            _ => write!(f, "{}", self.strerror()),
141        }
142    }
143}
144
145impl StdError for Error {
146    fn description(&self) -> &str {
147        self.strerror()
148    }
149}
150
151impl From<NulError> for Error {
152    fn from(_: NulError) -> Self {
153        Error::NulStr
154    }
155}
156
157/// Extension methods for LMDB results
158pub trait LmdbResultExt {
159    #[allow(missing_docs)]
160    type Inner;
161
162    /// Lift "not found" errors to `None`.
163    ///
164    /// If `Ok(val)`, return `Ok(Some(val))`. If `Err` but the error is
165    /// `Error::Code(NOTFOUND)`, return `Ok(None)`. Otherwise, return self.
166    fn to_opt(self) -> Result<Option<Self::Inner>>;
167
168    /// Suppress `KEYEXIST` errors.
169    ///
170    /// If this is `Err` and the error is `Error::Code(KEYEXIST)`, switch to
171    /// `Ok` with the given inner value.
172    fn ignore_exists(self, inner: Self::Inner) -> Self;
173}
174
175impl<T> LmdbResultExt for Result<T> {
176    type Inner = T;
177
178    fn to_opt(self) -> Result<Option<T>> {
179        match self {
180            Ok(val) => Ok(Some(val)),
181            Err(Error::Code(code)) if NOTFOUND == code => Ok(None),
182            Err(error) => Err(error),
183        }
184    }
185
186    fn ignore_exists(self, inner: T) -> Self {
187        match self {
188            Ok(val) => Ok(val),
189            Err(Error::Code(code)) if KEYEXIST == code => Ok(inner),
190            Err(error) => Err(error),
191        }
192    }
193}