1use crate::*;
7pub use ariadne::{Cache, ReportKind};
8
9pub trait Sources {
15 fn sources(self) -> impl ariadne::Cache<String>;
16}
17
18impl<I, S> Sources for I
19where
20 I: IntoIterator<Item = (String, S)>,
21 S: AsRef<str>,
22{
23 fn sources(self) -> impl ariadne::Cache<String> {
24 ariadne::sources(self)
25 }
26}
27
28pub struct Report {
33 builder: ariadne::ReportBuilder<'static, (String, std::ops::Range<usize>)>,
34}
35
36fn get_expansion_string(expansion_depth: usize, is_first: bool) -> String {
37 if expansion_depth == 0 {
38 "Original token".to_string()
39 } else if (expansion_depth == 1) && is_first {
40 "Expanded here".to_string()
41 } else {
42 let suffix = match expansion_depth % 10 {
43 1 => "st",
44 2 => "nd",
45 3 => "rd",
46 _ => "th",
47 };
48 format!("Expanded here {}{}", expansion_depth, suffix)
49 }
50}
51
52impl<'a> Report {
53 pub fn new<C, M>(
57 kind: ariadne::ReportKind<'static>,
58 span: &Span<'a>,
59 code: C,
60 msg: M,
61 ) -> Self
62 where
63 C: std::fmt::Display,
64 M: ToString,
65 {
66 Self {
67 builder: ariadne::Report::build(
68 kind,
69 (span.file.to_string(), span.bytes.clone()),
70 )
71 .with_config(
72 ariadne::Config::new()
73 .with_index_type(ariadne::IndexType::Byte),
74 )
75 .with_code(code)
76 .with_message(msg),
77 }
78 }
79
80 pub fn with_label<M>(
85 mut self,
86 span: &Span<'a>,
87 kind: ariadne::ReportKind<'static>,
88 msg: M,
89 ) -> Self
90 where
91 M: ToString,
92 {
93 let mut curr_span: &Span<'a> = span;
94 let mut expansion_depth: usize = curr_span.expansion_depth();
95 let mut expanded = false;
96 let color = match kind {
97 ReportKind::Error => Color::Red,
98 ReportKind::Warning => Color::Yellow,
99 ReportKind::Advice => Color::Fixed(147),
100 ReportKind::Custom(_, color) => color.clone(),
101 };
102 loop {
103 if let Some(expanded_span) = curr_span.expanded_from {
104 self.builder = self.builder.with_label(
105 Label::new((
106 curr_span.file.to_string(),
107 curr_span.bytes.clone(),
108 ))
109 .with_message(get_expansion_string(
110 expansion_depth,
111 expanded == false,
112 ))
113 .with_color(Color::BrightGreen),
114 );
115 curr_span = expanded_span;
116 expansion_depth -= 1;
117 expanded = true;
118 } else {
119 break;
120 }
121 }
122 if expanded {
123 self.builder = self.builder.with_label(
125 Label::new((
126 curr_span.file.to_string(),
127 curr_span.bytes.clone(),
128 ))
129 .with_message(get_expansion_string(expansion_depth, false))
130 .with_color(Color::BrightGreen),
131 );
132 }
133 curr_span = &span;
134 self.builder = self.builder.with_label(
135 Label::new((curr_span.file.to_string(), curr_span.bytes.clone()))
136 .with_message(msg)
137 .with_color(color)
138 .with_priority(1),
139 );
140 let mut note = "".to_string();
141 let mut note_pad = "".to_string();
142 let total_inclusion_depth = curr_span.inclusion_depth();
143 let mut curr_inclusion_depth = 0;
144 loop {
146 if (curr_inclusion_depth == 3) & (total_inclusion_depth > 7) {
147 for _ in 7..=total_inclusion_depth {
148 curr_span = curr_span.included_from.unwrap();
149 }
150 note = format!("{}\n{}╰- ...", note, note_pad);
151 note_pad += " ";
152 }
153 if let Some(included_span) = curr_span.included_from {
154 curr_inclusion_depth += 1;
155 curr_span = included_span;
156 if note.is_empty() {
157 note = format!("Included from {}", curr_span.file);
158 } else {
159 note = format!(
160 "{}\n{}╰-Included from {}",
161 note, note_pad, curr_span.file
162 );
163 note_pad += " ";
164 }
165 } else {
166 break;
167 }
168 }
169 debug_assert!(curr_span.included_from.is_none());
170 if !note.is_empty() {
171 self.builder = self.builder.with_note(note);
172 }
173 self
174 }
175
176 pub fn print<C>(self, source_cache: &mut C) -> std::io::Result<()>
178 where
179 C: ariadne::Cache<String>,
180 {
181 let report = self.builder.finish();
182 report.print(source_cache)
183 }
184
185 pub fn eprint<C>(self, source_cache: &mut C) -> std::io::Result<()>
187 where
188 C: ariadne::Cache<String>,
189 {
190 let report = self.builder.finish();
191 report.eprint(source_cache)
192 }
193
194 pub fn write<C, W>(
196 self,
197 source_cache: &mut C,
198 target: W,
199 ) -> std::io::Result<()>
200 where
201 C: ariadne::Cache<String>,
202 W: std::io::Write,
203 {
204 let report = self.builder.finish();
205 report.write(source_cache, target)
206 }
207}