mirror of
https://github.com/ModernRelay/omnigraph.git
synced 2026-06-09 01:35:18 +02:00
146 lines
3.5 KiB
Rust
146 lines
3.5 KiB
Rust
use thiserror::Error;
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub struct SourceSpan {
|
|
pub start: usize,
|
|
pub end: usize,
|
|
}
|
|
|
|
impl SourceSpan {
|
|
pub fn new(start: usize, end: usize) -> Self {
|
|
Self { start, end }
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
pub struct ParseDiagnostic {
|
|
pub message: String,
|
|
pub span: Option<SourceSpan>,
|
|
}
|
|
|
|
impl ParseDiagnostic {
|
|
pub fn new(message: String, span: Option<SourceSpan>) -> Self {
|
|
Self { message, span }
|
|
}
|
|
}
|
|
|
|
impl std::fmt::Display for ParseDiagnostic {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
write!(f, "{}", self.message)
|
|
}
|
|
}
|
|
|
|
impl std::error::Error for ParseDiagnostic {}
|
|
|
|
pub fn render_span(span: SourceSpan) -> SourceSpan {
|
|
SourceSpan {
|
|
start: span.start,
|
|
end: span.end.max(span.start.saturating_add(1)),
|
|
}
|
|
}
|
|
|
|
pub fn decode_string_literal(raw: &str) -> Result<String> {
|
|
let inner = raw
|
|
.strip_prefix('"')
|
|
.and_then(|inner| inner.strip_suffix('"'))
|
|
.unwrap_or(raw);
|
|
|
|
let mut decoded = String::with_capacity(inner.len());
|
|
let mut chars = inner.chars();
|
|
while let Some(ch) = chars.next() {
|
|
if ch != '\\' {
|
|
decoded.push(ch);
|
|
continue;
|
|
}
|
|
|
|
let escaped = chars
|
|
.next()
|
|
.ok_or_else(|| NanoError::Parse("unterminated escape sequence".to_string()))?;
|
|
match escaped {
|
|
'"' => decoded.push('"'),
|
|
'\\' => decoded.push('\\'),
|
|
'n' => decoded.push('\n'),
|
|
'r' => decoded.push('\r'),
|
|
't' => decoded.push('\t'),
|
|
other => {
|
|
return Err(NanoError::Parse(format!(
|
|
"unsupported escape sequence: \\{}",
|
|
other
|
|
)));
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(decoded)
|
|
}
|
|
|
|
#[derive(Debug, Error)]
|
|
pub enum NanoError {
|
|
#[error("parse error: {0}")]
|
|
Parse(String),
|
|
|
|
#[error("catalog error: {0}")]
|
|
Catalog(String),
|
|
|
|
#[error("type error: {0}")]
|
|
Type(String),
|
|
|
|
#[error("storage error: {0}")]
|
|
Storage(String),
|
|
|
|
#[error(
|
|
"@unique constraint violation on {type_name}.{property}: duplicate value '{value}' at rows {first_row} and {second_row}"
|
|
)]
|
|
UniqueConstraint {
|
|
type_name: String,
|
|
property: String,
|
|
value: String,
|
|
first_row: usize,
|
|
second_row: usize,
|
|
},
|
|
|
|
#[error("plan error: {0}")]
|
|
Plan(String),
|
|
|
|
#[error("execution error: {0}")]
|
|
Execution(String),
|
|
|
|
#[error(transparent)]
|
|
Arrow(#[from] arrow_schema::ArrowError),
|
|
|
|
#[error("io error: {0}")]
|
|
Io(#[from] std::io::Error),
|
|
|
|
#[error("lance error: {0}")]
|
|
Lance(String),
|
|
|
|
#[error("manifest error: {0}")]
|
|
Manifest(String),
|
|
}
|
|
|
|
pub type Result<T> = std::result::Result<T, NanoError>;
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::{SourceSpan, decode_string_literal, render_span};
|
|
|
|
#[test]
|
|
fn source_span_preserves_zero_width() {
|
|
let span = SourceSpan::new(7, 7);
|
|
assert_eq!(span.start, 7);
|
|
assert_eq!(span.end, 7);
|
|
}
|
|
|
|
#[test]
|
|
fn render_span_widens_zero_width_for_diagnostics() {
|
|
let rendered = render_span(SourceSpan::new(7, 7));
|
|
assert_eq!(rendered.start, 7);
|
|
assert_eq!(rendered.end, 8);
|
|
}
|
|
|
|
#[test]
|
|
fn decode_string_literal_supports_common_escapes() {
|
|
let decoded = decode_string_literal("\"a\\n\\r\\t\\\\\\\"b\"").unwrap();
|
|
assert_eq!(decoded, "a\n\r\t\\\"b");
|
|
}
|
|
}
|