use core::fmt;
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct SmallHex {
buf: [u8; Self::CAP],
len: u8,
}
impl SmallHex {
pub const CAP: usize = 16;
#[must_use]
pub fn new(bytes: &[u8]) -> Self {
let mut buf = [0u8; Self::CAP];
let n = bytes.len().min(Self::CAP);
buf[..n].copy_from_slice(&bytes[..n]);
Self { buf, len: n as u8 }
}
#[must_use]
pub fn as_bytes(&self) -> &[u8] {
self.buf.get(..self.len as usize).unwrap_or(&[])
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.len == 0
}
}
impl fmt::Debug for SmallHex {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "SmallHex(")?;
for b in self.as_bytes() {
write!(f, "{b:02x}")?;
}
write!(f, ")")
}
}
impl fmt::Display for SmallHex {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut first = true;
for b in self.as_bytes() {
if !first {
write!(f, " ")?;
}
write!(f, "{b:02x}")?;
first = false;
}
Ok(())
}
}
#[non_exhaustive]
#[derive(Debug, thiserror::Error)]
pub enum VfsError {
#[error("I/O during {op}: {source}")]
Io {
op: &'static str,
#[source]
source: std::io::Error,
},
#[error("decode failed in {layer} at offset {offset}: {detail} (bytes: {bytes})")]
Decode {
layer: &'static str,
offset: u64,
detail: String,
bytes: SmallHex,
},
#[error("unrecognized data at {at} offset {offset} (bytes: {bytes})")]
Unrecognized {
at: &'static str,
offset: u64,
bytes: SmallHex,
},
#[error("ambiguous detection: {candidates:?}")]
Ambiguous { candidates: Vec<&'static str> },
#[error("bootstrap failed at {stage}: {detail}")]
Bootstrap { stage: &'static str, detail: String },
#[error("unsupported {layer}: {scheme}")]
Unsupported { layer: &'static str, scheme: String },
#[error("budget exceeded: {cap} (limit {limit})")]
Budget { cap: &'static str, limit: u64 },
#[error("credentials required for {scheme} ({target})")]
NeedCredentials {
scheme: &'static str,
target: String,
},
#[error("{what}: offset {offset}+{len} past bound {bound}")]
OutOfRange {
what: &'static str,
offset: u64,
len: u64,
bound: u64,
},
}
pub type VfsResult<T> = Result<T, VfsError>;
pub(crate) fn io_err(op: &'static str) -> impl Fn(std::io::Error) -> VfsError {
move |source| VfsError::Io { op, source }
}
#[cfg(test)]
mod tests {
use super::io_err;
#[test]
fn io_err_wraps_with_the_op_label() {
let e = io_err("read")(std::io::Error::other("boom"));
assert!(format!("{e}").contains("read"));
}
}