Skip to main content

zuzu_rust/
error.rs

1use crate::span::Span;
2use std::error::Error;
3use std::fmt;
4
5pub type Result<T> = std::result::Result<T, ZuzuRustError>;
6
7#[derive(Debug)]
8pub enum ZuzuRustError {
9    Lex {
10        message: String,
11        source_file: Option<String>,
12        line: usize,
13        column: usize,
14    },
15    Parse {
16        message: String,
17        source_file: Option<String>,
18        line: usize,
19        column: usize,
20    },
21    IncompleteParse {
22        message: String,
23        source_file: Option<String>,
24        line: usize,
25        column: usize,
26    },
27    Semantic {
28        message: String,
29        source_file: Option<String>,
30        line: usize,
31        column: usize,
32    },
33    Thrown {
34        value: String,
35        token: Option<String>,
36    },
37    Runtime {
38        message: String,
39    },
40    Cli {
41        message: String,
42    },
43    Io(std::io::Error),
44}
45
46impl ZuzuRustError {
47    pub fn parse(message: impl Into<String>, span: Span) -> Self {
48        Self::Parse {
49            message: message.into(),
50            source_file: None,
51            line: span.line,
52            column: span.column,
53        }
54    }
55
56    pub fn incomplete_parse(message: impl Into<String>, span: Span) -> Self {
57        Self::IncompleteParse {
58            message: message.into(),
59            source_file: None,
60            line: span.line,
61            column: span.column,
62        }
63    }
64
65    pub fn lex(message: impl Into<String>, line: usize, column: usize) -> Self {
66        Self::Lex {
67            message: message.into(),
68            source_file: None,
69            line,
70            column,
71        }
72    }
73
74    pub fn semantic(message: impl Into<String>, line: usize) -> Self {
75        Self::Semantic {
76            message: message.into(),
77            source_file: None,
78            line,
79            column: 1,
80        }
81    }
82
83    pub fn cli(message: impl Into<String>) -> Self {
84        Self::Cli {
85            message: message.into(),
86        }
87    }
88
89    pub fn runtime(message: impl Into<String>) -> Self {
90        Self::Runtime {
91            message: message.into(),
92        }
93    }
94
95    pub fn thrown(value: impl Into<String>) -> Self {
96        Self::Thrown {
97            value: value.into(),
98            token: None,
99        }
100    }
101
102    pub fn thrown_with_token(value: impl Into<String>, token: impl Into<String>) -> Self {
103        Self::Thrown {
104            value: value.into(),
105            token: Some(token.into()),
106        }
107    }
108
109    pub fn with_source_file(mut self, source_file: Option<&str>) -> Self {
110        let Some(source_file) = source_file else {
111            return self;
112        };
113        let source_file = source_file.to_owned();
114        match &mut self {
115            ZuzuRustError::Lex {
116                source_file: current,
117                ..
118            }
119            | ZuzuRustError::Parse {
120                source_file: current,
121                ..
122            }
123            | ZuzuRustError::IncompleteParse {
124                source_file: current,
125                ..
126            }
127            | ZuzuRustError::Semantic {
128                source_file: current,
129                ..
130            } => {
131                current.get_or_insert(source_file);
132            }
133            _ => {}
134        }
135        self
136    }
137
138    pub fn is_iterator_exhausted(&self) -> bool {
139        matches!(self, ZuzuRustError::Runtime { message } if message == "iterator exhausted")
140    }
141
142    pub fn thrown_value(&self) -> Option<&str> {
143        match self {
144            ZuzuRustError::Thrown { value, .. } => Some(value),
145            _ => None,
146        }
147    }
148}
149
150impl fmt::Display for ZuzuRustError {
151    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152        match self {
153            ZuzuRustError::Lex {
154                message,
155                source_file,
156                line,
157                column,
158            } => write_diagnostic(
159                f,
160                "lex error",
161                source_file.as_deref(),
162                *line,
163                *column,
164                message,
165            ),
166            ZuzuRustError::Parse {
167                message,
168                source_file,
169                line,
170                column,
171            } => write_diagnostic(
172                f,
173                "parse error",
174                source_file.as_deref(),
175                *line,
176                *column,
177                message,
178            ),
179            ZuzuRustError::IncompleteParse {
180                message,
181                source_file,
182                line,
183                column,
184            } => write_diagnostic(
185                f,
186                "incomplete parse error",
187                source_file.as_deref(),
188                *line,
189                *column,
190                message,
191            ),
192            ZuzuRustError::Semantic {
193                message,
194                source_file,
195                line,
196                column,
197            } => write_diagnostic(
198                f,
199                "semantic error",
200                source_file.as_deref(),
201                *line,
202                *column,
203                message,
204            ),
205            ZuzuRustError::Thrown { value, .. } => {
206                write!(f, "uncaught exception: {value}")
207            }
208            ZuzuRustError::Runtime { message } => write!(f, "runtime error: {message}"),
209            ZuzuRustError::Cli { message } => write!(f, "{message}"),
210            ZuzuRustError::Io(err) => write!(f, "io error: {err}"),
211        }
212    }
213}
214
215fn write_diagnostic(
216    f: &mut fmt::Formatter<'_>,
217    kind: &str,
218    source_file: Option<&str>,
219    line: usize,
220    column: usize,
221    message: &str,
222) -> fmt::Result {
223    match source_file {
224        Some(source_file) => write!(f, "{kind} at {source_file}:{line}:{column}: {message}"),
225        None => write!(f, "{kind} at {line}:{column}: {message}"),
226    }
227}
228
229impl Error for ZuzuRustError {}
230
231impl From<std::io::Error> for ZuzuRustError {
232    fn from(value: std::io::Error) -> Self {
233        Self::Io(value)
234    }
235}