Skip to main content

molframe_core/diagnostic/
convert.rs

1//! Turning a kernel's own error into a registered diagnostic.
2//!
3//! Every kernel keeps a small error enum of its own, and a caller that wants one
4//! vocabulary for all of them — the Python bindings above all — needs each to
5//! name a stable code. The mapping lives beside the enum so a new variant must
6//! choose its code where it is declared.
7
8/// Implements `From<&E>` and `From<E>` for [`Diagnostic`](crate::Diagnostic).
9///
10/// The code is chosen by the expression given and the message is the error's
11/// own `Display` text, so nothing the error already says is lost. An optional
12/// third argument adds structured context to the diagnostic.
13///
14/// ```
15/// use molframe_core::{Code, Diagnostic, diagnostic_from};
16///
17/// #[derive(Debug)]
18/// struct TooBig(usize);
19/// impl std::fmt::Display for TooBig {
20///     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
21///         write!(f, "{} is too big", self.0)
22///     }
23/// }
24/// diagnostic_from!(TooBig, |_error| Code::E7001, |diagnostic, error| {
25///     diagnostic.with_context("size", error.0.to_string())
26/// });
27///
28/// let diagnostic = Diagnostic::from(TooBig(7));
29/// assert_eq!(diagnostic.code(), Code::E7001);
30/// ```
31#[macro_export]
32macro_rules! diagnostic_from {
33    ($error:ty, |$value:ident| $code:expr) => {
34        $crate::diagnostic_from!($error, |$value| $code, |diagnostic, _error| diagnostic);
35    };
36    ($error:ty, |$value:ident| $code:expr, |$diagnostic:ident, $inner:ident| $context:expr) => {
37        impl ::core::convert::From<&$error> for $crate::Diagnostic {
38            fn from($value: &$error) -> Self {
39                let code: $crate::Code = $code;
40                let $diagnostic = $crate::Diagnostic::new(code)
41                    .with_message(::std::string::ToString::to_string($value));
42                let $inner = $value;
43                $context
44            }
45        }
46
47        impl ::core::convert::From<$error> for $crate::Diagnostic {
48            fn from(value: $error) -> Self {
49                Self::from(&value)
50            }
51        }
52    };
53}
54
55impl From<&std::convert::Infallible> for crate::Diagnostic {
56    /// An error that cannot be constructed has no code to name.
57    fn from(never: &std::convert::Infallible) -> Self {
58        match *never {}
59    }
60}
61
62impl From<&crate::Diagnostic> for crate::Diagnostic {
63    /// A finding is already a diagnostic; kernels that fail with one convert by copy.
64    fn from(diagnostic: &crate::Diagnostic) -> Self {
65        diagnostic.clone()
66    }
67}