omnigraph_compiler/
error.rs1use thiserror::Error;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4pub struct SourceSpan {
5 pub start: usize,
6 pub end: usize,
7}
8
9impl SourceSpan {
10 pub fn new(start: usize, end: usize) -> Self {
11 Self { start, end }
12 }
13}
14
15#[derive(Debug, Clone, PartialEq, Eq)]
16pub struct ParseDiagnostic {
17 pub message: String,
18 pub span: Option<SourceSpan>,
19}
20
21impl ParseDiagnostic {
22 pub fn new(message: String, span: Option<SourceSpan>) -> Self {
23 Self { message, span }
24 }
25}
26
27impl std::fmt::Display for ParseDiagnostic {
28 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29 write!(f, "{}", self.message)
30 }
31}
32
33impl std::error::Error for ParseDiagnostic {}
34
35pub fn render_span(span: SourceSpan) -> SourceSpan {
36 SourceSpan {
37 start: span.start,
38 end: span.end.max(span.start.saturating_add(1)),
39 }
40}
41
42pub fn decode_string_literal(raw: &str) -> Result<String> {
43 let inner = raw
44 .strip_prefix('"')
45 .and_then(|inner| inner.strip_suffix('"'))
46 .unwrap_or(raw);
47
48 let mut decoded = String::with_capacity(inner.len());
49 let mut chars = inner.chars();
50 while let Some(ch) = chars.next() {
51 if ch != '\\' {
52 decoded.push(ch);
53 continue;
54 }
55
56 let escaped = chars
57 .next()
58 .ok_or_else(|| NanoError::Parse("unterminated escape sequence".to_string()))?;
59 match escaped {
60 '"' => decoded.push('"'),
61 '\\' => decoded.push('\\'),
62 'n' => decoded.push('\n'),
63 'r' => decoded.push('\r'),
64 't' => decoded.push('\t'),
65 other => {
66 return Err(NanoError::Parse(format!(
67 "unsupported escape sequence: \\{}",
68 other
69 )));
70 }
71 }
72 }
73
74 Ok(decoded)
75}
76
77#[derive(Debug, Error)]
78pub enum NanoError {
79 #[error("parse error: {0}")]
80 Parse(String),
81
82 #[error("catalog error: {0}")]
83 Catalog(String),
84
85 #[error("type error: {0}")]
86 Type(String),
87
88 #[error("storage error: {0}")]
89 Storage(String),
90
91 #[error(
92 "@unique constraint violation on {type_name}.{property}: duplicate value '{value}' at rows {first_row} and {second_row}"
93 )]
94 UniqueConstraint {
95 type_name: String,
96 property: String,
97 value: String,
98 first_row: usize,
99 second_row: usize,
100 },
101
102 #[error("plan error: {0}")]
103 Plan(String),
104
105 #[error("execution error: {0}")]
106 Execution(String),
107
108 #[error(transparent)]
109 Arrow(#[from] arrow_schema::ArrowError),
110
111 #[error("io error: {0}")]
112 Io(#[from] std::io::Error),
113
114 #[error("lance error: {0}")]
115 Lance(String),
116
117 #[error("manifest error: {0}")]
118 Manifest(String),
119}
120
121pub type Result<T> = std::result::Result<T, NanoError>;
122
123#[cfg(test)]
124mod tests {
125 use super::{SourceSpan, decode_string_literal, render_span};
126
127 #[test]
128 fn source_span_preserves_zero_width() {
129 let span = SourceSpan::new(7, 7);
130 assert_eq!(span.start, 7);
131 assert_eq!(span.end, 7);
132 }
133
134 #[test]
135 fn render_span_widens_zero_width_for_diagnostics() {
136 let rendered = render_span(SourceSpan::new(7, 7));
137 assert_eq!(rendered.start, 7);
138 assert_eq!(rendered.end, 8);
139 }
140
141 #[test]
142 fn decode_string_literal_supports_common_escapes() {
143 let decoded = decode_string_literal("\"a\\n\\r\\t\\\\\\\"b\"").unwrap();
144 assert_eq!(decoded, "a\n\r\t\\\"b");
145 }
146}