surrealdb_core/syn/error/
mod.rs

1use crate::syn::token::Span;
2use std::fmt::Display;
3
4mod location;
5mod mac;
6mod render;
7pub use location::Location;
8pub(crate) use mac::{bail, syntax_error};
9pub use render::{RenderedError, Snippet};
10
11#[derive(Debug, Clone, Copy)]
12pub enum MessageKind {
13	Suggestion,
14	Error,
15}
16
17#[derive(Debug)]
18enum DiagnosticKind {
19	Cause(String),
20	Span {
21		kind: MessageKind,
22		span: Span,
23		label: Option<String>,
24	},
25}
26
27#[derive(Debug)]
28pub struct Diagnostic {
29	kind: DiagnosticKind,
30	next: Option<Box<Diagnostic>>,
31}
32
33/// A parsing error.
34#[derive(Debug)]
35pub struct SyntaxError {
36	diagnostic: Box<Diagnostic>,
37	data_pending: bool,
38}
39
40impl SyntaxError {
41	/// Create a new parse error.
42	pub fn new<T>(message: T) -> Self
43	where
44		T: Display,
45	{
46		let diagnostic = Diagnostic {
47			kind: DiagnosticKind::Cause(message.to_string()),
48			next: None,
49		};
50
51		Self {
52			diagnostic: Box::new(diagnostic),
53			data_pending: false,
54		}
55	}
56
57	/// Returns whether this error is possibly the result of missing data.
58	pub fn is_data_pending(&self) -> bool {
59		self.data_pending
60	}
61
62	/// Indicate that this error might be the result of missing data and could be resolved with
63	/// more data.
64	pub fn with_data_pending(mut self) -> Self {
65		self.data_pending = true;
66		self
67	}
68
69	pub fn with_span(mut self, span: Span, kind: MessageKind) -> Self {
70		self.diagnostic = Box::new(Diagnostic {
71			kind: DiagnosticKind::Span {
72				kind,
73				span,
74				label: None,
75			},
76			next: Some(self.diagnostic),
77		});
78		self
79	}
80
81	pub fn with_labeled_span<T: Display>(
82		mut self,
83		span: Span,
84		kind: MessageKind,
85		label: T,
86	) -> Self {
87		self.diagnostic = Box::new(Diagnostic {
88			kind: DiagnosticKind::Span {
89				kind,
90				span,
91				label: Some(label.to_string()),
92			},
93			next: Some(self.diagnostic),
94		});
95		self
96	}
97
98	pub fn with_cause<T: Display>(mut self, t: T) -> Self {
99		self.diagnostic = Box::new(Diagnostic {
100			kind: DiagnosticKind::Cause(t.to_string()),
101			next: Some(self.diagnostic),
102		});
103		self
104	}
105
106	pub fn render_on(&self, source: &str) -> RenderedError {
107		let mut res = RenderedError {
108			errors: Vec::new(),
109			snippets: Vec::new(),
110		};
111		Self::render_on_inner(&self.diagnostic, source, &mut res);
112		res
113	}
114
115	pub fn render_on_bytes(&self, source: &[u8]) -> RenderedError {
116		let source = String::from_utf8_lossy(source);
117		self.render_on(&source)
118	}
119
120	fn render_on_inner(diagnostic: &Diagnostic, source: &str, res: &mut RenderedError) {
121		if let Some(ref x) = diagnostic.next {
122			Self::render_on_inner(x, source, res);
123		}
124
125		match diagnostic.kind {
126			DiagnosticKind::Cause(ref x) => res.errors.push(x.clone()),
127			DiagnosticKind::Span {
128				ref span,
129				ref label,
130				ref kind,
131			} => {
132				let locations = Location::range_of_span(source, *span);
133				let snippet = Snippet::from_source_location_range(
134					source,
135					locations,
136					label.as_ref().map(|x| x.as_str()),
137					*kind,
138				);
139				res.snippets.push(snippet)
140			}
141		}
142	}
143}