conjure_cp_essence_parser/
errors.rs1pub use conjure_cp_core::error::Error as ConjureParseError;
2use conjure_cp_core::error::Error;
3use serde_json::Error as JsonError;
4use thiserror::Error as ThisError;
5
6#[derive(Debug, ThisError)]
7pub enum FatalParseError {
8 #[error("Could not parse Essence AST: {0}")]
9 TreeSitterError(String),
10 #[error("Error running `conjure pretty`: {0}")]
11 ConjurePrettyError(String),
12 #[error("Internal parser error: {msg}{}\nThis indicates a bug in the parser or syntax validator. Please report this issue.",
13 match range {
14 Some(range) => format!(" at {}-{}", range.start_point, range.end_point),
15 None => "".to_string(),
16 }
17 )]
18 InternalError {
19 msg: String,
20 range: Option<tree_sitter::Range>,
21 },
22 #[error("JSON Error: {0}")]
23 JsonError(#[from] JsonError),
24 #[error("Error: {0} is not yet implemented.")]
25 NotImplemented(String),
26 #[error("Error: {0}")]
27 Other(Error),
28}
29
30impl FatalParseError {
31 pub fn internal_error(msg: String, range: Option<tree_sitter::Range>) -> Self {
32 FatalParseError::InternalError { msg, range }
33 }
34}
35
36impl From<ConjureParseError> for FatalParseError {
37 fn from(value: ConjureParseError) -> Self {
38 match value {
39 Error::Parse(msg) => FatalParseError::internal_error(msg, None),
40 Error::NotImplemented(msg) => FatalParseError::NotImplemented(msg),
41 Error::Json(err) => FatalParseError::JsonError(err),
42 Error::Other(err) => FatalParseError::Other(err.into()),
43 }
44 }
45}
46
47#[derive(Debug)]
48pub struct RecoverableParseError {
49 pub msg: String,
50 pub range: Option<tree_sitter::Range>,
51 pub file_name: Option<String>,
52 pub source_code: Option<String>,
53}
54
55impl RecoverableParseError {
56 pub fn new(msg: String, range: Option<tree_sitter::Range>) -> Self {
57 Self {
58 msg,
59 range,
60 file_name: None,
61 source_code: None,
62 }
63 }
64
65 pub fn enrich(mut self, file_name: Option<String>, source_code: Option<String>) -> Self {
66 self.file_name = file_name;
67 self.source_code = source_code;
68 self
69 }
70}
71
72impl std::fmt::Display for RecoverableParseError {
73 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
74 if let (Some(range), Some(file_name), Some(source_code)) =
76 (&self.range, &self.file_name, &self.source_code)
77 {
78 let line_num = range.start_point.row + 1; let col_num = range.start_point.column + 1; let lines: Vec<&str> = source_code.lines().collect();
83 let line_content = lines.get(range.start_point.row).unwrap_or(&"");
84
85 let pointer = " ".repeat(range.start_point.column) + "^";
87
88 write!(
89 f,
90 "{}:{}:{}:\n |\n{} | {}\n | {}\n{}",
91 file_name, line_num, col_num, line_num, line_content, pointer, self.msg
92 )
93 } else {
94 write!(f, "Essence syntax error: {}", self.msg)?;
96 if let Some(range) = &self.range {
97 write!(f, " at {}-{}", range.start_point, range.end_point)?;
98 }
99 Ok(())
100 }
101 }
102}
103
104#[derive(Debug)]
106pub struct InstantiateModelError {
107 pub msg: String,
108}
109
110impl std::fmt::Display for InstantiateModelError {
111 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112 write!(f, "{}", self.msg)
113 }
114}
115
116impl std::error::Error for InstantiateModelError {}
117#[derive(Debug)]
119pub enum ParseErrorCollection {
120 Fatal(FatalParseError),
122 Multiple {
124 errors: Vec<RecoverableParseError>,
125 },
126
127 InstantiateModel(InstantiateModelError),
128}
129
130impl ParseErrorCollection {
131 pub fn fatal(error: FatalParseError) -> Self {
133 ParseErrorCollection::Fatal(error)
134 }
135
136 pub fn multiple(
139 errors: Vec<RecoverableParseError>,
140 source_code: Option<String>,
141 file_name: Option<String>,
142 ) -> Self {
143 let enriched_errors = errors
144 .into_iter()
145 .map(|err| err.enrich(file_name.clone(), source_code.clone()))
146 .collect();
147 ParseErrorCollection::Multiple {
148 errors: enriched_errors,
149 }
150 }
151}
152
153impl std::fmt::Display for ParseErrorCollection {
154 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
155 match self {
156 ParseErrorCollection::Fatal(error) => write!(f, "{}", error),
157 ParseErrorCollection::InstantiateModel(error) => write!(f, "{}", error),
158 ParseErrorCollection::Multiple { errors } => {
159 let mut indices: Vec<usize> = (0..errors.len()).collect();
161 indices.sort_by(|&a, &b| {
162 match (&errors[a], &errors[b]) {
163 (
164 RecoverableParseError {
165 range: Some(r1), ..
166 },
167 RecoverableParseError {
168 range: Some(r2), ..
169 },
170 ) => {
171 match r1.start_point.row.cmp(&r2.start_point.row) {
173 std::cmp::Ordering::Equal => {
174 r1.start_point.column.cmp(&r2.start_point.column)
175 }
176 other => other,
177 }
178 }
179 (RecoverableParseError { range: Some(_), .. }, _) => {
181 std::cmp::Ordering::Less
182 }
183 (_, RecoverableParseError { range: Some(_), .. }) => {
184 std::cmp::Ordering::Greater
185 }
186 _ => std::cmp::Ordering::Equal,
187 }
188 });
189
190 for (i, &idx) in indices.iter().enumerate() {
192 if i > 0 {
193 write!(f, "\n\n")?;
194 }
195 write!(f, "{}", errors[idx])?;
196 }
197 Ok(())
198 }
199 }
200 }
201}
202
203impl std::error::Error for ParseErrorCollection {}
204
205#[cfg(test)]
206mod tests {
207 use super::*;
208
209 #[test]
210 fn instantiate_model_error_display_and_error_trait() {
211 let err = InstantiateModelError {
212 msg: "hello".to_string(),
213 };
214
215 assert_eq!(err.to_string(), "hello");
216 let _as_error: &dyn std::error::Error = &err;
217 }
218
219 #[test]
220 fn parse_error_collection_instantiate_model_variant_is_displayed() {
221 let err = ParseErrorCollection::InstantiateModel(InstantiateModelError {
222 msg: "missing param".to_string(),
223 });
224 assert_eq!(err.to_string(), "missing param");
225 }
226
227 #[test]
228 fn parse_error_collection_multiple_constructor_works() {
229 let err = ParseErrorCollection::multiple(
230 vec![RecoverableParseError::new("bad token".to_string(), None)],
231 None,
232 None,
233 );
234 let formatted = err.to_string();
235 assert!(formatted.contains("bad token"));
236 }
237}