#![cfg(feature = "std")]
#![allow(clippy::std_instead_of_core)]
use core::fmt;
use std::{error::Error, io};
use anystack::{FrameKind, Report, ResultExt as _, ResultStack};
fn io_error() -> Result<(), io::Error> {
Err(io::Error::from(io::ErrorKind::Other))
}
#[derive(Debug)]
struct OuterError {
inner: InnerError,
}
impl fmt::Display for OuterError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "outer error: {}", self.inner)
}
}
impl Error for OuterError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
Some(&self.inner)
}
}
#[derive(Debug)]
struct InnerError {
inner: io::Error,
}
impl fmt::Display for InnerError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "inner error: {}", self.inner)
}
}
impl Error for InnerError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
Some(&self.inner)
}
}
fn error_with_sources() -> Result<(), OuterError> {
Err(OuterError {
inner: InnerError {
inner: io::Error::from(io::ErrorKind::Other),
},
})
}
#[test]
fn report() {
let report = io_error().map_err(Report::new).expect_err("not an error");
assert!(report.contains::<io::Error>());
assert_eq!(report.current_context().kind(), io::ErrorKind::Other);
}
#[test]
fn error() {
let report = error_with_sources()
.map_err(Report::new)
.expect_err("not an error");
let mut frames = report
.frames()
.skip_while(|frame| !matches!(frame.kind(), FrameKind::Context(_)));
assert!(frames.next().expect("no frames").is::<OuterError>());
let mut frames = frames.skip_while(|frame| !matches!(frame.kind(), FrameKind::Context(_)));
assert!(frames.next().is_some());
let mut frames = frames.skip_while(|frame| !matches!(frame.kind(), FrameKind::Context(_)));
assert!(frames.next().is_some());
let mut frames = frames.skip_while(|frame| !matches!(frame.kind(), FrameKind::Context(_)));
assert!(frames.next().is_none());
}
#[test]
fn into_report() {
let report = io_error()
.map_err(Report::<io::Error>::from)
.expect_err("not an error");
assert!(report.contains::<io::Error>());
assert_eq!(report.current_context().kind(), io::ErrorKind::Other);
}
fn returning_boxed_error() -> Result<(), Box<dyn core::error::Error + Send + Sync>> {
io_error().attach(10_u32)?;
Ok(())
}
#[test]
fn boxed_error() {
let report = returning_boxed_error().expect_err("not an error");
assert_eq!(
report.to_string(),
io_error().expect_err("not an error").to_string()
);
}
#[test]
fn dyn_error_auto_propagation() {
fn base_err() -> ResultStack<()> {
#[expect(clippy::try_err)]
Err(io::Error::from(io::ErrorKind::Other))?;
Ok(())
}
fn other_report() -> ResultStack<()> {
#[expect(clippy::try_err)]
Err(Report::new(io::Error::from(io::ErrorKind::Other)))?;
Ok(())
}
let _: ResultStack<()> = base_err();
let _: Report<dyn Error> = io::Error::from(io::ErrorKind::Other).into();
let _: ResultStack<()> = other_report();
let _: Report<dyn Error> = Report::new(io::Error::from(io::ErrorKind::Other)).into();
}