use alloc::{
boxed::Box,
format,
string::{String, ToString},
vec::Vec,
};
use core::{
fmt,
fmt::{Debug, Display},
str::FromStr,
};
#[derive(Clone, Debug, PartialEq, Eq, derive_more::Display)]
pub enum DatabaseError {
#[display("failed to open the database: {_0}")]
Open(DatabaseErrorInfo),
#[display("failed to create a table: {_0}")]
CreateTable(DatabaseErrorInfo),
Write(Box<DatabaseWriteError>),
#[display("failed to read a value from a database table: {_0}")]
Read(DatabaseErrorInfo),
#[display("database delete error code: {_0}")]
Delete(DatabaseErrorInfo),
#[display("failed to commit transaction changes: {_0}")]
Commit(DatabaseErrorInfo),
#[display("failed to initialize a transaction: {_0}")]
InitTx(DatabaseErrorInfo),
#[display("failed to initialize a cursor: {_0}")]
InitCursor(DatabaseErrorInfo),
#[display("failed to decode a key from a table")]
Decode,
#[display("failed to get stats: {_0}")]
Stats(DatabaseErrorInfo),
#[display("log level {_0:?} is not available")]
LogLevelUnavailable(LogLevel),
#[display("{_0}")]
Other(String),
}
impl core::error::Error for DatabaseError {
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
match self {
Self::Write(err) => core::error::Error::source(err),
_ => Option::None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, derive_more::Display)]
#[display("{message} ({code})")]
pub struct DatabaseErrorInfo {
pub message: String,
pub code: i32,
}
impl<E> From<E> for DatabaseErrorInfo
where
E: Display + Into<i32>,
{
#[inline]
fn from(error: E) -> Self {
Self { message: error.to_string(), code: error.into() }
}
}
impl From<DatabaseWriteError> for DatabaseError {
#[inline]
fn from(error: DatabaseWriteError) -> Self {
Self::Write(Box::new(error))
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct DatabaseWriteError {
pub info: DatabaseErrorInfo,
pub operation: DatabaseWriteOperation,
pub table_name: &'static str,
pub key: Vec<u8>,
}
impl fmt::Display for DatabaseWriteError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"write operation {:?} failed for key \"{}\" in table {}: {}",
self.operation,
alloy_primitives::hex::encode(&self.key),
self.table_name,
self.info
)
}
}
impl core::error::Error for DatabaseWriteError {}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum DatabaseWriteOperation {
CursorAppend,
CursorUpsert,
CursorInsert,
CursorAppendDup,
Put,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum LogLevel {
Fatal,
Error,
Warn,
Notice,
Verbose,
Debug,
Trace,
Extra,
}
impl LogLevel {
pub const fn value_variants() -> &'static [Self] {
&[
Self::Fatal,
Self::Error,
Self::Warn,
Self::Notice,
Self::Verbose,
Self::Debug,
Self::Trace,
Self::Extra,
]
}
pub const fn variant_name(&self) -> &'static str {
match self {
Self::Fatal => "fatal",
Self::Error => "error",
Self::Warn => "warn",
Self::Notice => "notice",
Self::Verbose => "verbose",
Self::Debug => "debug",
Self::Trace => "trace",
Self::Extra => "extra",
}
}
pub const fn help_message(&self) -> &'static str {
match self {
Self::Fatal => "Enables logging for critical conditions, i.e. assertion failures",
Self::Error => "Enables logging for error conditions",
Self::Warn => "Enables logging for warning conditions",
Self::Notice => "Enables logging for normal but significant condition",
Self::Verbose => "Enables logging for verbose informational",
Self::Debug => "Enables logging for debug-level messages",
Self::Trace => "Enables logging for trace debug-level messages",
Self::Extra => "Enables logging for extra debug-level messages",
}
}
}
impl FromStr for LogLevel {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"fatal" => Ok(Self::Fatal),
"error" => Ok(Self::Error),
"warn" => Ok(Self::Warn),
"notice" => Ok(Self::Notice),
"verbose" => Ok(Self::Verbose),
"debug" => Ok(Self::Debug),
"trace" => Ok(Self::Trace),
"extra" => Ok(Self::Extra),
_ => Err(format!("Invalid log level: {s}")),
}
}
}