use std::fmt;
use std::ffi::{CStr, NulError};
use bincode::Error as BincodeError;
use gdbm_sys;
type ErrorCode = u32;
#[derive(Debug)]
pub enum GdbmError {
InvalidDatabase,
NeedsRecovery,
UnexpectedEOF,
Other(ErrorCode),
}
#[derive(Debug)]
pub enum Error {
InvalidPath,
KeyExists,
NoRecord,
Bincode(BincodeError),
Internal(GdbmError),
}
pub type GdbmResult<T> = Result<T, Error>;
impl GdbmError {
pub (crate) fn from_last() -> Self {
last_errno().into()
}
}
impl Error {
pub (crate) fn from_last() -> Self {
last_errno().into()
}
pub fn is_no_record(&self) -> bool {
match *self {
Error::NoRecord => true,
_ => false
}
}
}
#[doc(hidden)]
impl fmt::Display for GdbmError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
GdbmError::InvalidDatabase => write!(f, "The file is not a valid database"),
GdbmError::NeedsRecovery => write!(f, "The database is corrupted"),
GdbmError::UnexpectedEOF => write!(f, "Unexpected EOF in database"),
GdbmError::Other(code) => unsafe {
let err_ptr = gdbm_sys::gdbm_strerror(code as i32);
let err_string = CStr::from_ptr(err_ptr);
write!(f, "{}", err_string.to_string_lossy())
},
}
}
}
#[doc(hidden)]
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::Internal(ref e) => e.fmt(f),
Error::InvalidPath => write!(f, "Invalid path (interior null byte)"),
Error::KeyExists => write!(f, "key already exists in database"),
Error::NoRecord => write!(f, "key does not exist in database"),
Error::Bincode(ref e) => e.fmt(f),
}
}
}
impl std::error::Error for Error {}
#[doc(hidden)]
impl From<u32> for Error {
fn from(src: u32) -> Error {
match src {
errno if errno == gdbm_sys::GDBM_ITEM_NOT_FOUND => Error::NoRecord,
errno => Error::Internal(errno.into()),
}
}
}
#[doc(hidden)]
impl From<u32> for GdbmError {
fn from(src: u32) -> GdbmError {
match src {
errno if errno == gdbm_sys::GDBM_EMPTY_DATABASE => GdbmError::InvalidDatabase,
errno if errno == gdbm_sys::GDBM_NEED_RECOVERY => GdbmError::NeedsRecovery,
other => GdbmError::Other(other),
}
}
}
#[doc(hidden)]
impl From<NulError> for Error {
fn from(_src: NulError) -> Error {
Error::InvalidPath
}
}
#[doc(hidden)]
impl From<GdbmError> for Error {
fn from(src: GdbmError) -> Error {
Error::Internal(src)
}
}
#[doc(hidden)]
impl From<BincodeError> for Error {
fn from(src: BincodeError) -> Error {
Error::Bincode(src)
}
}
#[doc(hidden)]
pub fn last_errno() -> u32 {
unsafe {
let err_loc = gdbm_sys::gdbm_errno_location();
*err_loc as u32
}
}