tree_sitter_graph/execution/
error.rs1use std::path::Path;
9use thiserror::Error;
10
11use crate::ast::Stanza;
12use crate::ast::Statement;
13use crate::execution::CancellationError;
14use crate::parse_error::Excerpt;
15use crate::Location;
16
17#[derive(Debug, Error)]
19pub enum ExecutionError {
20 #[error(transparent)]
21 Cancelled(#[from] CancellationError),
22 #[error("Cannot assign immutable variable {0}")]
23 CannotAssignImmutableVariable(String),
24 #[error("Cannot assign scoped variable {0}")]
25 CannotAssignScopedVariable(String),
26 #[error("Cannot define mutable scoped variable {0}")]
27 CannotDefineMutableScopedVariable(String),
28 #[error("Duplicate attribute {0}")]
29 DuplicateAttribute(String),
30 #[error("Duplicate edge {0}")]
31 DuplicateEdge(String),
32 #[error("Duplicate variable {0}")]
33 DuplicateVariable(String),
34 #[error("Expected a graph node reference {0}")]
35 ExpectedGraphNode(String),
36 #[error("Expected a list {0}")]
37 ExpectedList(String),
38 #[error("Expected a boolean {0}")]
39 ExpectedBoolean(String),
40 #[error("Expected an integer {0}")]
41 ExpectedInteger(String),
42 #[error("Expected a string {0}")]
43 ExpectedString(String),
44 #[error("Expected a syntax node {0}")]
45 ExpectedSyntaxNode(String),
46 #[error("Invalid parameters {0}")]
47 InvalidParameters(String),
48 #[error("Scoped variables can only be attached to syntax nodes {0}")]
49 InvalidVariableScope(String),
50 #[error("Missing global variable {0}")]
51 MissingGlobalVariable(String),
52 #[error("Recursively defined scoped variable {0}")]
53 RecursivelyDefinedScopedVariable(String),
54 #[error("Recursively defined variable {0}")]
55 RecursivelyDefinedVariable(String),
56 #[error("Undefined capture {0}")]
57 UndefinedCapture(String),
58 #[error("Undefined function {0}")]
59 UndefinedFunction(String),
60 #[error("Undefined regex capture {0}")]
61 UndefinedRegexCapture(String),
62 #[error("Undefined scoped variable {0}")]
63 UndefinedScopedVariable(String),
64 #[error("Empty regex capture {0}")]
65 EmptyRegexCapture(String),
66 #[error("Undefined edge {0}")]
67 UndefinedEdge(String),
68 #[error("Undefined variable {0}")]
69 UndefinedVariable(String),
70 #[error("Cannot add scoped variable after being forced {0}")]
71 VariableScopesAlreadyForced(String),
72 #[error("Function {0} failed: {1}")]
73 FunctionFailed(String, String),
74 #[error("{0}. Caused by: {1}")]
75 InContext(Context, Box<ExecutionError>),
76}
77
78#[derive(Clone, Debug)]
79pub enum Context {
80 Statement(Vec<StatementContext>),
81 Other(String),
82}
83
84#[derive(Clone, Debug)]
85pub struct StatementContext {
86 pub statement: String,
87 pub statement_location: Location,
88 pub stanza_location: Location,
89 pub source_location: Location,
90 pub node_kind: String,
91}
92
93impl StatementContext {
94 pub(crate) fn new(stmt: &Statement, stanza: &Stanza, source_node: &tree_sitter::Node) -> Self {
95 Self {
96 statement: format!("{}", stmt),
97 statement_location: stmt.location(),
98 stanza_location: stanza.range.start,
99 source_location: Location::from(source_node.range().start_point),
100 node_kind: source_node.kind().to_string(),
101 }
102 }
103
104 pub(crate) fn update_statement(&mut self, stmt: &Statement) {
105 self.statement = format!("{}", stmt);
106 self.statement_location = stmt.location();
107 }
108}
109
110impl From<StatementContext> for Context {
111 fn from(value: StatementContext) -> Self {
112 Self::Statement(vec![value])
113 }
114}
115
116impl From<(StatementContext, StatementContext)> for Context {
117 fn from((left, right): (StatementContext, StatementContext)) -> Self {
118 Self::Statement(vec![left, right])
119 }
120}
121
122impl From<String> for Context {
123 fn from(value: String) -> Self {
124 Self::Other(value)
125 }
126}
127
128impl std::fmt::Display for Context {
129 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
130 match self {
131 Self::Statement(stmts) => {
132 let mut first = true;
133 for stmt in stmts {
134 stmt.fmt(f, first)?;
135 first = false;
136 }
137 }
138 Self::Other(msg) => write!(f, "{}", msg)?,
139 }
140 Ok(())
141 }
142}
143
144impl StatementContext {
145 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, first: bool) -> std::fmt::Result {
146 if first {
147 write!(f, "Error executing",)?;
148 } else {
149 write!(f, " and executing",)?;
150 }
151 write!(
152 f,
153 " {} in stanza at {} matching ({}) node at {}",
154 self.statement, self.stanza_location, self.node_kind, self.source_location
155 )?;
156 Ok(())
157 }
158}
159
160pub(super) trait ResultWithExecutionError<R> {
161 fn with_context<F>(self, with_context: F) -> Result<R, ExecutionError>
162 where
163 F: FnOnce() -> Context;
164}
165
166impl<R> ResultWithExecutionError<R> for Result<R, ExecutionError> {
167 fn with_context<F>(self, with_context: F) -> Result<R, ExecutionError>
168 where
169 F: FnOnce() -> Context,
170 {
171 self.map_err(|e| match e {
172 cancelled @ ExecutionError::Cancelled(_) => cancelled,
173 in_other_context @ ExecutionError::InContext(Context::Other(_), _) => {
174 ExecutionError::InContext(with_context(), Box::new(in_other_context))
175 }
176 in_stmt_context @ ExecutionError::InContext(_, _) => in_stmt_context,
177 _ => ExecutionError::InContext(with_context(), Box::new(e)),
178 })
179 }
180}
181
182impl ExecutionError {
183 pub fn display_pretty<'a>(
184 &'a self,
185 source_path: &'a Path,
186 source: &'a str,
187 tsg_path: &'a Path,
188 tsg: &'a str,
189 ) -> impl std::fmt::Display + 'a {
190 DisplayExecutionErrorPretty {
191 error: self,
192 source_path,
193 source,
194 tsg_path,
195 tsg,
196 }
197 }
198}
199
200struct DisplayExecutionErrorPretty<'a> {
201 error: &'a ExecutionError,
202 source_path: &'a Path,
203 source: &'a str,
204 tsg_path: &'a Path,
205 tsg: &'a str,
206}
207
208impl std::fmt::Display for DisplayExecutionErrorPretty<'_> {
209 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
210 self.fmt_entry(f, 0, self.error)
211 }
212}
213
214impl DisplayExecutionErrorPretty<'_> {
215 fn fmt_entry(
216 &self,
217 f: &mut std::fmt::Formatter<'_>,
218 index: usize,
219 error: &ExecutionError,
220 ) -> std::fmt::Result {
221 match error {
222 ExecutionError::InContext(context, cause) => {
223 match context {
224 Context::Statement(stmts) => {
225 let mut first = true;
226 for stmt in stmts {
227 stmt.fmt_pretty(
228 f,
229 self.source_path,
230 self.source,
231 self.tsg_path,
232 self.tsg,
233 index,
234 first,
235 )?;
236 first = false;
237 }
238 }
239 Context::Other(msg) => writeln!(f, "{:>5}: {}", index, msg)?,
240 };
241 self.fmt_entry(f, index + 1, cause)?;
242 Ok(())
243 }
244 other => writeln!(f, "{:>5}: {}", index, other),
245 }
246 }
247}
248
249impl StatementContext {
250 fn fmt_pretty(
251 &self,
252 f: &mut std::fmt::Formatter<'_>,
253 source_path: &Path,
254 source: &str,
255 tsg_path: &Path,
256 tsg: &str,
257 index: usize,
258 first: bool,
259 ) -> std::fmt::Result {
260 if first {
261 writeln!(
262 f,
263 "{:>5}: Error executing statement {}",
264 index, self.statement
265 )?;
266 } else {
267 writeln!(f, " > and executing statement {}", self.statement)?;
268 }
269 write!(
270 f,
271 "{}",
272 Excerpt::from_source(
273 tsg_path,
274 tsg,
275 self.statement_location.row,
276 self.statement_location.to_column_range(),
277 7
278 )
279 )?;
280 writeln!(f, "{}in stanza", " ".repeat(7))?;
281 write!(
282 f,
283 "{}",
284 Excerpt::from_source(
285 tsg_path,
286 tsg,
287 self.stanza_location.row,
288 self.stanza_location.to_column_range(),
289 7
290 )
291 )?;
292 writeln!(f, "{}matching ({}) node", " ".repeat(7), self.node_kind)?;
293 write!(
294 f,
295 "{}",
296 Excerpt::from_source(
297 source_path,
298 source,
299 self.source_location.row,
300 self.source_location.to_column_range(),
301 7
302 )
303 )?;
304 Ok(())
305 }
306}