tale_lib/
error.rs

1use std::ops::Range;
2
3use ariadne::{Color, Label, Report, Source};
4
5use crate::{
6    lexer::{Position, Token},
7    state::{ParserState, SymbolValue},
8};
9
10pub type TaleResult<T> = Result<T, TaleError>;
11
12pub type TaleResultVec<T> = Result<T, Vec<TaleError>>;
13
14#[derive(Debug, Clone)]
15pub struct TaleError {
16    kind: TaleErrorKind,
17    span: Range<usize>,
18    position: Position,
19    msg: String,
20}
21
22#[derive(Debug, Clone)]
23pub enum TaleErrorKind {
24    Lexical,
25    Parse,
26    Analysis,
27    Evaluation,
28    System,
29}
30
31impl TaleError {
32    #[must_use]
33    pub fn system(msg: String) -> Self {
34        Self {
35            kind: TaleErrorKind::System,
36            span: 0..0,
37            position: (0, 0),
38            msg,
39        }
40    }
41
42    #[must_use]
43    pub fn lexer(span: Range<usize>, position: Position, msg: String) -> Self {
44        Self {
45            kind: TaleErrorKind::Lexical,
46            span,
47            position,
48            msg,
49        }
50    }
51
52    #[must_use]
53    pub fn parser(span: Range<usize>, position: Position, msg: String) -> Self {
54        Self {
55            kind: TaleErrorKind::Parse,
56            span,
57            position,
58            msg,
59        }
60    }
61
62    #[must_use]
63    pub fn from_parser_vec(errs: Vec<chumsky::error::Rich<'_, Token>>) -> Vec<Self> {
64        errs.into_iter()
65            .map(|err| {
66                // TODO: Better handling of context nesting
67                let context_list = err
68                    .contexts()
69                    .map(|context| context.0.to_string())
70                    .collect::<Vec<_>>()
71                    .join(" -> ");
72                let msg = if context_list.is_empty() {
73                    err.reason().to_string()
74                } else {
75                    format!("{} In: [{context_list}]", err.reason())
76                };
77                Self::parser(err.span().into_range(), Default::default(), msg)
78            })
79            .collect::<Vec<Self>>()
80    }
81
82    #[must_use]
83    pub fn update_parser_vec_with_state(mut errs: Vec<Self>, state: &ParserState) -> Vec<Self> {
84        for err in &mut errs {
85            let span = err.span();
86            err.update_span(state.get_source_span(&span));
87            err.update_position(state.get_source_position(&span));
88        }
89        errs
90    }
91
92    #[must_use]
93    pub fn analyzer(span: Range<usize>, position: Position, msg: String) -> Self {
94        Self {
95            kind: TaleErrorKind::Analysis,
96            span,
97            position,
98            msg,
99        }
100    }
101
102    #[must_use]
103    pub fn evaluator(span: Range<usize>, position: Position, msg: String) -> Self {
104        Self {
105            kind: TaleErrorKind::Evaluation,
106            span,
107            position,
108            msg,
109        }
110    }
111
112    #[must_use]
113    pub fn span(&self) -> Range<usize> {
114        self.span.clone()
115    }
116
117    pub fn update_span(&mut self, span: Range<usize>) {
118        self.span = span;
119    }
120
121    #[must_use]
122    pub fn start(&self) -> usize {
123        self.span.start
124    }
125
126    #[must_use]
127    pub fn end(&self) -> usize {
128        self.span.end
129    }
130
131    #[must_use]
132    pub fn position(&self) -> Position {
133        self.position
134    }
135
136    pub fn update_position(&mut self, position: Position) {
137        self.position = position;
138    }
139
140    pub fn append_message(&mut self, add_msg: &str) {
141        self.msg.push_str(add_msg);
142    }
143
144    #[must_use]
145    pub fn kind(&self) -> &TaleErrorKind {
146        &self.kind
147    }
148
149    #[must_use]
150    pub fn msg(&self) -> &String {
151        &self.msg
152    }
153}
154
155impl From<std::io::Error> for TaleError {
156    fn from(value: std::io::Error) -> Self {
157        Self::system(value.to_string())
158    }
159}
160
161impl From<std::num::TryFromIntError> for TaleError {
162    fn from(value: std::num::TryFromIntError) -> Self {
163        Self::evaluator(0..0, Position::default(), value.to_string())
164    }
165}
166
167impl From<TaleError> for Vec<TaleError> {
168    fn from(value: TaleError) -> Self {
169        vec![value]
170    }
171}
172
173pub fn render_tale_result_vec(
174    prefix: &str,
175    source_name: &str,
176    source: &str,
177    tale_result_vec: TaleResultVec<SymbolValue>,
178) -> TaleResultVec<SymbolValue> {
179    match tale_result_vec {
180        Ok(value) => {
181            value.render(prefix);
182            Ok(SymbolValue::Placeholder)
183        }
184        Err(tev) => render_tale_error_vec(tev, source_name, source),
185    }
186}
187
188pub fn render_tale_error_vec(
189    tev: Vec<TaleError>,
190    source_name: &str,
191    source: &str,
192) -> TaleResultVec<SymbolValue> {
193    for error in tev {
194        Report::build(ariadne::ReportKind::Error, (source_name, error.span()))
195            .with_message(format!("{:?} Error: {}", error.kind(), error.msg()))
196            .with_label(
197                Label::new((source_name, error.span()))
198                    .with_message("Problem occurred here.")
199                    .with_color(Color::Red),
200            )
201            .finish()
202            .eprint((source_name, Source::from(source)))
203            .map_err(TaleError::from)?;
204    }
205    Ok(SymbolValue::Placeholder)
206}