busbar_sf_agentscript/
error.rs1use ariadne::{Color, Label, Report, ReportKind, Source};
35use std::fmt;
36
37#[derive(Debug)]
42pub enum AgentScriptError {
43 Parse(ParseErrorInfo),
45 Validation(ValidationError),
47}
48
49impl fmt::Display for AgentScriptError {
50 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51 match self {
52 AgentScriptError::Parse(e) => write!(f, "Parse error: {}", e),
53 AgentScriptError::Validation(e) => write!(f, "Validation error: {}", e),
54 }
55 }
56}
57
58impl std::error::Error for AgentScriptError {}
59
60#[derive(Debug)]
62pub struct ParseErrorInfo {
63 pub message: String,
64 pub span: Option<std::ops::Range<usize>>,
65 pub expected: Vec<String>,
66 pub found: Option<String>,
67 pub contexts: Vec<(String, std::ops::Range<usize>)>,
69}
70
71impl fmt::Display for ParseErrorInfo {
72 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73 write!(f, "{}", self.message)?;
74 if let Some(ref found) = self.found {
75 write!(f, ", found '{}'", found)?;
76 }
77 if !self.expected.is_empty() {
78 write!(f, ", expected one of: {}", self.expected.join(", "))?;
79 }
80 Ok(())
81 }
82}
83
84#[derive(Debug)]
86pub struct ValidationError {
87 pub message: String,
88 pub span: Option<std::ops::Range<usize>>,
89 pub hint: Option<String>,
90}
91
92impl fmt::Display for ValidationError {
93 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94 write!(f, "{}", self.message)?;
95 if let Some(ref hint) = self.hint {
96 write!(f, " (hint: {})", hint)?;
97 }
98 Ok(())
99 }
100}
101
102pub struct ErrorReporter<'src> {
104 source_name: String,
105 source: &'src str,
106}
107
108impl<'src> ErrorReporter<'src> {
109 pub fn new(source_name: impl Into<String>, source: &'src str) -> Self {
111 Self {
112 source_name: source_name.into(),
113 source,
114 }
115 }
116
117 pub fn report_parse_error(&self, error: &ParseErrorInfo) {
119 let span = error.span.clone().unwrap_or(0..0);
120
121 let mut report = Report::build(ReportKind::Error, &self.source_name, span.start)
122 .with_message(&error.message);
123
124 let mut label = Label::new((&self.source_name, span.clone())).with_color(Color::Red);
125
126 if let Some(ref found) = error.found {
127 label = label.with_message(format!("found '{}'", found));
128 }
129
130 report = report.with_label(label);
131
132 for (i, (ctx_label, ctx_span)) in error.contexts.iter().enumerate() {
134 let color = if i == 0 { Color::Yellow } else { Color::Cyan };
135 report = report.with_label(
136 Label::new((&self.source_name, ctx_span.clone()))
137 .with_color(color)
138 .with_message(format!("while parsing {}", ctx_label))
139 .with_order(i as i32 + 1), );
141 }
142
143 if !error.expected.is_empty() {
144 report = report.with_note(format!("expected one of: {}", error.expected.join(", ")));
145 }
146
147 report
148 .finish()
149 .eprint((&self.source_name, Source::from(self.source)))
150 .unwrap();
151 }
152
153 pub fn report_validation_error(&self, error: &ValidationError) {
155 let span = error.span.clone().unwrap_or(0..0);
156
157 let mut report = Report::build(ReportKind::Error, &self.source_name, span.start)
158 .with_message(&error.message)
159 .with_label(
160 Label::new((&self.source_name, span))
161 .with_color(Color::Yellow)
162 .with_message("here"),
163 );
164
165 if let Some(ref hint) = error.hint {
166 report = report.with_help(hint);
167 }
168
169 report
170 .finish()
171 .eprint((&self.source_name, Source::from(self.source)))
172 .unwrap();
173 }
174}
175
176pub type Result<T> = std::result::Result<T, AgentScriptError>;