use std::fmt;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug)]
pub enum Error {
Io(std::io::Error),
Corrupt {
segment: String,
offset: u64,
reason: String,
},
Encode(String),
Crypto(String),
Schema(String),
NotFound(String),
Locked(String),
InvalidCursor(String),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::Io(e) => write!(f, "io error: {e}"),
Error::Corrupt {
segment,
offset,
reason,
} => {
write!(
f,
"corrupt record in {segment} at offset {offset}: {reason}"
)
}
Error::Encode(m) => write!(f, "encode/decode error: {m}"),
Error::Crypto(m) => write!(f, "crypto error: {m}"),
Error::Schema(m) => write!(f, "schema error: {m}"),
Error::NotFound(m) => write!(f, "not found: {m}"),
Error::Locked(root) => write!(
f,
"store root '{root}' is locked by another process (the store is single-process)"
),
Error::InvalidCursor(m) => write!(f, "invalid cursor: {m}"),
}
}
}
impl Error {
pub fn is_storage_full(&self) -> bool {
match self {
Error::Io(e) => io_is_storage_full(e),
_ => false,
}
}
}
pub fn io_is_storage_full(e: &std::io::Error) -> bool {
e.kind() == std::io::ErrorKind::StorageFull || e.raw_os_error() == Some(ENOSPC)
}
const ENOSPC: i32 = 28;
impl std::error::Error for Error {}
impl From<std::io::Error> for Error {
fn from(e: std::io::Error) -> Self {
Error::Io(e)
}
}
impl From<bincode::Error> for Error {
fn from(e: bincode::Error) -> Self {
Error::Encode(e.to_string())
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::ErrorKind;
#[test]
fn classifies_enospc_by_raw_os_error() {
let e = Error::Io(std::io::Error::from_raw_os_error(ENOSPC));
assert!(e.is_storage_full());
}
#[test]
fn classifies_enospc_by_error_kind() {
let e = Error::Io(std::io::Error::new(ErrorKind::StorageFull, "disk full"));
assert!(e.is_storage_full());
}
#[test]
fn other_errors_are_not_storage_full() {
for e in [
Error::Io(std::io::Error::from_raw_os_error(13)), Error::Io(std::io::Error::new(ErrorKind::PermissionDenied, "nope")),
Error::Schema("x".into()),
Error::NotFound("x".into()),
Error::Encode("x".into()),
] {
assert!(!e.is_storage_full(), "{e} misclassified as storage-full");
}
}
}