use std::path::PathBuf;
use error_forge::ForgeError;
use iqdb_types::{DistanceMetric, IqdbError};
#[non_exhaustive]
#[derive(Debug)]
pub enum PersistError {
Io {
path: PathBuf,
source: std::io::Error,
},
BadMagic {
found: [u8; 8],
},
UnsupportedVersion {
found: u32,
supported: u32,
},
ChecksumMismatch {
expected: u32,
computed: u32,
},
TruncatedHeader {
needed: usize,
found: usize,
},
TruncatedPayload {
needed: u64,
found: u64,
},
InvalidMetric {
tag: u8,
},
UnsupportedMetric {
metric: DistanceMetric,
},
InvalidIndexType {
found: String,
expected: &'static str,
},
InvalidPayload {
reason: &'static str,
},
Compression {
reason: &'static str,
},
IndexBuild(IqdbError),
Unsupported {
feature: &'static str,
available_in: &'static str,
},
}
impl std::fmt::Display for PersistError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Io { path, source } => {
write!(f, "I/O error on {}: {source}", path.display())
}
Self::BadMagic { found } => {
write!(f, "bad magic: not an iqdb snapshot (found {found:?})")
}
Self::UnsupportedVersion { found, supported } => {
write!(
f,
"unsupported format version: found {found}, supported {supported}",
)
}
Self::ChecksumMismatch { expected, computed } => {
write!(
f,
"checksum mismatch: header expected {expected:#010x}, computed {computed:#010x}",
)
}
Self::TruncatedHeader { needed, found } => {
write!(f, "truncated header: needed {needed} bytes, found {found}")
}
Self::TruncatedPayload { needed, found } => {
write!(f, "truncated payload: needed {needed} bytes, found {found}")
}
Self::InvalidMetric { tag } => {
write!(f, "invalid metric tag: {tag}")
}
Self::UnsupportedMetric { metric } => {
write!(f, "unsupported metric for this build: {metric:?}")
}
Self::InvalidIndexType { found, expected } => {
write!(
f,
"index type mismatch: file declared {found:?}, caller expected {expected:?}",
)
}
Self::InvalidPayload { reason } => {
write!(f, "invalid payload: {reason}")
}
Self::Compression { reason } => {
write!(f, "compression error: {reason}")
}
Self::IndexBuild(e) => write!(f, "index construction failed: {e}"),
Self::Unsupported {
feature,
available_in,
} => {
write!(
f,
"feature not supported in this build: {feature} (available in {available_in})",
)
}
}
}
}
impl std::error::Error for PersistError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Io { source, .. } => Some(source),
Self::IndexBuild(e) => Some(e),
_ => None,
}
}
}
impl ForgeError for PersistError {
fn kind(&self) -> &'static str {
match self {
Self::Io { .. } => "Io",
Self::BadMagic { .. } => "BadMagic",
Self::UnsupportedVersion { .. } => "UnsupportedVersion",
Self::ChecksumMismatch { .. } => "ChecksumMismatch",
Self::TruncatedHeader { .. } => "TruncatedHeader",
Self::TruncatedPayload { .. } => "TruncatedPayload",
Self::InvalidMetric { .. } => "InvalidMetric",
Self::UnsupportedMetric { .. } => "UnsupportedMetric",
Self::InvalidIndexType { .. } => "InvalidIndexType",
Self::InvalidPayload { .. } => "InvalidPayload",
Self::Compression { .. } => "Compression",
Self::IndexBuild(_) => "IndexBuild",
Self::Unsupported { .. } => "Unsupported",
}
}
fn caption(&self) -> &'static str {
match self {
Self::Io { .. } => "OS-level I/O failure on a snapshot file",
Self::BadMagic { .. } => "file is not an iqdb snapshot",
Self::UnsupportedVersion { .. } => {
"snapshot format version is not supported by this build"
}
Self::ChecksumMismatch { .. } => "payload CRC32 does not match the header",
Self::TruncatedHeader { .. } => "file ended before the full header could be read",
Self::TruncatedPayload { .. } => "file ended before the full payload could be read",
Self::InvalidMetric { .. } => {
"metric tag does not correspond to any known distance metric"
}
Self::UnsupportedMetric { .. } => "distance metric has no on-disk tag in this build",
Self::InvalidIndexType { .. } => {
"header's index-type tag does not match the caller's I"
}
Self::InvalidPayload { .. } => "payload bytes decoded to a structurally invalid index",
Self::Compression { .. } => "a compression or decompression step failed",
Self::IndexBuild(_) => "a downstream Index::new or insert returned an error",
Self::Unsupported { .. } => {
"the requested feature lands in a later version of iqdb-persist"
}
}
}
}
impl From<IqdbError> for PersistError {
fn from(value: IqdbError) -> Self {
Self::IndexBuild(value)
}
}
pub type Result<T> = core::result::Result<T, PersistError>;