use super::types::{ShapeError, SourceLocation};
pub type Result<T> = std::result::Result<T, ShapeError>;
pub struct ErrorContext<T> {
result: std::result::Result<T, ShapeError>,
}
impl<T> ErrorContext<T> {
pub fn new(result: std::result::Result<T, ShapeError>) -> Self {
Self { result }
}
pub fn with_location(self, location: SourceLocation) -> std::result::Result<T, ShapeError> {
self.result.map_err(|mut e| {
match &mut e {
ShapeError::ParseError { location: loc, .. }
| ShapeError::LexError { location: loc, .. }
| ShapeError::SemanticError { location: loc, .. }
| ShapeError::RuntimeError { location: loc, .. } => {
*loc = Some(location);
}
_ => {}
}
e
})
}
pub fn context(self, msg: impl Into<String>) -> std::result::Result<T, ShapeError> {
self.result
.map_err(|e| ShapeError::Custom(format!("{}: {}", msg.into(), e)))
}
}
pub trait ResultExt<T> {
fn with_location(self, location: SourceLocation) -> Result<T>;
fn with_context(self, msg: impl Into<String>) -> Result<T>;
}
impl<T> ResultExt<T> for Result<T> {
fn with_location(self, location: SourceLocation) -> Result<T> {
ErrorContext::new(self).with_location(location)
}
fn with_context(self, msg: impl Into<String>) -> Result<T> {
ErrorContext::new(self).context(msg)
}
}
pub fn span_to_location(
source: &str,
span: crate::ast::Span,
file: Option<String>,
) -> SourceLocation {
if span.is_dummy() {
let mut loc = SourceLocation::new(1, 1);
loc.is_synthetic = true;
return loc;
}
let mut line = 1usize;
let mut col = 1usize;
let mut last_newline_pos = 0usize;
for (i, ch) in source.char_indices() {
if i >= span.start {
break;
}
if ch == '\n' {
line += 1;
col = 1;
last_newline_pos = i + 1;
} else {
col += 1;
}
}
let line_start = last_newline_pos;
let line_end = source[span.start.min(source.len())..]
.find('\n')
.map(|i| span.start.min(source.len()) + i)
.unwrap_or(source.len());
let source_line = source.get(line_start..line_end).unwrap_or("").to_string();
let mut loc = SourceLocation::new(line, col)
.with_length(span.len())
.with_source_line(source_line);
if let Some(f) = file {
loc = loc.with_file(f);
}
loc
}