diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/examples/multifile.rs b/examples/multifile.rs index 201160b..feebe9d 100644 --- a/examples/multifile.rs +++ b/examples/multifile.rs @@ -25,10 +25,10 @@ fn main() { .with_message(format!("Original definition of {} is here", "five".fg(a))) .with_color(a)) .with_note(format!("{} is a number and can only be added to other numbers", "Nat".fg(a))) - .finish() - .print(sources(vec![ + .finish(sources(vec![ ("a.tao", include_str!("a.tao")), ("b.tao", include_str!("b.tao")), ])) + .print() .unwrap(); } diff --git a/examples/multiline.rs b/examples/multiline.rs index ad2a77a..acf1f60 100644 --- a/examples/multiline.rs +++ b/examples/multiline.rs @@ -37,7 +37,7 @@ fn main() { )) .with_color(out2)) .with_note(format!("Outputs of {} expressions must coerce to the same type", "match".fg(out))) - .finish() - .print(("sample.tao", Source::from(include_str!("sample.tao")))) + .finish(("sample.tao", Source::from(include_str!("sample.tao")))) + .print() .unwrap(); } diff --git a/examples/simple.rs b/examples/simple.rs index 5f2002c..5b14099 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -6,8 +6,8 @@ fn main() { .with_message("Incompatible types") .with_label(Label::new(32..33).with_message("This is of type Nat")) .with_label(Label::new(42..45).with_message("This is of type Str")) - .finish() - .print(Source::from(include_str!("sample.tao"))) + .finish(Source::from(include_str!("sample.tao"))) + .print() .unwrap(); const SOURCE: &str = "a b c d e f"; @@ -19,7 +19,7 @@ fn main() { .with_label(Label::new(2..3).with_color(Color::Blue).with_message("`b` for banana").with_order(1)) .with_label(Label::new(4..5).with_color(Color::Green)) .with_label(Label::new(7..9).with_color(Color::Cyan).with_message("`e` for emerald")) - .finish() - .print(Source::from(SOURCE)) + .finish(Source::from(SOURCE)) + .print() .unwrap(); } diff --git a/examples/stresstest.rs b/examples/stresstest.rs index 0fc9d5f..b29a887 100644 --- a/examples/stresstest.rs +++ b/examples/stresstest.rs @@ -53,7 +53,7 @@ fn main() { .with_compact(true) .with_underlines(true) .with_tab_width(4)) - .finish() - .print(("stresstest.tao", Source::from(include_str!("stresstest.tao")))) + .finish(("stresstest.tao", Source::from(include_str!("stresstest.tao")))) + .print() .unwrap(); } diff --git a/src/lib.rs b/src/lib.rs index 652b6a7..3d7c3a4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,6 +23,7 @@ use std::{ cmp::{PartialEq, Eq}, fmt, }; +use std::fmt::Formatter; use unicode_width::UnicodeWidthChar; /// A trait implemented by spans within a character-based source. @@ -152,6 +153,7 @@ pub struct Report<'a, S: Span = Range> { location: (::Owned, usize), labels: Vec>, config: Config, + rendered: Option } impl Report<'_, S> { @@ -170,16 +172,23 @@ impl Report<'_, S> { } /// Write this diagnostic out to `stderr`. - pub fn eprint>(&self, cache: C) -> io::Result<()> { - self.write(cache, io::stderr()) + pub fn eprint(&self) -> io::Result<()> { + write!(io::stderr(), "{}", self.rendered.as_ref().unwrap()) } /// Write this diagnostic out to `stdout`. /// /// In most cases, [`Report::eprint`] is the /// ['more correct'](https://en.wikipedia.org/wiki/Standard_streams#Standard_error_(stderr)) function to use. - pub fn print>(&self, cache: C) -> io::Result<()> { - self.write_for_stdout(cache, io::stdout()) + pub fn print(&self) -> io::Result<()> { + write!(io::stdout(), "{}", self.rendered.as_ref().unwrap()) + } + + /// TODO: Move the description from the fork + fn write_to_string>(&self, cache: C) -> String { + let mut vec = Vec::new(); + self.write(cache, &mut vec).unwrap(); + String::from_utf8(vec).unwrap() } } @@ -195,6 +204,21 @@ impl<'a, S: Span> fmt::Debug for Report<'a, S> { .finish() } } + +impl<'a, S: Span> fmt::Display for Report<'a, S> { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + assert!( + self.rendered.is_some(), + "A report render is necessary to do display" + ); + + write!(f, "{}", self.rendered.as_ref().unwrap()) + } +} + +impl<'a, S: Span> std::error::Error for Report<'a, S> {} + + /// A type that defines the kind of report being produced. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum ReportKind<'a> { @@ -303,8 +327,8 @@ impl<'a, S: Span> ReportBuilder<'a, S> { } /// Finish building the [`Report`]. - pub fn finish(self) -> Report<'a, S> { - Report { + pub fn finish>(self, cache: C) -> Report<'a, S> { + let mut report = Report { kind: self.kind, code: self.code, msg: self.msg, @@ -313,7 +337,11 @@ impl<'a, S: Span> ReportBuilder<'a, S> { location: self.location, labels: self.labels, config: self.config, - } + rendered: None + }; + report.rendered = Some(report.write_to_string(cache)); + + report } } diff --git a/src/write.rs b/src/write.rs index 542ec60..9d2cf2e 100644 --- a/src/write.rs +++ b/src/write.rs @@ -805,15 +805,7 @@ mod tests { use insta::assert_snapshot; - use crate::{Cache, CharSet, Config, Label, Report, ReportKind, Source, Span}; - - impl Report<'_, S> { - fn write_to_string>(&self, cache: C) -> String { - let mut vec = Vec::new(); - self.write(cache, &mut vec).unwrap(); - String::from_utf8(vec).unwrap() - } - } + use crate::{CharSet, Config, Label, Report, ReportKind, Source}; fn no_color_and_ascii() -> Config { Config::default() @@ -828,8 +820,8 @@ mod tests { let msg = Report::>::build(ReportKind::Error, (), 0) .with_config(no_color_and_ascii()) .with_message("can't compare apples with oranges") - .finish() - .write_to_string(Source::from("")); + .finish(Source::from("")) + .to_string(); assert_snapshot!(msg, @r###" Error: can't compare apples with oranges "###) @@ -843,8 +835,8 @@ mod tests { .with_message("can't compare apples with oranges") .with_label(Label::new(0..5)) .with_label(Label::new(9..15)) - .finish() - .write_to_string(Source::from(source)); + .finish(Source::from(source)) + .to_string(); // TODO: it would be nice if these spans still showed up (like codespan-reporting does) assert_snapshot!(msg, @r###" Error: can't compare apples with oranges @@ -863,8 +855,8 @@ mod tests { .with_message("can't compare apples with oranges") .with_label(Label::new(0..5).with_message("This is an apple")) .with_label(Label::new(9..15).with_message("This is an orange")) - .finish() - .write_to_string(Source::from(source)); + .finish(Source::from(source)) + .to_string(); // TODO: it would be nice if these lines didn't cross assert_snapshot!(msg, @r###" Error: can't compare apples with oranges @@ -888,8 +880,8 @@ mod tests { .with_label( Label::new(source.len() - 5..source.len()).with_message("This is an orange"), ) - .finish() - .write_to_string(Source::from(source)); + .finish(Source::from(source)) + .to_string(); // TODO: it would be nice if the start of long lines would be omitted (like rustc does) assert_snapshot!(msg, @r###" Error: can't compare apples with oranges @@ -908,8 +900,8 @@ mod tests { let msg = Report::>::build(ReportKind::Error, (), 0) .with_config(no_color_and_ascii()) .with_label(Label::new(0..source.len()).with_message("illegal comparison")) - .finish() - .write_to_string(Source::from(source)); + .finish(Source::from(source)) + .to_string(); // TODO: it would be nice if the 2nd line wasn't omitted assert_snapshot!(msg, @r###" Error: @@ -931,8 +923,8 @@ mod tests { .with_config(no_color_and_ascii()) .with_label(Label::new(0..source.len()).with_message("URL")) .with_label(Label::new(0..source.find(':').unwrap()).with_message("scheme")) - .finish() - .write_to_string(Source::from(source)); + .finish(Source::from(source)) + .to_string(); // TODO: it would be nice if you could tell where the spans start and end. assert_snapshot!(msg, @r###" Error: @@ -959,8 +951,8 @@ mod tests { .with_label(Label::new(9..15).with_message("This is an orange")) .with_label(Label::new(9..15).with_message("Have I mentioned that this is an orange?")) .with_label(Label::new(9..15).with_message("No really, have I mentioned that?")) - .finish() - .write_to_string(Source::from(source)); + .finish(Source::from(source)) + .to_string(); assert_snapshot!(msg, @r###" Error: can't compare apples with oranges ,-[:1:1] @@ -991,8 +983,8 @@ mod tests { .with_label(Label::new(0..5).with_message("This is an apple")) .with_label(Label::new(9..15).with_message("This is an orange")) .with_note("stop trying ... this is a fruitless endeavor") - .finish() - .write_to_string(Source::from(source)); + .finish(Source::from(source)) + .to_string(); assert_snapshot!(msg, @r###" Error: can't compare apples with oranges ,-[:1:1] @@ -1017,8 +1009,8 @@ mod tests { .with_label(Label::new(0..5).with_message("This is an apple")) .with_label(Label::new(9..15).with_message("This is an orange")) .with_help("have you tried peeling the orange?") - .finish() - .write_to_string(Source::from(source)); + .finish(Source::from(source)) + .to_string(); assert_snapshot!(msg, @r###" Error: can't compare apples with oranges ,-[:1:1] @@ -1044,8 +1036,8 @@ mod tests { .with_label(Label::new(9..15).with_message("This is an orange")) .with_help("have you tried peeling the orange?") .with_note("stop trying ... this is a fruitless endeavor") - .finish() - .write_to_string(Source::from(source)); + .finish(Source::from(source)) + .to_string(); assert_snapshot!(msg, @r###" Error: can't compare apples with oranges ,-[:1:1] @@ -1062,4 +1054,22 @@ mod tests { ---' "###) } + + #[test] + fn report_to_error() { + let source = "apple == orange;"; + let msg = Report::>::build(ReportKind::Error, (), 0) + .with_config(no_color_and_ascii()) + .with_message("can't compare apples with oranges") + .with_label(Label::new(0..5).with_message("This is an apple")) + .with_label(Label::new(9..15).with_message("This is an orange")) + .with_help("have you tried peeling the orange?") + .with_note("stop trying ... this is a fruitless endeavor") + .finish(Source::from(source)); + + let err: &dyn std::error::Error = &msg; // This bit compiling proves it is Error + + println!("{err}"); + println!("{err:?}"); + } }