Skip to content

Commit d31ae45

Browse files
authored
Merge pull request #42 from gt-cs2110/updates-4.3.0
LC3Tools v4.2.6
2 parents 7dc0b31 + e055e01 commit d31ae45

26 files changed

+2754
-2166
lines changed

src/backend/Cargo.lock

Lines changed: 256 additions & 22 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/backend/Cargo.toml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,17 @@
22
name = "lc3-backend"
33
version = "0.1.0"
44
license = "MIT"
5-
edition = "2021"
5+
edition = "2024"
66
exclude = ["index.node"]
7-
rust-version = "1.80"
7+
rust-version = "1.85"
88

99
[lib]
1010
crate-type = ["cdylib"]
1111

1212
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
1313

1414
[dependencies]
15-
ariadne = "0.5.0"
16-
lc3-ensemble = "0.9.2"
15+
lc3-ensemble = "0.10.0"
16+
miette = { version = "7.6.0", features = ["fancy"] }
1717
neon = "1"
18+
owo-colors = "4.2.0"

src/backend/src/err.rs

Lines changed: 160 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -1,134 +1,188 @@
11
use std::borrow::Cow;
22
use std::convert::Infallible;
33
use std::path::Path;
4+
use std::sync::LazyLock;
45

5-
use ariadne::{ColorGenerator, Label, Report, ReportKind, Source};
66
use lc3_ensemble::err::ErrSpan;
7+
use miette::highlighters::{Highlighter, HighlighterState};
8+
use miette::{Diagnostic, GraphicalReportHandler, GraphicalTheme, NamedSource, Severity, ThemeCharacters, ThemeStyles};
79
use neon::context::Context;
810
use neon::result::Throw;
11+
use owo_colors::style;
912

10-
pub(crate) fn simple_reporter<E: std::fmt::Display + ?Sized>(err: &E) -> Reporter<'_, E> {
11-
Reporter {
12-
err,
13-
filename: None,
14-
source: None,
15-
span: None,
16-
help: None,
17-
msg_includes_fname: true
13+
struct FlatHighlighter(owo_colors::Style);
14+
impl Highlighter for FlatHighlighter {
15+
fn start_highlighter_state<'h>(
16+
&'h self,
17+
_: &dyn miette::SpanContents<'_>,
18+
) -> Box<dyn HighlighterState + 'h> {
19+
struct Highlighted(owo_colors::Style);
20+
impl HighlighterState for Highlighted {
21+
fn highlight_line<'s>(&mut self, line: &'s str) -> Vec<owo_colors::Styled<&'s str>> {
22+
vec![self.0.style(line)]
23+
}
24+
}
25+
26+
Box::new(Highlighted(self.0))
1827
}
1928
}
29+
static REPORT_HANDLER: LazyLock<GraphicalReportHandler> = LazyLock::new(|| {
30+
let style_dimmed = style().fg_rgb::<0xA0, 0xA0, 0xA0>();
31+
32+
GraphicalReportHandler::new_themed(GraphicalTheme {
33+
characters: ThemeCharacters {
34+
error: String::from("Error:"),
35+
warning: String::from("Warning:"),
36+
advice: String::from("Info:"),
37+
..ThemeCharacters::unicode()
38+
},
39+
styles: ThemeStyles {
40+
link: style(),
41+
linum: style_dimmed,
42+
highlights: vec![
43+
style().magenta(),
44+
style().yellow(),
45+
style().green(),
46+
],
47+
..ThemeStyles::ansi()
48+
}
49+
})
50+
.with_context_lines(1)
51+
.with_syntax_highlighting(FlatHighlighter(style_dimmed)) // Make code gray
52+
});
2053

21-
pub(crate) fn io_reporter<'a, E: std::fmt::Display + ?Sized>(err: &'a E, fp: &'a Path) -> Reporter<'a, E> {
22-
Reporter {
23-
err,
24-
filename: fp.file_name().and_then(|s| s.to_str()),
25-
source: None,
26-
span: None,
27-
help: None,
28-
msg_includes_fname: true
54+
#[derive(Debug)]
55+
enum ReporterSource<'s> {
56+
Unlabeled(&'s str),
57+
Labeled(NamedSource<String>)
58+
}
59+
impl<'s> ReporterSource<'s> {
60+
fn new<'a>(filename: Option<&'a str>, source: &'s str) -> Self {
61+
match filename {
62+
Some(name) => ReporterSource::Labeled(NamedSource::new(name, source.to_string())),
63+
None => ReporterSource::Unlabeled(source),
64+
}
2965
}
3066
}
31-
pub(crate) fn error_reporter<'a, E: lc3_ensemble::err::Error + ?Sized>(err: &'a E, fp: &'a Path, src: &'a str) -> Reporter<'a, E> {
32-
let span = err.span();
33-
let help = err.help();
34-
35-
Reporter {
36-
err,
37-
filename: fp.file_name().and_then(|s| s.to_str()),
38-
source: Some(Source::from(src.to_string())),
39-
span,
40-
help,
41-
msg_includes_fname: false,
67+
impl miette::SourceCode for ReporterSource<'_> {
68+
fn read_span<'a>(
69+
&'a self,
70+
span: &miette::SourceSpan,
71+
context_lines_before: usize,
72+
context_lines_after: usize,
73+
) -> Result<Box<dyn miette::SpanContents<'a> + 'a>, miette::MietteError> {
74+
match self {
75+
ReporterSource::Unlabeled(s) => s.read_span(span, context_lines_before, context_lines_after),
76+
ReporterSource::Labeled(s) => s.read_span(span, context_lines_before, context_lines_after),
77+
}
4278
}
4379
}
4480

45-
pub(crate) struct Reporter<'c, E: ?Sized> {
46-
err: &'c E,
47-
filename: Option<&'c str>,
48-
source: Option<Source<String>>,
81+
pub(crate) struct Reporter<'r, E: std::fmt::Display + ?Sized> {
82+
/// Error (and message to print)
83+
err: &'r E,
84+
/// The filename of file where this error occurred (if there is a file)
85+
filename: Option<&'r str>,
86+
/// Source code
87+
source: Option<ReporterSource<'r>>,
88+
/// Span where error occurred
4989
span: Option<ErrSpan>,
50-
help: Option<Cow<'c, str>>,
51-
msg_includes_fname: bool
90+
/// Relevant help messages
91+
help: Option<Cow<'r, str>>,
92+
/// Whether to include the filename in the error message
93+
/// (can be false if it'd already appear elsewhere)
94+
include_name_in_msg: bool
5295
}
96+
impl<E: std::fmt::Display + ?Sized> std::fmt::Debug for Reporter<'_, E> {
97+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
98+
f.debug_struct("Reporter")
99+
.field("err", &self.err.to_string())
100+
.field("filename", &self.filename)
101+
.field("source", &self.source)
102+
.field("span", &self.span)
103+
.field("help", &self.help)
104+
.field("include_name_in_msg", &self.include_name_in_msg)
105+
.finish()
106+
}
107+
}
108+
impl<E: std::fmt::Display + ?Sized> std::fmt::Display for Reporter<'_, E> {
109+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
110+
match self.filename {
111+
Some(filename) if self.include_name_in_msg => write!(f, "{filename}: {}", self.err),
112+
_ => write!(f, "{}", self.err),
113+
}
114+
}
115+
}
116+
impl<E: std::fmt::Display + ?Sized> std::error::Error for Reporter<'_, E> {}
117+
impl<E: std::fmt::Display + ?Sized> Diagnostic for Reporter<'_, E> {
118+
fn severity(&self) -> Option<Severity> {
119+
Some(Severity::Error)
120+
}
53121

54-
impl<E: std::fmt::Display + ?Sized> Reporter<'_, E> {
55-
pub(crate) fn report(&mut self, writer: &mut impl std::io::Write) {
56-
let mut colors = ColorGenerator::new();
57-
58-
let msg = if self.msg_includes_fname {
59-
if let Some(fname) = self.filename {
60-
format!("{}: {}", fname, self.err)
61-
} else {
62-
self.err.to_string()
63-
}
64-
} else {
65-
self.err.to_string()
66-
};
67-
let fname = self.filename.unwrap_or("source");
68-
let span = self.span.as_ref().map_or(0..0, |s| s.first());
69-
70-
let mut report = Report::build(ReportKind::Error, (fname, span)).with_message(msg);
71-
match self.span.clone() {
72-
Some(ErrSpan::One(r)) => {
73-
report.add_label({
74-
let mut label = Label::new((fname, r))
75-
.with_color(colors.next());
76-
77-
if let Some(help) = self.help.as_deref() {
78-
label = label.with_message(help);
79-
}
122+
fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
123+
self.help.as_ref().filter(|s| !s.is_empty()).map(|s| Box::new(s) as _)
124+
}
80125

81-
label
82-
})
83-
},
84-
Some(ErrSpan::Two([r0, r1])) => {
85-
report.add_label({
86-
Label::new((fname, r0))
87-
.with_color(colors.next())
88-
.with_message("")
89-
});
90-
report.add_label({
91-
Label::new((fname, r1))
92-
.with_color(colors.next())
93-
.with_message("")
94-
});
126+
fn source_code(&self) -> Option<&dyn miette::SourceCode> {
127+
self.source.as_ref().map(|s| s as &dyn miette::SourceCode)
128+
}
95129

96-
if let Some(help) = self.help.as_deref() {
97-
report.set_help(help);
98-
}
99-
},
100-
Some(ErrSpan::Many(mr)) => {
101-
report.add_labels({
102-
mr.into_iter()
103-
.map(|s| {
104-
Label::new((fname, s.clone()))
105-
.with_color(colors.next())
106-
.with_message("")
107-
})
108-
});
130+
fn labels(&self) -> Option<Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {
131+
self.span.as_ref().map(|s| Box::new(
132+
s.iter().map(|s| miette::LabeledSpan::new_with_span(None, s.clone()))
133+
) as _)
134+
}
135+
}
109136

110-
if let Some(help) = self.help.as_deref() {
111-
report.set_help(help);
112-
}
113-
},
114-
None => {
115-
if let Some(help) = self.help.as_deref() {
116-
report.add_label(Label::new((fname, 0..0)));
117-
report.set_help(help);
118-
};
119-
}
137+
impl<'r, E: std::fmt::Display + ?Sized> Reporter<'r, E> {
138+
pub(crate) fn simple(err: &'r E) -> Self {
139+
Reporter {
140+
err,
141+
filename: None,
142+
source: None,
143+
span: None,
144+
help: None,
145+
include_name_in_msg: true
146+
}
147+
}
148+
149+
pub(crate) fn io(err: &'r E, fp: &'r Path) -> Self {
150+
Reporter {
151+
err,
152+
filename: fp.file_name().and_then(|s| s.to_str()),
153+
source: None,
154+
span: None,
155+
help: None,
156+
include_name_in_msg: true
157+
}
158+
}
159+
pub(crate) fn ensemble(err: &'r E, fp: &'r Path, src: &'r str) -> Self
160+
where E: lc3_ensemble::err::Error
161+
{
162+
let span = err.span();
163+
let help = err.help();
164+
let filename = fp.file_name().and_then(|s| s.to_str());
165+
Reporter {
166+
err,
167+
filename,
168+
source: Some(ReporterSource::new(filename, src)),
169+
span,
170+
help,
171+
include_name_in_msg: false,
120172
}
121-
122-
let source = match &self.source {
123-
Some(s) => s.clone(),
124-
None => Source::from(String::new()),
125-
};
126-
report.finish()
127-
.write((fname, source), writer)
128-
.unwrap();
173+
}
174+
}
175+
176+
impl<E: std::fmt::Display + ?Sized> Reporter<'_, E> {
177+
pub(crate) fn report(&mut self, writer: &mut Vec<u8>) {
178+
let mut report = String::new();
179+
REPORT_HANDLER.render_report(&mut report, self).unwrap();
180+
181+
// Remove whitespace that miette adds at the beginning of reports
182+
writer.extend(report.trim_start().as_bytes());
129183
}
130184

131-
pub(crate) fn report_and_throw<'a>(mut self, writer: &mut impl std::io::Write, cx: &mut impl Context<'a>) -> Throw {
185+
pub(crate) fn report_and_throw<'a>(mut self, writer: &mut Vec<u8>, cx: &mut impl Context<'a>) -> Throw {
132186
self.report(writer);
133187

134188
cx.throw_error::<_, Infallible>(self.err.to_string())

0 commit comments

Comments
 (0)