use std::error::Error as StdError;
use std::fmt;
#[derive(Debug)]
pub struct IoContext {
pub context: &'static str,
pub detail: String,
}
impl IoContext {
#[must_use]
pub fn new(context: &'static str, detail: impl Into<String>) -> Self {
Self {
context,
detail: detail.into(),
}
}
}
impl fmt::Display for IoContext {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}: {}", self.context, self.detail)
}
}
impl StdError for IoContext {}
#[cfg(test)]
#[allow(clippy::expect_used)] mod tests {
use super::IoContext;
use std::io;
#[test]
fn display_includes_context_and_detail() {
let ctx = IoContext::new("failed to read entry metadata", "/tmp/x: denied");
assert_eq!(
ctx.to_string(),
"failed to read entry metadata: /tmp/x: denied"
);
}
#[test]
fn roundtrips_through_io_error_other() {
let err = io::Error::other(IoContext::new("failed to finish zip archive", "disk full"));
assert_eq!(err.kind(), io::ErrorKind::Other);
assert_eq!(err.to_string(), "failed to finish zip archive: disk full");
let ctx = err
.get_ref()
.and_then(|inner| inner.downcast_ref::<IoContext>())
.expect("IoContext should be downcastable from io::Error");
assert_eq!(ctx.context, "failed to finish zip archive");
assert_eq!(ctx.detail, "disk full");
}
}