use std::fmt;
use crate::Lsn;
pub type Result<T> = std::result::Result<T, WalError>;
#[derive(Debug)]
#[non_exhaustive]
pub enum WalError {
Io(std::io::Error),
Corruption {
segment: Lsn,
offset: u64,
detail: &'static str,
},
TornMidLog {
segment: Lsn,
offset: u64,
},
RecordTooLarge,
InvalidConfig,
BadSegmentHeader,
Locked,
FsyncFailed,
ContiguityViolation,
Poisoned,
}
impl fmt::Display for WalError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
WalError::Io(e) => write!(f, "I/O error: {e}"),
WalError::Corruption {
segment,
offset,
detail,
} => write!(
f,
"corruption in segment {segment} at offset {offset}: {detail}"
),
WalError::TornMidLog { segment, offset } => {
write!(
f,
"torn record mid-log in segment {segment} at offset {offset}"
)
}
WalError::RecordTooLarge => write!(f, "record exceeds max_record_size"),
WalError::InvalidConfig => write!(f, "invalid WAL configuration"),
WalError::BadSegmentHeader => write!(f, "bad segment header"),
WalError::Locked => write!(f, "WAL directory is already locked by another writer"),
WalError::FsyncFailed => write!(f, "fsync failed; handle is poisoned"),
WalError::ContiguityViolation => write!(f, "retained segments are not contiguous"),
WalError::Poisoned => write!(f, "WAL handle is poisoned by a prior durability failure"),
}
}
}
impl std::error::Error for WalError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
WalError::Io(e) => Some(e),
_ => None,
}
}
}
impl From<std::io::Error> for WalError {
fn from(e: std::io::Error) -> Self {
WalError::Io(e)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn io_error_source_is_preserved() {
let io = std::io::Error::other("boom");
let err = WalError::from(io);
assert!(std::error::Error::source(&err).is_some());
}
#[test]
fn display_includes_location_context() {
let err = WalError::Corruption {
segment: Lsn(100001),
offset: 84,
detail: "crc mismatch",
};
let s = err.to_string();
assert!(s.contains("100001"));
assert!(s.contains("84"));
assert!(s.contains("crc mismatch"));
}
}