prqlc_parser/
error.rs

1use std::fmt::Debug;
2
3use serde::Serialize;
4
5use crate::span::Span;
6
7/// A prqlc error. Used internally, exposed as prqlc::ErrorMessage.
8#[derive(Debug, Clone)]
9pub struct Error {
10    /// Message kind. Currently only Error is implemented.
11    pub kind: MessageKind,
12    pub span: Option<Span>,
13    pub reason: Reason,
14    pub hints: Vec<String>,
15    /// Machine readable identifier error code eg, "E0001"
16    pub code: Option<&'static str>,
17    // pub source: ErrorSource
18}
19
20#[derive(Clone, Debug, Default)]
21pub enum ErrorSource {
22    Lexer(String),
23    #[default]
24    Unknown,
25    NameResolver,
26    TypeResolver,
27    SQL,
28    Internal {
29        message: String,
30    },
31}
32
33/// Multiple prqlc errors. Used internally, exposed as prqlc::ErrorMessages.
34#[derive(Debug, Clone)]
35pub struct Errors(pub Vec<Error>);
36
37/// Compile message kind. Currently only Error is implemented.
38#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
39pub enum MessageKind {
40    Error,
41    Warning,
42    Lint,
43}
44
45#[derive(Debug, Clone)]
46pub enum Reason {
47    Simple(String),
48    Expected {
49        /// Where we were
50        // (could rename to `where` / `location` / `within`?)
51        who: Option<String>,
52        /// What we expected
53        expected: String,
54        /// What we found
55        found: String,
56    },
57    Unexpected {
58        found: String,
59    },
60    NotFound {
61        name: String,
62        namespace: String,
63    },
64    Bug {
65        issue: Option<i32>,
66        details: Option<String>,
67    },
68    Internal {
69        message: String,
70    },
71}
72
73impl Error {
74    pub fn new(reason: Reason) -> Self {
75        Error {
76            kind: MessageKind::Error,
77            span: None,
78            reason,
79            hints: Vec::new(),
80            code: None,
81            // source: ErrorSource::default()
82        }
83    }
84
85    pub fn new_simple<S: ToString>(reason: S) -> Self {
86        Error::new(Reason::Simple(reason.to_string()))
87    }
88
89    pub fn new_bug(issue_no: i32) -> Self {
90        Error::new(Reason::Bug {
91            issue: Some(issue_no),
92            details: None,
93        })
94    }
95
96    /// Used for things that you *think* should never happen, but are not sure.
97    pub fn new_assert<S: ToString>(details: S) -> Self {
98        Error::new(Reason::Bug {
99            issue: None,
100            details: Some(details.to_string()),
101        })
102    }
103}
104
105impl std::fmt::Display for Reason {
106    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107        match self {
108            Reason::Simple(text) => f.write_str(text),
109            Reason::Expected {
110                who,
111                expected,
112                found,
113            } => {
114                if let Some(who) = who {
115                    write!(f, "{who} ")?;
116                }
117                write!(f, "expected {expected}, but found {found}")
118            }
119            Reason::Unexpected { found } => write!(f, "unexpected {found}"),
120            Reason::NotFound { name, namespace } => write!(f, "{namespace} `{name}` not found"),
121            Reason::Bug { issue, details } => {
122                write!(f, "internal compiler error")?;
123                if let Some(details) = details {
124                    write!(f, "; {details}")?;
125                }
126                if let Some(issue_no) = issue {
127                    write!(
128                        f,
129                        "; tracked at https://github.com/PRQL/prql/issues/{issue_no}"
130                    )?;
131                }
132                Ok(())
133            }
134            Reason::Internal { message } => {
135                write!(f, "internal error: {message}")
136            }
137        }
138    }
139}
140
141impl From<Error> for Errors {
142    fn from(error: Error) -> Self {
143        Errors(vec![error])
144    }
145}
146
147// Needed for anyhow
148impl std::error::Error for Error {}
149
150// Needed for anyhow
151impl std::error::Error for Errors {}
152
153// Needed for StdError
154impl std::fmt::Display for Error {
155    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
156        Debug::fmt(&self, f)
157    }
158}
159
160// Needed for StdError
161impl std::fmt::Display for Errors {
162    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
163        Debug::fmt(&self, f)
164    }
165}
166
167pub trait WithErrorInfo: Sized {
168    fn push_hint<S: Into<String>>(self, hint: S) -> Self;
169
170    fn with_hints<S: Into<String>, I: IntoIterator<Item = S>>(self, hints: I) -> Self;
171
172    fn with_span(self, span: Option<Span>) -> Self;
173
174    fn with_span_fallback(self, span: Option<Span>) -> Self;
175
176    fn with_code(self, code: &'static str) -> Self;
177
178    fn with_source(self, source: ErrorSource) -> Self;
179}
180
181impl WithErrorInfo for Error {
182    fn push_hint<S: Into<String>>(mut self, hint: S) -> Self {
183        self.hints.push(hint.into());
184        self
185    }
186
187    fn with_hints<S: Into<String>, I: IntoIterator<Item = S>>(mut self, hints: I) -> Self {
188        self.hints = hints.into_iter().map(|x| x.into()).collect();
189        self
190    }
191
192    fn with_span(mut self, span: Option<Span>) -> Self {
193        self.span = span;
194        self
195    }
196
197    fn with_code(mut self, code: &'static str) -> Self {
198        self.code = Some(code);
199        self
200    }
201
202    fn with_span_fallback(mut self, span: Option<Span>) -> Self {
203        self.span = self.span.or(span);
204        self
205    }
206
207    fn with_source(self, _source: ErrorSource) -> Self {
208        // self.source = source;
209        self
210    }
211}
212
213impl<T, E: WithErrorInfo> WithErrorInfo for Result<T, E> {
214    fn push_hint<S: Into<String>>(self, hint: S) -> Self {
215        self.map_err(|e| e.push_hint(hint))
216    }
217
218    fn with_hints<S: Into<String>, I: IntoIterator<Item = S>>(self, hints: I) -> Self {
219        self.map_err(|e| e.with_hints(hints))
220    }
221
222    fn with_span(self, span: Option<Span>) -> Self {
223        self.map_err(|e| e.with_span(span))
224    }
225
226    fn with_span_fallback(self, span: Option<Span>) -> Self {
227        self.map_err(|e| e.with_span_fallback(span))
228    }
229
230    fn with_code(self, code: &'static str) -> Self {
231        self.map_err(|e| e.with_code(code))
232    }
233
234    fn with_source(self, source: ErrorSource) -> Self {
235        self.map_err(|e| e.with_source(source))
236    }
237}