yolk/script/
rhai_error.rs

1use std::ops::Range;
2
3use miette::Diagnostic;
4
5#[derive(Debug, thiserror::Error, Diagnostic)]
6pub enum RhaiError {
7    #[error("{origin}")]
8    #[diagnostic(forward(origin))]
9    SourceError {
10        #[label("here")]
11        span: Range<usize>,
12        origin: Box<RhaiError>,
13    },
14    #[error(transparent)]
15    RhaiError(#[from] rhai::EvalAltResult),
16    #[error("{}", .0)]
17    #[diagnostic(transparent)]
18    Other(miette::Report),
19}
20
21impl RhaiError {
22    pub fn new_other<E>(err: E) -> Self
23    where
24        E: std::error::Error + Send + Sync + 'static,
25    {
26        Self::Other(miette::miette!(err))
27    }
28    pub fn other<E>(err: E) -> Self
29    where
30        E: Diagnostic + Send + Sync + 'static,
31    {
32        Self::Other(miette::Report::from(err))
33    }
34
35    pub fn from_rhai_compile(source_code: &str, err: rhai::ParseError) -> Self {
36        Self::from_rhai(source_code, err.into())
37    }
38
39    pub fn from_rhai(source_code: &str, err: rhai::EvalAltResult) -> Self {
40        let position = err.position();
41        let mut span = 0..0;
42        if let Some(line_nr) = position.line() {
43            // TODO: this won't work with \r\n, _or will it_? *vsauce music starts playing*
44            let offset_start = source_code
45                .split_inclusive('\n')
46                .take(line_nr - 1)
47                .map(|x| x.len())
48                .sum::<usize>();
49            span = if let Some(within_line) = position.position() {
50                offset_start + within_line..offset_start + within_line + 1
51            } else {
52                let offset_end = offset_start
53                    + source_code
54                        .lines()
55                        .nth(line_nr - 1)
56                        .map(|x| x.len())
57                        .unwrap_or_default();
58                let indent = source_code[offset_start..]
59                    .chars()
60                    .take_while(|x| x.is_whitespace())
61                    .count();
62                offset_start + indent..offset_end
63            };
64        }
65        if span.start >= source_code.len() {
66            span = source_code.len() - 1..source_code.len();
67        }
68        Self::SourceError {
69            span,
70            origin: Box::new(RhaiError::RhaiError(err)),
71        }
72    }
73
74    /// Convert this error into a [`miette::Report`] with the given name and source code attached as a rust source.
75    pub fn into_report(self, name: impl ToString, source: impl ToString) -> miette::Report {
76        miette::Report::from(self).with_source_code(
77            miette::NamedSource::new(name.to_string(), source.to_string()).with_language("Rust"),
78        )
79    }
80}