use crate::*;
pub use ariadne::{Cache, ReportKind};
pub trait Sources {
fn sources(self) -> impl ariadne::Cache<String>;
}
impl<I, S> Sources for I
where
I: IntoIterator<Item = (String, S)>,
S: AsRef<str>,
{
fn sources(self) -> impl ariadne::Cache<String> {
ariadne::sources(self)
}
}
pub struct Report {
builder: ariadne::ReportBuilder<'static, (String, std::ops::Range<usize>)>,
}
fn get_expansion_string(expansion_depth: usize, is_first: bool) -> String {
if expansion_depth == 0 {
"Original token".to_string()
} else if (expansion_depth == 1) && is_first {
"Expanded here".to_string()
} else {
let suffix = match expansion_depth % 10 {
1 => "st",
2 => "nd",
3 => "rd",
_ => "th",
};
format!("Expanded here {}{}", expansion_depth, suffix)
}
}
impl<'a> Report {
pub fn new<C, M>(
kind: ariadne::ReportKind<'static>,
span: &Span<'a>,
code: C,
msg: M,
) -> Self
where
C: std::fmt::Display,
M: ToString,
{
Self {
builder: ariadne::Report::build(
kind,
(span.file.to_string(), span.bytes.clone()),
)
.with_config(
ariadne::Config::new()
.with_index_type(ariadne::IndexType::Byte),
)
.with_code(code)
.with_message(msg),
}
}
pub fn with_label<M>(
mut self,
span: &Span<'a>,
kind: ariadne::ReportKind<'static>,
msg: M,
) -> Self
where
M: ToString,
{
let mut curr_span: &Span<'a> = span;
let mut expansion_depth: usize = curr_span.expansion_depth();
let mut expanded = false;
let color = match kind {
ReportKind::Error => Color::Red,
ReportKind::Warning => Color::Yellow,
ReportKind::Advice => Color::Fixed(147),
ReportKind::Custom(_, color) => color.clone(),
};
loop {
if let Some(expanded_span) = curr_span.expanded_from {
self.builder = self.builder.with_label(
Label::new((
curr_span.file.to_string(),
curr_span.bytes.clone(),
))
.with_message(get_expansion_string(
expansion_depth,
expanded == false,
))
.with_color(Color::BrightGreen),
);
curr_span = expanded_span;
expansion_depth -= 1;
expanded = true;
} else {
break;
}
}
if expanded {
self.builder = self.builder.with_label(
Label::new((
curr_span.file.to_string(),
curr_span.bytes.clone(),
))
.with_message(get_expansion_string(expansion_depth, false))
.with_color(Color::BrightGreen),
);
}
curr_span = &span;
self.builder = self.builder.with_label(
Label::new((curr_span.file.to_string(), curr_span.bytes.clone()))
.with_message(msg)
.with_color(color)
.with_priority(1),
);
let mut note = "".to_string();
let mut note_pad = "".to_string();
let total_inclusion_depth = curr_span.inclusion_depth();
let mut curr_inclusion_depth = 0;
loop {
if (curr_inclusion_depth == 3) & (total_inclusion_depth > 7) {
for _ in 7..=total_inclusion_depth {
curr_span = curr_span.included_from.unwrap();
}
note = format!("{}\n{}╰- ...", note, note_pad);
note_pad += " ";
}
if let Some(included_span) = curr_span.included_from {
curr_inclusion_depth += 1;
curr_span = included_span;
if note.is_empty() {
note = format!("Included from {}", curr_span.file);
} else {
note = format!(
"{}\n{}╰-Included from {}",
note, note_pad, curr_span.file
);
note_pad += " ";
}
} else {
break;
}
}
debug_assert!(curr_span.included_from.is_none());
if !note.is_empty() {
self.builder = self.builder.with_note(note);
}
self
}
pub fn print<C>(self, source_cache: &mut C) -> std::io::Result<()>
where
C: ariadne::Cache<String>,
{
let report = self.builder.finish();
report.print(source_cache)
}
pub fn eprint<C>(self, source_cache: &mut C) -> std::io::Result<()>
where
C: ariadne::Cache<String>,
{
let report = self.builder.finish();
report.eprint(source_cache)
}
pub fn write<C, W>(
self,
source_cache: &mut C,
target: W,
) -> std::io::Result<()>
where
C: ariadne::Cache<String>,
W: std::io::Write,
{
let report = self.builder.finish();
report.write(source_cache, target)
}
}