pyo3_build_config/
errors.rs

1/// A simple macro for returning an error. Resembles anyhow::bail.
2#[macro_export]
3#[doc(hidden)]
4macro_rules! bail {
5    ($($args: tt)+) => { return Err(format!($($args)+).into()) };
6}
7
8/// A simple macro for checking a condition. Resembles anyhow::ensure.
9#[macro_export]
10#[doc(hidden)]
11macro_rules! ensure {
12    ($condition:expr, $($args: tt)+) => { if !($condition) { bail!($($args)+) } };
13}
14
15/// Show warning.
16#[macro_export]
17#[doc(hidden)]
18macro_rules! warn {
19    ($($args: tt)+) => {
20        println!("{}", $crate::format_warn!($($args)+))
21    };
22}
23
24/// Format warning into string.
25#[macro_export]
26#[doc(hidden)]
27macro_rules! format_warn {
28    ($($args: tt)+) => {
29        format!("cargo:warning={}", format_args!($($args)+))
30    };
31}
32
33/// A simple error implementation which allows chaining of errors, inspired somewhat by anyhow.
34#[derive(Debug)]
35pub struct Error {
36    value: String,
37    source: Option<Box<dyn std::error::Error>>,
38}
39
40/// Error report inspired by
41/// <https://blog.rust-lang.org/inside-rust/2021/07/01/What-the-error-handling-project-group-is-working-towards.html#2-error-reporter>
42pub struct ErrorReport<'a>(&'a Error);
43
44impl Error {
45    pub fn report(&self) -> ErrorReport<'_> {
46        ErrorReport(self)
47    }
48}
49
50impl std::fmt::Display for Error {
51    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
52        write!(f, "{}", self.value)
53    }
54}
55
56impl std::error::Error for Error {
57    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
58        self.source.as_deref()
59    }
60}
61
62impl std::fmt::Display for ErrorReport<'_> {
63    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64        use std::error::Error;
65        self.0.fmt(f)?;
66        let mut source = self.0.source();
67        if source.is_some() {
68            writeln!(f, "\ncaused by:")?;
69            let mut index = 0;
70            while let Some(some_source) = source {
71                writeln!(f, "  - {}: {}", index, some_source)?;
72                source = some_source.source();
73                index += 1;
74            }
75        }
76        Ok(())
77    }
78}
79
80impl From<String> for Error {
81    fn from(value: String) -> Self {
82        Self {
83            value,
84            source: None,
85        }
86    }
87}
88
89impl From<&'_ str> for Error {
90    fn from(value: &str) -> Self {
91        value.to_string().into()
92    }
93}
94
95impl From<std::convert::Infallible> for Error {
96    fn from(value: std::convert::Infallible) -> Self {
97        match value {}
98    }
99}
100
101pub type Result<T, E = Error> = std::result::Result<T, E>;
102
103pub trait Context<T> {
104    fn context(self, message: impl Into<String>) -> Result<T>;
105    fn with_context(self, message: impl FnOnce() -> String) -> Result<T>;
106}
107
108impl<T, E> Context<T> for Result<T, E>
109where
110    E: std::error::Error + 'static,
111{
112    fn context(self, message: impl Into<String>) -> Result<T> {
113        self.map_err(|error| Error {
114            value: message.into(),
115            source: Some(Box::new(error)),
116        })
117    }
118
119    fn with_context(self, message: impl FnOnce() -> String) -> Result<T> {
120        self.map_err(|error| Error {
121            value: message(),
122            source: Some(Box::new(error)),
123        })
124    }
125}
126
127#[cfg(test)]
128mod tests {
129    use super::*;
130
131    #[test]
132    fn error_report() {
133        let error: Result<()> = Err(Error::from("there was an internal error"))
134            .with_context(|| format!("failed to do {}", "something difficult"))
135            .context("things went wrong");
136
137        assert_eq!(
138            error
139                .unwrap_err()
140                .report()
141                .to_string()
142                .split('\n')
143                .collect::<Vec<&str>>(),
144            vec![
145                "things went wrong",
146                "caused by:",
147                "  - 0: failed to do something difficult",
148                "  - 1: there was an internal error",
149                ""
150            ]
151        );
152    }
153}
⚠️ Internal Docs ⚠️ Not Public API 👉 Official Docs Here