1#![allow(deprecated)]
2use std::sync::Arc;
3
4use miette::{Diagnostic, NamedSource, Severity, SourceSpan};
5use winnow::{
6 error::{AddContext, ErrorKind, FromRecoverableError, ParserError},
7 stream::{Location, Stream},
8};
9
10use crate::script::rhai_error::RhaiError;
11
12#[derive(Debug, thiserror::Error, miette::Diagnostic)]
13pub enum TemplateError {
14 #[error("Error evaluating rhai")]
15 Rhai {
16 #[source]
17 error: RhaiError,
18 #[label(primary, "here")]
19 error_span: Option<SourceSpan>,
20 },
21 #[error("Failed to evaluate template")]
22 Multiple(#[related] Vec<TemplateError>),
23}
24
25impl TemplateError {
26 pub fn from_rhai(error: RhaiError, expr_span: impl Into<SourceSpan>) -> Self {
27 match error {
28 RhaiError::SourceError { ref span, .. } => {
29 let expr_span = expr_span.into();
30 let start = expr_span.offset() + span.start;
31 let end = expr_span.offset() + span.end;
32 Self::Rhai {
33 error,
34 error_span: Some((start..end).into()),
35 }
36 }
37 error => Self::Rhai {
38 error,
39 error_span: None,
40 },
41 }
42 }
43
44 pub fn into_report(self, name: impl ToString, source: impl ToString) -> miette::Report {
46 miette::Report::from(self)
47 .with_source_code(NamedSource::new(name.to_string(), source.to_string()))
48 }
49}
50
51#[derive(Debug, Diagnostic, Clone, Eq, PartialEq, thiserror::Error)]
52#[error("Failed to parse yolk template file")]
53pub struct YolkParseFailure {
54 #[source_code]
55 pub input: Arc<miette::NamedSource<String>>,
56 #[related]
57 pub diagnostics: Vec<YolkParseDiagnostic>,
58}
59
60impl YolkParseFailure {
61 pub fn from_errs(errs: Vec<YolkParseError>, input: &str) -> YolkParseFailure {
62 let src = Arc::new(NamedSource::new("file", input.to_string()));
63 YolkParseFailure {
64 input: src.clone(),
65 diagnostics: errs
66 .into_iter()
67 .map(|e| YolkParseDiagnostic {
68 message: e.message,
69 input: src.clone(),
70 span: e.span.unwrap_or_else(|| (0usize..0usize).into()),
71 label: e.label,
72 help: e.help,
73 severity: Severity::Error,
74 })
75 .collect(),
76 }
77 }
78}
79
80#[derive(Debug, Diagnostic, Clone, Eq, PartialEq, thiserror::Error)]
81#[error("{}", message.unwrap_or_else(|| "An unspecified parse error occurred."))]
82pub struct YolkParseDiagnostic {
83 #[source_code]
84 pub input: Arc<NamedSource<String>>,
85
86 #[label("{}", label.unwrap_or_else(|| "here"))]
88 pub span: SourceSpan,
89
90 pub message: Option<&'static str>,
92
93 pub label: Option<&'static str>,
95
96 #[help]
98 pub help: Option<&'static str>,
99
100 #[diagnostic(severity)]
102 pub severity: miette::Severity,
103}
104
105#[derive(Debug, Clone, Eq, PartialEq)]
106pub struct YolkParseError {
107 pub message: Option<&'static str>,
108 pub span: Option<SourceSpan>,
109 pub label: Option<&'static str>,
110 pub help: Option<&'static str>,
111}
112
113impl<I: Stream> ParserError<I> for YolkParseError {
114 fn from_error_kind(_input: &I, _kind: ErrorKind) -> Self {
115 Self {
116 span: None,
117 label: None,
118 help: None,
119 message: None,
120 }
121 }
122
123 fn append(
124 self,
125 _input: &I,
126 _token_start: &<I as Stream>::Checkpoint,
127 _kind: ErrorKind,
128 ) -> Self {
129 self
130 }
131}
132
133impl<I: Stream> AddContext<I, YolkParseContext> for YolkParseError {
134 fn add_context(
135 mut self,
136 _input: &I,
137 _token_start: &<I as Stream>::Checkpoint,
138 ctx: YolkParseContext,
139 ) -> Self {
140 self.message = ctx.message.or(self.message);
141 self.label = ctx.label.or(self.label);
142 self.help = ctx.help.or(self.help);
143 self
144 }
145}
146
147impl<I: Stream + Location> FromRecoverableError<I, Self> for YolkParseError {
148 #[inline]
149 fn from_recoverable_error(
150 token_start: &<I as Stream>::Checkpoint,
151 _err_start: &<I as Stream>::Checkpoint,
152 input: &I,
153 mut e: Self,
154 ) -> Self {
155 e.span = e
156 .span
157 .or_else(|| Some(span_from_checkpoint(input, token_start)));
158 e
159 }
160}
161
162impl<I: Stream + Location> FromRecoverableError<I, YolkParseContext> for YolkParseError {
163 #[inline]
164 fn from_recoverable_error(
165 token_start: &<I as Stream>::Checkpoint,
166 _err_start: &<I as Stream>::Checkpoint,
167 input: &I,
168 e: YolkParseContext,
169 ) -> Self {
170 YolkParseError {
171 span: Some((input.offset_from(token_start).saturating_sub(1)..input.location()).into()),
172 label: e.label,
173 help: e.help,
174 message: e.message,
175 }
176 }
177}
178
179#[derive(Debug, Clone, Default, Eq, PartialEq)]
180pub(super) struct YolkParseContext {
181 message: Option<&'static str>,
182 label: Option<&'static str>,
183 help: Option<&'static str>,
184}
185
186impl YolkParseContext {
187 pub(super) fn msg(mut self, txt: &'static str) -> Self {
188 self.message = Some(txt);
189 self
190 }
191
192 pub(super) fn lbl(mut self, txt: &'static str) -> Self {
193 self.label = Some(txt);
194 self
195 }
196
197 pub(super) fn hlp(mut self, txt: &'static str) -> Self {
198 self.help = Some(txt);
199 self
200 }
201}
202
203pub(super) fn cx() -> YolkParseContext {
204 Default::default()
205}
206
207fn span_from_checkpoint<I: Stream + Location>(
208 input: &I,
209 start: &<I as Stream>::Checkpoint,
210) -> SourceSpan {
211 let offset = input.offset_from(start);
212 ((input.location() - offset)..input.location()).into()
213}