Skip to main content

mordant/
error.rs

1//! Custom error types for the mordant library.
2
3extern crate alloc;
4
5use alloc::{boxed::Box, format, string::String};
6use core::{
7    error::Error as CoreError,
8    fmt::{self, Debug, Display, Formatter},
9    result::Result as CoreResult,
10};
11#[cfg(feature = "std")]
12use std::backtrace::{Backtrace, BacktraceStatus};
13
14use crate::ast::NodeRef;
15
16/// Alias for a Result type that uses [`Error`].
17pub type Result<T> = CoreResult<T, Error>;
18
19/// Error type that can represent either an internal [`Error`] or a user-defined callback
20/// error.
21#[derive(Debug)]
22#[non_exhaustive]
23pub enum CallbackError<E: CoreError + 'static> {
24    /// Internal error from the mordant library.
25    Internal(Error),
26
27    /// User-defined callback error.
28    Callback(E),
29}
30
31impl<E: CoreError + 'static> Display for CallbackError<E> {
32    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
33        match self {
34            CallbackError::Internal(err) => write!(f, "{}", err),
35            CallbackError::Callback(err) => {
36                write!(f, "{}", err)
37            }
38        }
39    }
40}
41
42impl<E: CoreError + 'static> CoreError for CallbackError<E> {
43    fn source(&self) -> Option<&(dyn CoreError + 'static)> {
44        match self {
45            CallbackError::Internal(err) => Some(err),
46            CallbackError::Callback(err) => Some(err),
47        }
48    }
49}
50
51/// Custom error type for the mordant library.
52#[non_exhaustive]
53pub enum Error {
54    /// Invalid node reference error.
55    InvalidNodeRef {
56        noderef: NodeRef,
57        description: String,
58        #[cfg(feature = "std")]
59        backtrace: Backtrace,
60    },
61    /// Invalid node operation error.
62    InvalidNodeOperation {
63        message: String,
64        description: String,
65        #[cfg(feature = "std")]
66        backtrace: Backtrace,
67    },
68    /// I/O error.
69    Io {
70        message: String,
71        description: String,
72        source: Option<Box<dyn CoreError + 'static>>,
73        #[cfg(feature = "std")]
74        backtrace: Backtrace,
75    },
76}
77
78impl Error {
79    /// Creates a new invalid node reference error.
80    pub fn invalid_node_ref(node_ref: NodeRef) -> Self {
81        Error::InvalidNodeRef {
82            noderef: node_ref,
83            description: format!("invalid node reference: {}", node_ref),
84            #[cfg(feature = "std")]
85            backtrace: Backtrace::capture(),
86        }
87    }
88
89    /// Creates a new invalid operation error with a message.
90    pub fn invalid_node_operation(message: String) -> Self {
91        Error::InvalidNodeOperation {
92            message: message.clone(),
93            description: format!("invalid operation: {}", message),
94            #[cfg(feature = "std")]
95            backtrace: Backtrace::capture(),
96        }
97    }
98
99    /// Creates a new I/O error with an optional source error.
100    pub fn io<S>(m: S, source: Option<Box<dyn CoreError + 'static>>) -> Self
101    where
102        S: Into<String>,
103    {
104        let message = m.into();
105        Error::Io {
106            message: message.clone(),
107            description: format!("io error: {}", message),
108            source,
109            #[cfg(feature = "std")]
110            backtrace: Backtrace::capture(),
111        }
112    }
113
114    /// Returns the backtrace associated with the error, if available.
115    /// This is only available when the `std` feature is enabled.
116    #[cfg(feature = "std")]
117    pub fn backtrace(&self) -> Option<&Backtrace> {
118        match self {
119            Error::InvalidNodeRef { backtrace, .. } => Some(backtrace),
120            Error::InvalidNodeOperation { backtrace, .. } => Some(backtrace),
121            Error::Io { backtrace, .. } => Some(backtrace),
122        }
123    }
124}
125
126impl Display for Error {
127    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
128        match self {
129            Error::InvalidNodeRef { description, .. } => write!(f, "{}", description),
130            Error::InvalidNodeOperation { description, .. } => write!(f, "{}", description),
131            Error::Io { description, .. } => write!(f, "{}", description),
132        }
133    }
134}
135
136impl Debug for Error {
137    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
138        match self {
139            Error::InvalidNodeRef {
140                description,
141                #[cfg(feature = "std")]
142                backtrace,
143                ..
144            } => {
145                write!(f, "{}", description)?;
146                #[cfg(feature = "std")]
147                {
148                    write!(f, "{}", format_backtrace(backtrace))?;
149                }
150            }
151            Error::InvalidNodeOperation {
152                description,
153                #[cfg(feature = "std")]
154                backtrace,
155                ..
156            } => {
157                write!(f, "{}", description)?;
158                #[cfg(feature = "std")]
159                {
160                    write!(f, "{}", format_backtrace(backtrace))?;
161                }
162            }
163            Error::Io {
164                description,
165                #[cfg(feature = "std")]
166                backtrace,
167                ..
168            } => {
169                write!(f, "{}", description)?;
170                #[cfg(feature = "std")]
171                {
172                    write!(f, "{}", format_backtrace(backtrace))?;
173                }
174            }
175        }
176        if let Some(source) = self.source() {
177            writeln!(f, "Caused by: {:?}", source)?;
178        }
179        Ok(())
180    }
181}
182
183impl CoreError for Error {
184    fn source(&self) -> Option<&(dyn CoreError + 'static)> {
185        {
186            match self {
187                Error::Io { source, .. } => source.as_deref(),
188                _ => None,
189            }
190        }
191    }
192}
193
194#[cfg(feature = "std")]
195fn format_backtrace(backtrace: &Backtrace) -> String {
196    match backtrace.status() {
197        BacktraceStatus::Captured => format!("\nstack backtrace:\n{}", backtrace),
198        _ => String::new(),
199    }
200}