shape_ast/error/
context.rs1use super::types::{ShapeError, SourceLocation};
7
8pub type Result<T> = std::result::Result<T, ShapeError>;
10
11pub struct ErrorContext<T> {
13 result: std::result::Result<T, ShapeError>,
14}
15
16impl<T> ErrorContext<T> {
17 pub fn new(result: std::result::Result<T, ShapeError>) -> Self {
18 Self { result }
19 }
20
21 pub fn with_location(self, location: SourceLocation) -> std::result::Result<T, ShapeError> {
23 self.result.map_err(|mut e| {
24 match &mut e {
25 ShapeError::ParseError { location: loc, .. }
26 | ShapeError::LexError { location: loc, .. }
27 | ShapeError::SemanticError { location: loc, .. }
28 | ShapeError::RuntimeError { location: loc, .. } => {
29 *loc = Some(location);
30 }
31 _ => {}
32 }
33 e
34 })
35 }
36
37 pub fn context(self, msg: impl Into<String>) -> std::result::Result<T, ShapeError> {
39 self.result
40 .map_err(|e| ShapeError::Custom(format!("{}: {}", msg.into(), e)))
41 }
42}
43
44pub trait ResultExt<T> {
46 fn with_location(self, location: SourceLocation) -> Result<T>;
47 fn with_context(self, msg: impl Into<String>) -> Result<T>;
48}
49
50impl<T> ResultExt<T> for Result<T> {
51 fn with_location(self, location: SourceLocation) -> Result<T> {
52 ErrorContext::new(self).with_location(location)
53 }
54
55 fn with_context(self, msg: impl Into<String>) -> Result<T> {
56 ErrorContext::new(self).context(msg)
57 }
58}
59
60pub fn span_to_location(
65 source: &str,
66 span: crate::ast::Span,
67 file: Option<String>,
68) -> SourceLocation {
69 if span.is_dummy() {
70 let mut loc = SourceLocation::new(1, 1);
72 loc.is_synthetic = true;
73 return loc;
74 }
75
76 let mut line = 1usize;
77 let mut col = 1usize;
78 let mut last_newline_pos = 0usize;
79
80 for (i, ch) in source.char_indices() {
82 if i >= span.start {
83 break;
84 }
85 if ch == '\n' {
86 line += 1;
87 col = 1;
88 last_newline_pos = i + 1;
89 } else {
90 col += 1;
91 }
92 }
93
94 let line_start = last_newline_pos;
96 let line_end = source[span.start.min(source.len())..]
97 .find('\n')
98 .map(|i| span.start.min(source.len()) + i)
99 .unwrap_or(source.len());
100 let source_line = source.get(line_start..line_end).unwrap_or("").to_string();
101
102 let mut loc = SourceLocation::new(line, col)
103 .with_length(span.len())
104 .with_source_line(source_line);
105
106 if let Some(f) = file {
107 loc = loc.with_file(f);
108 }
109
110 loc
111}