use ariadne::{Color, Label, Report, ReportKind, Source};
use std::fmt;
#[derive(Debug)]
pub enum AgentScriptError {
Parse(ParseErrorInfo),
Validation(ValidationError),
}
impl fmt::Display for AgentScriptError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AgentScriptError::Parse(e) => write!(f, "Parse error: {}", e),
AgentScriptError::Validation(e) => write!(f, "Validation error: {}", e),
}
}
}
impl std::error::Error for AgentScriptError {}
#[derive(Debug)]
pub struct ParseErrorInfo {
pub message: String,
pub span: Option<std::ops::Range<usize>>,
pub expected: Vec<String>,
pub found: Option<String>,
pub contexts: Vec<(String, std::ops::Range<usize>)>,
}
impl fmt::Display for ParseErrorInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.message)?;
if let Some(ref found) = self.found {
write!(f, ", found '{}'", found)?;
}
if !self.expected.is_empty() {
write!(f, ", expected one of: {}", self.expected.join(", "))?;
}
Ok(())
}
}
#[derive(Debug)]
pub struct ValidationError {
pub message: String,
pub span: Option<std::ops::Range<usize>>,
pub hint: Option<String>,
}
impl fmt::Display for ValidationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.message)?;
if let Some(ref hint) = self.hint {
write!(f, " (hint: {})", hint)?;
}
Ok(())
}
}
pub struct ErrorReporter<'src> {
source_name: String,
source: &'src str,
}
impl<'src> ErrorReporter<'src> {
pub fn new(source_name: impl Into<String>, source: &'src str) -> Self {
Self {
source_name: source_name.into(),
source,
}
}
pub fn report_parse_error(&self, error: &ParseErrorInfo) {
let span = error.span.clone().unwrap_or(0..0);
let mut report = Report::build(ReportKind::Error, &self.source_name, span.start)
.with_message(&error.message);
let mut label = Label::new((&self.source_name, span.clone())).with_color(Color::Red);
if let Some(ref found) = error.found {
label = label.with_message(format!("found '{}'", found));
}
report = report.with_label(label);
for (i, (ctx_label, ctx_span)) in error.contexts.iter().enumerate() {
let color = if i == 0 { Color::Yellow } else { Color::Cyan };
report = report.with_label(
Label::new((&self.source_name, ctx_span.clone()))
.with_color(color)
.with_message(format!("while parsing {}", ctx_label))
.with_order(i as i32 + 1), );
}
if !error.expected.is_empty() {
report = report.with_note(format!("expected one of: {}", error.expected.join(", ")));
}
report
.finish()
.eprint((&self.source_name, Source::from(self.source)))
.unwrap();
}
pub fn report_validation_error(&self, error: &ValidationError) {
let span = error.span.clone().unwrap_or(0..0);
let mut report = Report::build(ReportKind::Error, &self.source_name, span.start)
.with_message(&error.message)
.with_label(
Label::new((&self.source_name, span))
.with_color(Color::Yellow)
.with_message("here"),
);
if let Some(ref hint) = error.hint {
report = report.with_help(hint);
}
report
.finish()
.eprint((&self.source_name, Source::from(self.source)))
.unwrap();
}
}
pub type Result<T> = std::result::Result<T, AgentScriptError>;