use core::fmt;
pub type Result<T> = core::result::Result<T, Error>;
#[derive(Debug)]
#[non_exhaustive]
pub enum Error {
Io(std::io::Error),
InvalidConfig(&'static str),
InvalidVector {
reason: &'static str,
},
DimensionMismatch {
left: usize,
right: usize,
},
NotImplemented,
}
impl Error {
pub(crate) const fn invalid_vector(reason: &'static str) -> Self {
Self::InvalidVector { reason }
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Io(err) => write!(f, "iqdb: io error ({})", err.kind()),
Self::InvalidConfig(msg) => write!(f, "iqdb: invalid configuration ({msg})"),
Self::InvalidVector { reason } => write!(f, "iqdb: invalid vector ({reason})"),
Self::DimensionMismatch { left, right } => {
write!(f, "iqdb: dimension mismatch (left={left}, right={right})")
}
Self::NotImplemented => f.write_str("iqdb: not implemented"),
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Io(err) => Some(err),
_ => None,
}
}
}
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 error_implements_std_error() {
fn assert_error<E: std::error::Error>() {}
assert_error::<Error>();
}
#[test]
fn 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 invalid_config_display_is_stable() {
let msg = format!("{}", Error::InvalidConfig("bad path"));
assert!(msg.contains("invalid configuration"));
assert!(msg.contains("bad path"));
}
#[test]
fn invalid_vector_display_is_stable() {
let err = Error::invalid_vector("empty vector");
let msg = format!("{err}");
assert!(msg.contains("invalid vector"));
assert!(msg.contains("empty vector"));
}
#[test]
fn dimension_mismatch_display_includes_both_dims() {
let msg = format!("{}", Error::DimensionMismatch { left: 4, right: 8 });
assert!(msg.contains("dimension mismatch"));
assert!(msg.contains("left=4"));
assert!(msg.contains("right=8"));
}
#[test]
fn not_implemented_display_is_stable() {
let msg = format!("{}", Error::NotImplemented);
assert!(msg.contains("not implemented"));
}
#[test]
fn from_io_maps_to_io_variant() {
let err: Error = std::io::Error::new(std::io::ErrorKind::NotFound, "missing").into();
assert!(matches!(err, Error::Io(_)));
}
#[test]
fn io_error_exposes_source() {
let inner = std::io::Error::other("x");
let err = Error::Io(inner);
assert!(std::error::Error::source(&err).is_some());
}
#[test]
fn non_io_variant_has_no_source() {
let err = Error::invalid_vector("empty vector");
assert!(std::error::Error::source(&err).is_none());
}
}