1use itertools::Itertools;
3
4use crate::{GPosIdx, Id, WithPos};
5
6pub type CalyxResult<T> = std::result::Result<T, Error>;
8
9#[derive(Clone)]
11pub struct Error {
12 kind: Box<ErrorKind>,
13 pos: GPosIdx,
14 annotations: Vec<(GPosIdx, String)>,
15 post_msg: Option<String>,
16}
17
18pub struct MultiError {
20 errors: Vec<Error>,
21}
22
23impl From<Vec<Error>> for MultiError {
24 fn from(errors: Vec<Error>) -> Self {
25 MultiError { errors }
26 }
27}
28
29impl std::fmt::Debug for Error {
30 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31 if self.pos == GPosIdx::UNKNOWN {
32 write!(f, "{}", self.kind)?
33 } else {
34 write!(f, "{}", self.pos.format(self.kind.to_string()))?;
35 for (other_pos, msg) in &self.annotations {
36 write!(f, "\n...\n{}", other_pos.format_raw(msg))?;
37 }
38 }
39 if let Some(post) = &self.post_msg {
40 write!(f, "\n{post}")?;
41 }
42 Ok(())
43 }
44}
45
46impl std::fmt::Debug for MultiError {
47 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48 let errors = self.errors.iter().map(|e| format!("{e:?}")).join("\n\n");
49 write!(f, "{errors}")
50 }
51}
52
53impl Error {
54 pub fn with_pos<T: WithPos>(mut self, pos: &T) -> Self {
55 self.pos = pos.copy_span();
56 self
57 }
58
59 pub fn with_annotation<T: WithPos, S: ToString>(
60 mut self,
61 pos: &T,
62 msg: S,
63 ) -> Self {
64 self.annotations.push((pos.copy_span(), msg.to_string()));
65 self
66 }
67
68 pub fn with_annotations<T: WithPos, S: ToString>(
69 mut self,
70 pos_iter: impl Iterator<Item = (T, S)>,
71 ) -> Self {
72 self.annotations.extend(
73 pos_iter.map(|(pos, msg)| (pos.copy_span(), msg.to_string())),
74 );
75 self
76 }
77
78 pub fn with_post_msg(mut self, msg: Option<String>) -> Self {
79 self.post_msg = msg;
80 self
81 }
82
83 pub fn reserved_name(name: Id) -> Self {
84 Self {
85 kind: Box::new(ErrorKind::ReservedName(name)),
86 pos: GPosIdx::UNKNOWN,
87 annotations: vec![],
88 post_msg: None,
89 }
90 }
91 pub fn malformed_control<S: ToString>(msg: S) -> Self {
92 Self {
93 kind: Box::new(ErrorKind::MalformedControl(msg.to_string())),
94 pos: GPosIdx::UNKNOWN,
95 annotations: vec![],
96 post_msg: None,
97 }
98 }
99 pub fn malformed_structure<S: ToString>(msg: S) -> Self {
100 Self {
101 kind: Box::new(ErrorKind::MalformedStructure(msg.to_string())),
102 pos: GPosIdx::UNKNOWN,
103 annotations: vec![],
104 post_msg: None,
105 }
106 }
107 pub fn pass_assumption<S: ToString, M: ToString>(pass: S, msg: M) -> Self {
108 Self {
109 kind: Box::new(ErrorKind::PassAssumption(
110 pass.to_string(),
111 msg.to_string(),
112 )),
113 pos: GPosIdx::UNKNOWN,
114 annotations: vec![],
115 post_msg: None,
116 }
117 }
118 pub fn undefined<S: ToString>(name: Id, typ: S) -> Self {
119 Self {
120 kind: Box::new(ErrorKind::Undefined(name, typ.to_string())),
121 pos: GPosIdx::UNKNOWN,
122 annotations: vec![],
123 post_msg: None,
124 }
125 }
126 pub fn already_bound<S: ToString>(name: Id, typ: S) -> Self {
127 Self {
128 kind: Box::new(ErrorKind::AlreadyBound(name, typ.to_string())),
129 pos: GPosIdx::UNKNOWN,
130 annotations: vec![],
131 post_msg: None,
132 }
133 }
134 pub fn unused<S: ToString>(group: Id, typ: S) -> Self {
135 Self {
136 kind: Box::new(ErrorKind::Unused(group, typ.to_string())),
137 pos: GPosIdx::UNKNOWN,
138 annotations: vec![],
139 post_msg: None,
140 }
141 }
142 pub fn papercut<S: ToString>(msg: S) -> Self {
143 Self {
144 kind: Box::new(ErrorKind::Papercut(msg.to_string())),
145 pos: GPosIdx::UNKNOWN,
146 annotations: vec![],
147 post_msg: None,
148 }
149 }
150 pub fn misc<S: ToString>(msg: S) -> Self {
151 Self {
152 kind: Box::new(ErrorKind::Misc(msg.to_string())),
153 pos: GPosIdx::UNKNOWN,
154 annotations: vec![],
155 post_msg: None,
156 }
157 }
158 pub fn parse_error<S: ToString>(msg: S) -> Self {
159 Self {
160 kind: Box::new(ErrorKind::Parse),
161 pos: GPosIdx::UNKNOWN,
162 annotations: vec![],
163 post_msg: Some(msg.to_string()),
164 }
165 }
166 pub fn invalid_file<S: ToString>(msg: S) -> Self {
167 Self {
168 kind: Box::new(ErrorKind::InvalidFile(msg.to_string())),
169 pos: GPosIdx::UNKNOWN,
170 annotations: vec![],
171 post_msg: None,
172 }
173 }
174 pub fn write_error<S: ToString>(msg: S) -> Self {
175 Self {
176 kind: Box::new(ErrorKind::WriteError(msg.to_string())),
177 pos: GPosIdx::UNKNOWN,
178 annotations: vec![],
179 post_msg: None,
180 }
181 }
182 pub fn location(&self) -> (&str, usize, usize) {
183 self.pos.get_location()
184 }
185 pub fn message(&self) -> String {
186 self.kind.to_string()
187 }
188 pub fn annotations(&self) -> Vec<(String, usize, usize)> {
189 self.annotations
190 .iter()
191 .map(|(pos, msg)| {
192 let (_, s, e) = pos.get_location();
193 (msg.to_string(), s, e)
194 })
195 .collect()
196 }
197}
198
199#[derive(Clone)]
201enum ErrorKind {
202 ReservedName(Id),
204
205 MalformedControl(String),
207 MalformedStructure(String),
209
210 PassAssumption(String, String),
212
213 Undefined(Id, String),
215 AlreadyBound(Id, String),
217
218 Unused(Id, String),
220
221 Papercut(String),
223
224 Parse,
227 Misc(String),
229 InvalidFile(String),
231 WriteError(String),
233}
234
235impl std::fmt::Display for ErrorKind {
236 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
237 use ErrorKind::*;
238 match self {
239 Papercut(msg) => {
240 write!(f, "[Papercut] {msg}")
241 }
242 Unused(name, typ) => {
243 write!(f, "Unused {typ} `{name}'")
244 }
245 AlreadyBound(name, bound_by) => {
246 write!(f, "Name `{name}' already bound by {bound_by}")
247 }
248 ReservedName(name) => {
249 write!(f, "Use of reserved keyword: {name}")
250 }
251 Undefined(name, typ) => {
252 write!(f, "Undefined {typ} name: {name}")
253 }
254 MalformedControl(msg) => write!(f, "Malformed Control: {msg}"),
255 PassAssumption(pass, msg) => {
256 write!(f, "Pass `{pass}` assumption violated: {msg}")
257 }
258 MalformedStructure(msg) => {
259 write!(f, "Malformed Structure: {msg}")
260 }
261 Parse => {
262 write!(f, "Parse error")
263 }
264 InvalidFile(msg) | WriteError(msg) | Misc(msg) => {
265 write!(f, "{msg}")
266 }
267 }
268 }
269}
270
271impl From<std::str::Utf8Error> for Error {
274 fn from(err: std::str::Utf8Error) -> Self {
275 Error::invalid_file(err.to_string())
276 }
277}
278
279impl From<std::io::Error> for Error {
280 fn from(e: std::io::Error) -> Self {
281 Error::write_error(format!("IO Error: {e}"))
282 }
283}
284
285impl From<serde_json::Error> for Error {
286 fn from(e: serde_json::Error) -> Self {
287 Error::write_error(format!("serde_json Error: {e}"))
288 }
289}
290
291impl From<std::str::Utf8Error> for MultiError {
292 fn from(value: std::str::Utf8Error) -> Self {
293 MultiError {
294 errors: vec![value.into()],
295 }
296 }
297}
298
299impl From<std::io::Error> for MultiError {
300 fn from(value: std::io::Error) -> Self {
301 MultiError {
302 errors: vec![value.into()],
303 }
304 }
305}
306
307impl From<serde_json::Error> for MultiError {
308 fn from(value: serde_json::Error) -> Self {
309 MultiError {
310 errors: vec![value.into()],
311 }
312 }
313}
314
315impl From<Error> for MultiError {
316 fn from(value: Error) -> Self {
317 MultiError {
318 errors: vec![value],
319 }
320 }
321}