use core::fmt;
pub type Result<T> = core::result::Result<T, Error>;
#[derive(Debug)]
#[non_exhaustive]
pub enum Error {
NotImplemented,
#[cfg(feature = "nested")]
InvalidPath,
#[cfg(feature = "ttl")]
TtlOverflow,
Io(std::io::Error),
MagicMismatch,
VersionMismatch {
found: u32,
expected: u32,
},
FeatureMismatch {
file_flags: u32,
build_flags: u32,
},
Corrupted {
offset: u64,
reason: &'static str,
},
InvalidConfig(&'static str),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::NotImplemented => f.write_str("emdb: operation not yet implemented"),
#[cfg(feature = "nested")]
Self::InvalidPath => f.write_str("emdb: invalid nested path"),
#[cfg(feature = "ttl")]
Self::TtlOverflow => f.write_str("emdb: ttl overflow"),
Self::Io(err) => write!(f, "emdb: io error ({})", err.kind()),
Self::MagicMismatch => f.write_str("emdb: file magic mismatch"),
Self::VersionMismatch { found, expected } => {
write!(f, "emdb: format version mismatch (found {}, expected {})", found, expected)
}
Self::FeatureMismatch {
file_flags,
build_flags,
} => write!(
f,
"emdb: feature mismatch (file flags 0x{file_flags:08x}, build flags 0x{build_flags:08x})"
),
Self::Corrupted { offset, reason } => {
write!(f, "emdb: corrupted data at offset {} ({})", offset, reason)
}
Self::InvalidConfig(msg) => write!(f, "emdb: invalid configuration ({msg})"),
}
}
}
impl std::error::Error for Error {}
impl From<std::io::Error> for Error {
fn from(value: std::io::Error) -> Self {
Self::Io(value)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_not_implemented_display_is_stable() {
let msg = format!("{}", Error::NotImplemented);
assert!(msg.contains("not yet implemented"));
}
#[test]
fn test_error_implements_std_error() {
fn assert_error<E: std::error::Error>() {}
assert_error::<Error>();
}
#[test]
fn test_io_error_display_does_not_leak_payload() {
let err = Error::Io(std::io::Error::new(
std::io::ErrorKind::PermissionDenied,
"secret",
));
let msg = format!("{}", err);
assert!(msg.contains("permission denied") || msg.contains("PermissionDenied"));
assert!(!msg.contains("secret"));
}
#[test]
fn test_version_mismatch_display_is_stable() {
let msg = format!(
"{}",
Error::VersionMismatch {
found: 2,
expected: 1,
}
);
assert!(msg.contains("found 2"));
assert!(msg.contains("expected 1"));
}
#[test]
fn test_corrupted_display_includes_offset_and_reason() {
let msg = format!(
"{}",
Error::Corrupted {
offset: 42,
reason: "crc mismatch",
}
);
assert!(msg.contains("42"));
assert!(msg.contains("crc mismatch"));
}
#[test]
fn test_from_io_maps_to_io_variant() {
let err: Error = std::io::Error::new(std::io::ErrorKind::NotFound, "missing").into();
assert!(matches!(err, Error::Io(_)));
}
#[cfg(feature = "nested")]
#[test]
fn test_invalid_path_display_is_stable() {
let msg = format!("{}", Error::InvalidPath);
assert_eq!(msg, "emdb: invalid nested path");
}
#[cfg(feature = "ttl")]
#[test]
fn test_ttl_overflow_display_is_stable() {
let msg = format!("{}", Error::TtlOverflow);
assert_eq!(msg, "emdb: ttl overflow");
}
}