use core::fmt;
use crate::record::RecordId;
pub type Result<T> = core::result::Result<T, Error>;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum Error {
Sink(SinkError),
ChainBroken,
Capacity,
NonMonotonicClock,
HashMismatch(RecordId),
LinkMismatch(RecordId),
IdMismatch(RecordId),
Truncated,
InvalidFormat,
Io,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Sink(_) => f.write_str("audit sink failure"),
Self::ChainBroken => f.write_str("audit hash chain broken"),
Self::Capacity => f.write_str("audit capacity exceeded"),
Self::NonMonotonicClock => f.write_str("audit clock not monotonic"),
Self::HashMismatch(id) => write!(f, "audit hash mismatch at record {}", id.as_u64()),
Self::LinkMismatch(id) => write!(f, "audit link mismatch at record {}", id.as_u64()),
Self::IdMismatch(id) => write!(f, "audit id mismatch at record {}", id.as_u64()),
Self::Truncated => f.write_str("audit input truncated"),
Self::InvalidFormat => f.write_str("audit input invalid format"),
Self::Io => f.write_str("audit i/o failure"),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Sink(inner) => Some(inner),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum SinkError {
Io,
Capacity,
Closed,
Other,
}
impl fmt::Display for SinkError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Io => f.write_str("sink i/o failure"),
Self::Capacity => f.write_str("sink capacity exceeded"),
Self::Closed => f.write_str("sink closed"),
Self::Other => f.write_str("sink error"),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for SinkError {}
impl From<SinkError> for Error {
#[inline]
fn from(value: SinkError) -> Self {
Self::Sink(value)
}
}
#[cfg(all(test, feature = "std"))]
mod tests {
use super::{Error, SinkError};
use crate::record::RecordId;
#[test]
fn error_display_covers_all_variants() {
let id = RecordId::from_u64(7);
assert_eq!(Error::Sink(SinkError::Io).to_string(), "audit sink failure");
assert_eq!(Error::ChainBroken.to_string(), "audit hash chain broken");
assert_eq!(Error::Capacity.to_string(), "audit capacity exceeded");
assert_eq!(
Error::NonMonotonicClock.to_string(),
"audit clock not monotonic"
);
assert_eq!(
Error::HashMismatch(id).to_string(),
"audit hash mismatch at record 7"
);
assert_eq!(
Error::LinkMismatch(id).to_string(),
"audit link mismatch at record 7"
);
assert_eq!(
Error::IdMismatch(id).to_string(),
"audit id mismatch at record 7"
);
assert_eq!(Error::Truncated.to_string(), "audit input truncated");
assert_eq!(
Error::InvalidFormat.to_string(),
"audit input invalid format"
);
assert_eq!(Error::Io.to_string(), "audit i/o failure");
}
#[test]
fn sink_error_display_covers_all_variants() {
assert_eq!(SinkError::Io.to_string(), "sink i/o failure");
assert_eq!(SinkError::Capacity.to_string(), "sink capacity exceeded");
assert_eq!(SinkError::Closed.to_string(), "sink closed");
assert_eq!(SinkError::Other.to_string(), "sink error");
}
#[test]
fn error_source_chains_to_sink_error() {
use std::error::Error as _;
let err = Error::Sink(SinkError::Closed);
assert!(err.source().is_some());
let other = Error::Truncated;
assert!(other.source().is_none());
}
#[test]
fn from_sink_error_wraps() {
let e: Error = SinkError::Capacity.into();
assert_eq!(e, Error::Sink(SinkError::Capacity));
}
}