use std::{convert::Infallible, fmt::Display};
use backtrace::Backtrace;
use parking_lot::{Mutex, MutexGuard};
use thiserror::Error;
use crate::AbortError;
#[derive(Debug)]
pub struct Error {
pub kind: ErrorKind,
backtrace: Mutex<Backtrace>,
}
impl Error {
pub(crate) fn data_integrity(error: impl Into<Self>) -> Self {
Self {
kind: ErrorKind::DataIntegrity(Box::new(error.into())),
backtrace: Mutex::new(Backtrace::new_unresolved()),
}
}
pub fn backtrace(&self) -> MutexGuard<'_, Backtrace> {
let mut backtrace = self.backtrace.lock();
backtrace.resolve();
backtrace
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.kind.source()
}
}
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.kind.fmt(f)?;
#[cfg(debug_assertions)]
{
f.write_str("\nstack backtrace:")?;
let mut backtrace = self.backtrace.lock();
backtrace.resolve();
for (index, frame) in backtrace.frames().iter().enumerate() {
write!(f, "\n#{}: {:?}", index, frame)?;
}
}
Ok(())
}
}
impl From<ErrorKind> for Error {
fn from(kind: ErrorKind) -> Self {
Self {
kind,
backtrace: Mutex::new(Backtrace::new_unresolved()),
}
}
}
impl From<AbortError<Infallible>> for Error {
fn from(ae: AbortError<Infallible>) -> Self {
ae.infallible()
}
}
impl From<std::io::Error> for Error {
fn from(err: std::io::Error) -> Self {
Self {
kind: ErrorKind::from(err),
backtrace: Mutex::new(Backtrace::new_unresolved()),
}
}
}
impl From<&'static str> for Error {
fn from(message: &'static str) -> Self {
Self {
kind: ErrorKind::message(message),
backtrace: Mutex::new(Backtrace::new_unresolved()),
}
}
}
impl From<flume::RecvError> for Error {
fn from(_err: flume::RecvError) -> Self {
Self {
kind: ErrorKind::Internal(InternalError::InternalCommunication),
backtrace: Mutex::new(Backtrace::new_unresolved()),
}
}
}
impl<T> From<flume::SendError<T>> for Error {
fn from(_err: flume::SendError<T>) -> Self {
Self {
kind: ErrorKind::Internal(InternalError::InternalCommunication),
backtrace: Mutex::new(Backtrace::new_unresolved()),
}
}
}
impl From<String> for Error {
fn from(message: String) -> Self {
Self {
kind: ErrorKind::message(message),
backtrace: Mutex::new(Backtrace::new_unresolved()),
}
}
}
#[derive(Debug, Error)]
#[error(transparent)]
pub enum ErrorKind {
#[error("{0}")]
Message(String),
#[error("io error: {0}")]
Io(#[from] std::io::Error),
#[error("an unrecoverable error with the data on disk has been found: {0}")]
DataIntegrity(Box<Error>),
#[error("tree name not valid")]
InvalidTreeName,
#[error("key too large")]
KeyTooLarge,
#[error("value too large")]
ValueTooLarge,
#[error("multi-key operation did not have its keys ordered")]
KeysNotOrdered,
#[error("an internal error occurred: {0}")]
Internal(InternalError),
#[error("the file has been compacted. reopen the file and try again")]
TreeCompacted,
#[error("a vault error occurred: {0}")]
Vault(Box<dyn SendSyncError>),
#[error("transaction pushed out of order")]
TransactionPushedOutOfOrder,
}
pub trait SendSyncError: std::error::Error + Send + Sync + 'static {}
impl<T> SendSyncError for T where T: std::error::Error + Send + Sync + 'static {}
impl ErrorKind {
pub(crate) fn message<S: Display>(message: S) -> Self {
Self::Message(message.to_string())
}
pub(crate) fn data_integrity(error: impl Into<Error>) -> Self {
Self::DataIntegrity(Box::new(error.into()))
}
}
impl From<&'static str> for ErrorKind {
fn from(message: &'static str) -> Self {
Self::message(message)
}
}
impl From<flume::RecvError> for ErrorKind {
fn from(_err: flume::RecvError) -> Self {
Self::Internal(InternalError::InternalCommunication)
}
}
impl<T> From<flume::SendError<T>> for ErrorKind {
fn from(_err: flume::SendError<T>) -> Self {
Self::Internal(InternalError::InternalCommunication)
}
}
impl From<String> for ErrorKind {
fn from(message: String) -> Self {
Self::message(message)
}
}
#[derive(Debug, Error)]
pub enum InternalError {
#[error("the b-tree header is too large")]
HeaderTooLarge,
#[error("the transaction manager has stopped")]
TransactionManagerStopped,
#[error("an error on an internal channel has occurred")]
InternalCommunication,
}