#[non_exhaustive]
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("invalid argument: {0}")]
InvalidArgument(String),
#[error("corruption: {0}")]
Corruption(#[source] std::io::Error),
#[error("database was opened read-only")]
ReadOnly,
#[error("database is closed")]
Closed,
#[error("invalid column family: {0}")]
InvalidColumnFamily(String),
#[error("engine busy: {0}")]
Busy(&'static str),
#[error("merge operator failed for key {0:?}")]
MergeFailed(Vec<u8>),
#[error("I/O error: {0}")]
Io(#[source] std::io::Error),
}
impl Error {
pub(crate) fn invalid_argument(message: impl Into<String>) -> Self {
Self::InvalidArgument(message.into())
}
pub(crate) fn invalid_column_family(message: impl Into<String>) -> Self {
Self::InvalidColumnFamily(message.into())
}
pub(crate) fn corruption(message: impl Into<String>) -> Self {
Self::Corruption(std::io::Error::new(
std::io::ErrorKind::InvalidData,
message.into(),
))
}
}
impl From<std::io::Error> for Error {
fn from(err: std::io::Error) -> Self {
match err.kind() {
std::io::ErrorKind::InvalidInput => Self::InvalidArgument(err.to_string()),
std::io::ErrorKind::InvalidData | std::io::ErrorKind::UnexpectedEof => {
Self::Corruption(err)
}
std::io::ErrorKind::NotConnected if err.to_string() == "database is closed" => {
Self::Closed
}
_ => Self::Io(err),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn io_error_converts_via_from_when_filesystem_related() {
let ioe = std::io::Error::new(std::io::ErrorKind::NotFound, "nope");
let e: Error = ioe.into();
assert!(matches!(e, Error::Io(_)));
}
#[test]
fn invalid_input_converts_to_invalid_argument() {
let ioe = std::io::Error::new(std::io::ErrorKind::InvalidInput, "bad key");
let e: Error = ioe.into();
assert!(matches!(e, Error::InvalidArgument(msg) if msg.contains("bad key")));
}
#[test]
fn corruption_kinds_convert_to_corruption() {
for kind in [
std::io::ErrorKind::InvalidData,
std::io::ErrorKind::UnexpectedEof,
] {
let ioe = std::io::Error::new(kind, "bad bytes");
let e: Error = ioe.into();
assert!(matches!(e, Error::Corruption(source) if source.kind() == kind));
}
}
#[test]
fn busy_display_contains_reason() {
let e = Error::Busy("too many L0 files");
let msg = format!("{e}");
assert!(msg.contains("too many L0 files"));
assert!(msg.contains("busy"));
}
#[test]
fn merge_failed_display_contains_key_bytes() {
let e = Error::MergeFailed(b"k".to_vec());
let msg = format!("{e}");
assert!(msg.contains("merge"));
assert!(msg.contains("107"));
}
}