diff --git a/benches/simple.rs b/benches/simple.rs index f6abcee..4eacfda 100644 --- a/benches/simple.rs +++ b/benches/simple.rs @@ -4,12 +4,10 @@ extern crate criterion; use criterion::{black_box, Criterion}; -use annotate_snippets::{Annotation, AnnotationType, Renderer, Slice, Snippet, SourceAnnotation}; +use annotate_snippets::{Label, Renderer, Slice, Snippet}; fn create_snippet(renderer: Renderer) { - let snippet = Snippet { - slices: vec![Slice { - source: r#") -> Option { + let source = r#") -> Option { for ann in annotations { match (ann.range.0, ann.range.1) { (None, None) => continue, @@ -30,30 +28,15 @@ fn create_snippet(renderer: Renderer) { } _ => continue, } - }"#, - line_start: 51, - origin: Some("src/format.rs"), - fold: false, - annotations: vec![ - SourceAnnotation { - label: "expected `Option` because of return type", - annotation_type: AnnotationType::Warning, - range: 5..19, - }, - SourceAnnotation { - label: "expected enum `std::option::Option`", - annotation_type: AnnotationType::Error, - range: 26..724, - }, - ], - }], - title: Some(Annotation { - label: Some("mismatched types"), - id: Some("E0308"), - annotation_type: AnnotationType::Error, - }), - footer: vec![], - }; + }"#; + let snippet = Snippet::error("mismatched types").id("E0308").slice( + Slice::new(source, 51) + .origin("src/format.rs") + .annotation( + Label::warning("expected `Option` because of return type").span(5..19), + ) + .annotation(Label::error("expected enum `std::option::Option`").span(26..724)), + ); let _result = renderer.render(snippet).to_string(); } diff --git a/examples/expected_type.rs b/examples/expected_type.rs index 613cf60..99fb1bf 100644 --- a/examples/expected_type.rs +++ b/examples/expected_type.rs @@ -1,35 +1,22 @@ -use annotate_snippets::{Annotation, AnnotationType, Renderer, Slice, Snippet, SourceAnnotation}; +use annotate_snippets::{Label, Renderer, Slice, Snippet}; fn main() { - let snippet = Snippet { - title: Some(Annotation { - label: Some("expected type, found `22`"), - id: None, - annotation_type: AnnotationType::Error, - }), - footer: vec![], - slices: vec![Slice { - source: r#" annotations: vec![SourceAnnotation { + let source = r#" annotations: vec![SourceAnnotation { label: "expected struct `annotate_snippets::snippet::Slice`, found reference" , - range: <22, 25>,"#, - line_start: 26, - origin: Some("examples/footer.rs"), - fold: true, - annotations: vec![ - SourceAnnotation { - label: "", - annotation_type: AnnotationType::Error, - range: 193..195, - }, - SourceAnnotation { - label: "while parsing this struct", - annotation_type: AnnotationType::Info, - range: 34..50, - }, - ], - }], - }; + range: <22, 25>,"#; + let snippet = Snippet::error("expected type, found `22`").slice( + Slice::new(source, 26) + .origin("examples/footer.rs") + .fold(true) + .annotation( + Label::error( + "expected struct `annotate_snippets::snippet::Slice`, found reference", + ) + .span(193..195), + ) + .annotation(Label::info("while parsing this struct").span(34..50)), + ); let renderer = Renderer::plain(); println!("{}", renderer.render(snippet)); diff --git a/examples/footer.rs b/examples/footer.rs index 433aa83..d24b497 100644 --- a/examples/footer.rs +++ b/examples/footer.rs @@ -1,31 +1,21 @@ -use annotate_snippets::{Annotation, AnnotationType, Renderer, Slice, Snippet, SourceAnnotation}; +use annotate_snippets::{Label, Renderer, Slice, Snippet}; fn main() { - let snippet = Snippet { - title: Some(Annotation { - label: Some("mismatched types"), - id: Some("E0308"), - annotation_type: AnnotationType::Error, - }), - footer: vec![Annotation { - label: Some( - "expected type: `snippet::Annotation`\n found type: `__&__snippet::Annotation`", - ), - id: None, - annotation_type: AnnotationType::Note, - }], - slices: vec![Slice { - source: " slices: vec![\"A\",", - line_start: 13, - origin: Some("src/multislice.rs"), - fold: false, - annotations: vec![SourceAnnotation { - label: "expected struct `annotate_snippets::snippet::Slice`, found reference", - range: 21..24, - annotation_type: AnnotationType::Error, - }], - }], - }; + let snippet = Snippet::error("mismatched types") + .id("E0308") + .slice( + Slice::new(" slices: vec![\"A\",", 13) + .origin("src/multislice.rs") + .annotation( + Label::error( + "expected struct `annotate_snippets::snippet::Slice`, found reference", + ) + .span(21..24), + ), + ) + .footer(Label::note( + "expected type: `snippet::Annotation`\n found type: `__&__snippet::Annotation`", + )); let renderer = Renderer::plain(); println!("{}", renderer.render(snippet)); diff --git a/examples/format.rs b/examples/format.rs index a699f0a..5eb5a28 100644 --- a/examples/format.rs +++ b/examples/format.rs @@ -1,9 +1,7 @@ -use annotate_snippets::{Annotation, AnnotationType, Renderer, Slice, Snippet, SourceAnnotation}; +use annotate_snippets::{Label, Renderer, Slice, Snippet}; fn main() { - let snippet = Snippet { - slices: vec![Slice { - source: r#") -> Option { + let source = r#") -> Option { for ann in annotations { match (ann.range.0, ann.range.1) { (None, None) => continue, @@ -24,30 +22,15 @@ fn main() { } _ => continue, } - }"#, - line_start: 51, - origin: Some("src/format.rs"), - fold: false, - annotations: vec![ - SourceAnnotation { - label: "expected `Option` because of return type", - annotation_type: AnnotationType::Warning, - range: 5..19, - }, - SourceAnnotation { - label: "expected enum `std::option::Option`", - annotation_type: AnnotationType::Error, - range: 26..724, - }, - ], - }], - title: Some(Annotation { - label: Some("mismatched types"), - id: Some("E0308"), - annotation_type: AnnotationType::Error, - }), - footer: vec![], - }; + }"#; + let snippet = Snippet::error("mismatched types").id("E0308").slice( + Slice::new(source, 51) + .origin("src/format.rs") + .annotation( + Label::warning("expected `Option` because of return type").span(5..19), + ) + .annotation(Label::error("expected enum `std::option::Option`").span(26..724)), + ); let renderer = Renderer::plain(); println!("{}", renderer.render(snippet)); diff --git a/examples/multislice.rs b/examples/multislice.rs index 63ebb65..f0de557 100644 --- a/examples/multislice.rs +++ b/examples/multislice.rs @@ -1,30 +1,9 @@ -use annotate_snippets::{Annotation, AnnotationType, Renderer, Slice, Snippet}; +use annotate_snippets::{Renderer, Slice, Snippet}; fn main() { - let snippet = Snippet { - title: Some(Annotation { - label: Some("mismatched types"), - id: None, - annotation_type: AnnotationType::Error, - }), - footer: vec![], - slices: vec![ - Slice { - source: "Foo", - line_start: 51, - origin: Some("src/format.rs"), - fold: false, - annotations: vec![], - }, - Slice { - source: "Faa", - line_start: 129, - origin: Some("src/display.rs"), - fold: false, - annotations: vec![], - }, - ], - }; + let snippet = Snippet::error("mismatched types") + .slice(Slice::new("Foo", 51).origin("src/format.rs")) + .slice(Slice::new("Faa", 129).origin("src/display.rs")); let renderer = Renderer::plain(); println!("{}", renderer.render(snippet)); diff --git a/src/lib.rs b/src/lib.rs index 03ffbb6..2066e67 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,7 +48,6 @@ //! ```text //! cargo add annotate-snippets --dev --feature testing-colors //! ``` -//! pub mod renderer; mod snippet; diff --git a/src/renderer/display_list.rs b/src/renderer/display_list.rs index f8241db..01484d4 100644 --- a/src/renderer/display_list.rs +++ b/src/renderer/display_list.rs @@ -108,6 +108,7 @@ impl<'a> DisplayList<'a> { pub(crate) fn new( snippet::Snippet { title, + id, footer, slices, }: snippet::Snippet<'a>, @@ -116,9 +117,8 @@ impl<'a> DisplayList<'a> { margin: Option, ) -> DisplayList<'a> { let mut body = vec![]; - if let Some(annotation) = title { - body.push(format_title(annotation)); - } + + body.push(format_title(title, id)); for (idx, slice) in slices.into_iter().enumerate() { body.append(&mut format_slice( @@ -130,7 +130,7 @@ impl<'a> DisplayList<'a> { } for annotation in footer { - body.append(&mut format_annotation(annotation)); + body.append(&mut format_footer(annotation)); } Self { @@ -733,26 +733,24 @@ fn format_label( result } -fn format_title(annotation: snippet::Annotation<'_>) -> DisplayLine<'_> { - let label = annotation.label.unwrap_or_default(); +fn format_title<'a>(title: snippet::Label<'a>, id: Option<&'a str>) -> DisplayLine<'a> { DisplayLine::Raw(DisplayRawLine::Annotation { annotation: Annotation { - annotation_type: DisplayAnnotationType::from(annotation.annotation_type), - id: annotation.id, - label: format_label(Some(label), Some(DisplayTextStyle::Emphasis)), + annotation_type: DisplayAnnotationType::from(title.annotation_type), + id, + label: format_label(Some(title.label), Some(DisplayTextStyle::Emphasis)), }, source_aligned: false, continuation: false, }) } -fn format_annotation(annotation: snippet::Annotation<'_>) -> Vec> { +fn format_footer(footer: snippet::Label<'_>) -> Vec> { let mut result = vec![]; - let label = annotation.label.unwrap_or_default(); - for (i, line) in label.lines().enumerate() { + for (i, line) in footer.label.lines().enumerate() { result.push(DisplayLine::Raw(DisplayRawLine::Annotation { annotation: Annotation { - annotation_type: DisplayAnnotationType::from(annotation.annotation_type), + annotation_type: DisplayAnnotationType::from(footer.annotation_type), id: None, label: format_label(Some(line), None), }, @@ -1222,15 +1220,7 @@ mod tests { #[test] fn test_format_title() { - let input = snippet::Snippet { - title: Some(snippet::Annotation { - id: Some("E0001"), - label: Some("This is a title"), - annotation_type: snippet::AnnotationType::Error, - }), - footer: vec![], - slices: vec![], - }; + let input = snippet::Snippet::error("This is a title").id("E0001"); let output = from_display_lines(vec![DisplayLine::Raw(DisplayRawLine::Annotation { annotation: Annotation { annotation_type: DisplayAnnotationType::Error, @@ -1251,18 +1241,20 @@ mod tests { let line_1 = "This is line 1"; let line_2 = "This is line 2"; let source = [line_1, line_2].join("\n"); - let input = snippet::Snippet { - title: None, - footer: vec![], - slices: vec![snippet::Slice { - source: &source, - line_start: 5402, - origin: None, - annotations: vec![], - fold: false, - }], - }; + let input = snippet::Snippet::error("").slice(snippet::Slice::new(&source, 5402)); let output = from_display_lines(vec![ + DisplayLine::Raw(DisplayRawLine::Annotation { + annotation: Annotation { + annotation_type: DisplayAnnotationType::Error, + id: None, + label: vec![DisplayTextFragment { + content: "", + style: DisplayTextStyle::Emphasis, + }], + }, + source_aligned: false, + continuation: false, + }), DisplayLine::Source { lineno: None, inline_marks: vec![], @@ -1299,27 +1291,22 @@ mod tests { let src_0_len = src_0.len(); let src_1 = "This is slice 2"; let src_1_len = src_1.len(); - let input = snippet::Snippet { - title: None, - footer: vec![], - slices: vec![ - snippet::Slice { - source: src_0, - line_start: 5402, - origin: Some("file1.rs"), - annotations: vec![], - fold: false, - }, - snippet::Slice { - source: src_1, - line_start: 2, - origin: Some("file2.rs"), - annotations: vec![], - fold: false, - }, - ], - }; + let input = snippet::Snippet::error("") + .slice(snippet::Slice::new(src_0, 5402).origin("file1.rs")) + .slice(snippet::Slice::new(src_1, 2).origin("file2.rs")); let output = from_display_lines(vec![ + DisplayLine::Raw(DisplayRawLine::Annotation { + annotation: Annotation { + annotation_type: DisplayAnnotationType::Error, + id: None, + label: vec![DisplayTextFragment { + content: "", + style: DisplayTextStyle::Emphasis, + }], + }, + source_aligned: false, + continuation: false, + }), DisplayLine::Raw(DisplayRawLine::Origin { path: "file1.rs", pos: None, @@ -1377,22 +1364,23 @@ mod tests { let source = [line_1, line_2].join("\n"); // In line 2 let range = 22..24; - let input = snippet::Snippet { - title: None, - footer: vec![], - slices: vec![snippet::Slice { - source: &source, - line_start: 5402, - origin: None, - annotations: vec![snippet::SourceAnnotation { - range: range.clone(), - label: "Test annotation", - annotation_type: snippet::AnnotationType::Info, - }], - fold: false, - }], - }; + let input = snippet::Snippet::error("").slice( + snippet::Slice::new(&source, 5402) + .annotation(snippet::Label::info("Test annotation").span(range.clone())), + ); let output = from_display_lines(vec![ + DisplayLine::Raw(DisplayRawLine::Annotation { + annotation: Annotation { + annotation_type: DisplayAnnotationType::Error, + id: None, + label: vec![DisplayTextFragment { + content: "", + style: DisplayTextStyle::Emphasis, + }], + }, + source_aligned: false, + continuation: false, + }), DisplayLine::Source { lineno: None, inline_marks: vec![], @@ -1445,27 +1433,34 @@ mod tests { #[test] fn test_format_label() { - let input = snippet::Snippet { - title: None, - footer: vec![snippet::Annotation { - id: None, - label: Some("This __is__ a title"), - annotation_type: snippet::AnnotationType::Error, - }], - slices: vec![], - }; - let output = from_display_lines(vec![DisplayLine::Raw(DisplayRawLine::Annotation { - annotation: Annotation { - annotation_type: DisplayAnnotationType::Error, - id: None, - label: vec![DisplayTextFragment { - content: "This __is__ a title", - style: DisplayTextStyle::Regular, - }], - }, - source_aligned: true, - continuation: false, - })]); + let input = + snippet::Snippet::error("").footer(snippet::Label::error("This __is__ a title")); + let output = from_display_lines(vec![ + DisplayLine::Raw(DisplayRawLine::Annotation { + annotation: Annotation { + annotation_type: DisplayAnnotationType::Error, + id: None, + label: vec![DisplayTextFragment { + content: "", + style: DisplayTextStyle::Emphasis, + }], + }, + source_aligned: false, + continuation: false, + }), + DisplayLine::Raw(DisplayRawLine::Annotation { + annotation: Annotation { + annotation_type: DisplayAnnotationType::Error, + id: None, + label: vec![DisplayTextFragment { + content: "This __is__ a title", + style: DisplayTextStyle::Regular, + }], + }, + source_aligned: true, + continuation: false, + }), + ]); assert_eq!(DisplayList::new(input, &STYLESHEET, false, None), output); } @@ -1474,45 +1469,21 @@ mod tests { fn test_i26() { let source = "short"; let label = "label"; - let input = snippet::Snippet { - title: None, - footer: vec![], - slices: vec![snippet::Slice { - annotations: vec![snippet::SourceAnnotation { - range: 0..source.len() + 2, - label, - annotation_type: snippet::AnnotationType::Error, - }], - source, - line_start: 0, - origin: None, - fold: false, - }], - }; + let input = snippet::Snippet::error("").slice( + snippet::Slice::new(source, 0) + .annotation(snippet::Label::error(label).span(0..source.len() + 2)), + ); let _ = DisplayList::new(input, &STYLESHEET, false, None); } #[test] fn test_i_29() { - let snippets = snippet::Snippet { - title: Some(snippet::Annotation { - id: None, - label: Some("oops"), - annotation_type: snippet::AnnotationType::Error, - }), - footer: vec![], - slices: vec![snippet::Slice { - source: "First line\r\nSecond oops line", - line_start: 1, - origin: Some(""), - annotations: vec![snippet::SourceAnnotation { - range: 19..23, - label: "oops", - annotation_type: snippet::AnnotationType::Error, - }], - fold: true, - }], - }; + let snippets = snippet::Snippet::error("oops").slice( + snippet::Slice::new("First line\r\nSecond oops line", 1) + .origin("") + .fold(true) + .annotation(snippet::Label::error("oops").span(19..23)), + ); let expected = from_display_lines(vec![ DisplayLine::Raw(DisplayRawLine::Annotation { diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index b6108ce..7046407 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -2,31 +2,10 @@ //! //! # Example //! ``` -//! use annotate_snippets::{Annotation, AnnotationType, Renderer, Slice, Snippet}; -//! let snippet = Snippet { -//! title: Some(Annotation { -//! label: Some("mismatched types"), -//! id: None, -//! annotation_type: AnnotationType::Error, -//! }), -//! footer: vec![], -//! slices: vec![ -//! Slice { -//! source: "Foo", -//! line_start: 51, -//! origin: Some("src/format.rs"), -//! fold: false, -//! annotations: vec![], -//! }, -//! Slice { -//! source: "Faa", -//! line_start: 129, -//! origin: Some("src/display.rs"), -//! fold: false, -//! annotations: vec![], -//! }, -//! ], -//! }; +//! use annotate_snippets::{Renderer, Slice, Snippet}; +//! let snippet = Snippet::error("mismatched types") +//! .slice(Slice::new("Foo", 51).origin("src/format.rs")) +//! .slice(Slice::new("Faa", 129).origin("src/display.rs")); //! //! let renderer = Renderer::styled(); //! println!("{}", renderer.render(snippet)); diff --git a/src/snippet.rs b/src/snippet.rs index 7f052f0..e3a0bc0 100644 --- a/src/snippet.rs +++ b/src/snippet.rs @@ -5,40 +5,121 @@ //! ``` //! use annotate_snippets::*; //! -//! Snippet { -//! title: Some(Annotation { -//! label: Some("mismatched types"), -//! id: None, -//! annotation_type: AnnotationType::Error, -//! }), -//! footer: vec![], -//! slices: vec![ -//! Slice { -//! source: "Foo", -//! line_start: 51, -//! origin: Some("src/format.rs"), -//! fold: false, -//! annotations: vec![], -//! }, -//! Slice { -//! source: "Faa", -//! line_start: 129, -//! origin: Some("src/display.rs"), -//! fold: false, -//! annotations: vec![], -//! }, -//! ], -//! }; +//! Snippet::error("mismatched types") +//! .slice(Slice::new("Foo", 51).origin("src/format.rs")) +//! .slice(Slice::new("Faa", 129).origin("src/display.rs")); //! ``` use std::ops::Range; /// Primary structure provided for formatting -#[derive(Debug, Default)] pub struct Snippet<'a> { - pub title: Option>, - pub footer: Vec>, - pub slices: Vec>, + pub(crate) title: Label<'a>, + pub(crate) id: Option<&'a str>, + pub(crate) slices: Vec>, + pub(crate) footer: Vec>, +} + +impl<'a> Snippet<'a> { + pub fn title(title: Label<'a>) -> Self { + Self { + title, + id: None, + slices: vec![], + footer: vec![], + } + } + + pub fn error(title: &'a str) -> Self { + Self::title(Label::error(title)) + } + + pub fn warning(title: &'a str) -> Self { + Self::title(Label::warning(title)) + } + + pub fn info(title: &'a str) -> Self { + Self::title(Label::info(title)) + } + + pub fn note(title: &'a str) -> Self { + Self::title(Label::note(title)) + } + + pub fn help(title: &'a str) -> Self { + Self::title(Label::help(title)) + } + + pub fn id(mut self, id: &'a str) -> Self { + self.id = Some(id); + self + } + + pub fn slice(mut self, slice: Slice<'a>) -> Self { + self.slices.push(slice); + self + } + + pub fn footer(mut self, footer: Label<'a>) -> Self { + self.footer.push(footer); + self + } +} + +pub struct Label<'a> { + pub(crate) annotation_type: AnnotationType, + pub(crate) label: &'a str, +} + +impl<'a> Label<'a> { + pub fn new(annotation_type: AnnotationType, label: &'a str) -> Self { + Self { + annotation_type, + label, + } + } + pub fn error(label: &'a str) -> Self { + Self::new(AnnotationType::Error, label) + } + + pub fn warning(label: &'a str) -> Self { + Self::new(AnnotationType::Warning, label) + } + + pub fn info(label: &'a str) -> Self { + Self::new(AnnotationType::Info, label) + } + + pub fn note(label: &'a str) -> Self { + Self::new(AnnotationType::Note, label) + } + + pub fn help(label: &'a str) -> Self { + Self::new(AnnotationType::Help, label) + } + + pub fn label(mut self, label: &'a str) -> Self { + self.label = label; + self + } + + /// Create a [SourceAnnotation] with the given span for a [Slice] + pub fn span(&self, span: Range) -> SourceAnnotation<'a> { + SourceAnnotation { + range: span, + label: self.label, + annotation_type: self.annotation_type, + } + } +} + +impl From for Label<'_> { + fn from(annotation_type: AnnotationType) -> Self { + Label { + annotation_type, + label: "", + } + } } /// Structure containing the slice of text to be annotated and @@ -46,15 +127,39 @@ pub struct Snippet<'a> { /// /// One `Slice` is meant to represent a single, continuous, /// slice of source code that you want to annotate. -#[derive(Debug)] pub struct Slice<'a> { - pub source: &'a str, - pub line_start: usize, - pub origin: Option<&'a str>, - pub annotations: Vec>, - /// If set explicitly to `true`, the snippet will fold - /// parts of the slice that don't contain any annotations. - pub fold: bool, + pub(crate) source: &'a str, + pub(crate) line_start: usize, + pub(crate) origin: Option<&'a str>, + pub(crate) annotations: Vec>, + pub(crate) fold: bool, +} + +impl<'a> Slice<'a> { + pub fn new(source: &'a str, line_start: usize) -> Self { + Self { + source, + line_start, + origin: None, + annotations: vec![], + fold: false, + } + } + + pub fn origin(mut self, origin: &'a str) -> Self { + self.origin = Some(origin); + self + } + + pub fn annotation(mut self, annotation: SourceAnnotation<'a>) -> Self { + self.annotations.push(annotation); + self + } + + pub fn fold(mut self, fold: bool) -> Self { + self.fold = fold; + self + } } /// Types of annotations. @@ -70,19 +175,12 @@ pub enum AnnotationType { } /// An annotation for a `Slice`. +/// +/// This gets created by [Label::span]. #[derive(Debug)] pub struct SourceAnnotation<'a> { /// The byte range of the annotation in the `source` string - pub range: Range, - pub label: &'a str, - pub annotation_type: AnnotationType, -} - -/// An annotation for a `Snippet`. -#[derive(Debug)] -pub struct Annotation<'a> { - /// Identifier of the annotation. Usually error code like "E0308". - pub id: Option<&'a str>, - pub label: Option<&'a str>, - pub annotation_type: AnnotationType, + pub(crate) range: Range, + pub(crate) label: &'a str, + pub(crate) annotation_type: AnnotationType, } diff --git a/tests/fixtures/deserialize.rs b/tests/fixtures/deserialize.rs index 70e06ac..a01c343 100644 --- a/tests/fixtures/deserialize.rs +++ b/tests/fixtures/deserialize.rs @@ -2,7 +2,7 @@ use serde::{Deserialize, Deserializer, Serialize}; use std::ops::Range; use annotate_snippets::{ - renderer::Margin, Annotation, AnnotationType, Renderer, Slice, Snippet, SourceAnnotation, + renderer::Margin, AnnotationType, Label, Renderer, Slice, Snippet, SourceAnnotation, }; #[derive(Deserialize)] @@ -15,14 +15,16 @@ pub struct Fixture<'a> { #[derive(Deserialize)] pub struct SnippetDef<'a> { - #[serde(deserialize_with = "deserialize_annotation")] + #[serde(deserialize_with = "deserialize_label")] + #[serde(borrow)] + pub title: Label<'a>, #[serde(default)] #[serde(borrow)] - pub title: Option>, - #[serde(deserialize_with = "deserialize_annotations")] + pub id: Option<&'a str>, + #[serde(deserialize_with = "deserialize_labels")] #[serde(default)] #[serde(borrow)] - pub footer: Vec>, + pub footer: Vec>, #[serde(deserialize_with = "deserialize_slices")] #[serde(borrow)] pub slices: Vec>, @@ -32,64 +34,72 @@ impl<'a> From> for Snippet<'a> { fn from(val: SnippetDef<'a>) -> Self { let SnippetDef { title, + id, footer, slices, } = val; - Snippet { - title, - footer, - slices, + let mut snippet = Snippet::title(title); + if let Some(id) = id { + snippet = snippet.id(id); } + snippet = slices + .into_iter() + .fold(snippet, |snippet, slice| snippet.slice(slice)); + snippet = footer + .into_iter() + .fold(snippet, |snippet, label| snippet.footer(label)); + snippet } } -fn deserialize_slices<'de, D>(deserializer: D) -> Result>, D::Error> +fn deserialize_label<'de, D>(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, { #[derive(Deserialize)] struct Wrapper<'a>( - #[serde(with = "SliceDef")] + #[serde(with = "LabelDef")] #[serde(borrow)] - Slice<'a>, + LabelDef<'a>, ); - let v = Vec::deserialize(deserializer)?; - Ok(v.into_iter().map(|Wrapper(a)| a).collect()) + Wrapper::deserialize(deserializer) + .map(|Wrapper(label)| Label::new(label.annotation_type, label.label)) } -fn deserialize_annotation<'de, D>(deserializer: D) -> Result>, D::Error> +fn deserialize_labels<'de, D>(deserializer: D) -> Result>, D::Error> where D: Deserializer<'de>, { #[derive(Deserialize)] struct Wrapper<'a>( - #[serde(with = "AnnotationDef")] + #[serde(with = "LabelDef")] #[serde(borrow)] - Annotation<'a>, + LabelDef<'a>, ); - Option::::deserialize(deserializer) - .map(|opt_wrapped: Option| opt_wrapped.map(|wrapped: Wrapper| wrapped.0)) + let v = Vec::deserialize(deserializer)?; + Ok(v.into_iter() + .map(|Wrapper(a)| Label::new(a.annotation_type, a.label)) + .collect()) } -fn deserialize_annotations<'de, D>(deserializer: D) -> Result>, D::Error> +fn deserialize_slices<'de, D>(deserializer: D) -> Result>, D::Error> where D: Deserializer<'de>, { #[derive(Deserialize)] struct Wrapper<'a>( - #[serde(with = "AnnotationDef")] + #[serde(with = "SliceDef")] #[serde(borrow)] - Annotation<'a>, + SliceDef<'a>, ); let v = Vec::deserialize(deserializer)?; - Ok(v.into_iter().map(|Wrapper(a)| a).collect()) + Ok(v.into_iter().map(|Wrapper(a)| a.into()).collect()) } #[derive(Deserialize)] -#[serde(remote = "Slice")] pub struct SliceDef<'a> { #[serde(borrow)] pub source: &'a str, @@ -103,6 +113,26 @@ pub struct SliceDef<'a> { pub fold: bool, } +impl<'a> From> for Slice<'a> { + fn from(val: SliceDef<'a>) -> Self { + let SliceDef { + source, + line_start, + origin, + annotations, + fold, + } = val; + let mut slice = Slice::new(source, line_start).fold(fold); + if let Some(origin) = origin { + slice = slice.origin(origin) + } + slice = annotations + .into_iter() + .fold(slice, |slice, annotation| slice.annotation(annotation)); + slice + } +} + fn deserialize_source_annotations<'de, D>( deserializer: D, ) -> Result>, D::Error> @@ -110,18 +140,13 @@ where D: Deserializer<'de>, { #[derive(Deserialize)] - struct Wrapper<'a>( - #[serde(with = "SourceAnnotationDef")] - #[serde(borrow)] - SourceAnnotation<'a>, - ); + struct Wrapper<'a>(#[serde(borrow)] SourceAnnotationDef<'a>); let v = Vec::deserialize(deserializer)?; - Ok(v.into_iter().map(|Wrapper(a)| a).collect()) + Ok(v.into_iter().map(|Wrapper(a)| a.into()).collect()) } #[derive(Serialize, Deserialize)] -#[serde(remote = "SourceAnnotation")] pub struct SourceAnnotationDef<'a> { pub range: Range, #[serde(borrow)] @@ -130,15 +155,23 @@ pub struct SourceAnnotationDef<'a> { pub annotation_type: AnnotationType, } +impl<'a> From> for SourceAnnotation<'a> { + fn from(val: SourceAnnotationDef<'a>) -> Self { + let SourceAnnotationDef { + range, + label, + annotation_type, + } = val; + Label::new(annotation_type, label).span(range) + } +} + #[derive(Serialize, Deserialize)] -#[serde(remote = "Annotation")] -pub struct AnnotationDef<'a> { - #[serde(borrow)] - pub id: Option<&'a str>, - #[serde(borrow)] - pub label: Option<&'a str>, +pub struct LabelDef<'a> { #[serde(with = "AnnotationTypeDef")] pub annotation_type: AnnotationType, + #[serde(borrow)] + pub label: &'a str, } #[allow(dead_code)] diff --git a/tests/fixtures/no-color/issue_52.toml b/tests/fixtures/no-color/issue_52.toml index d31e0cb..ea239dd 100644 --- a/tests/fixtures/no-color/issue_52.toml +++ b/tests/fixtures/no-color/issue_52.toml @@ -1,5 +1,6 @@ [snippet.title] annotation_type = "Error" +label = "" [[snippet.slices]] source = """ diff --git a/tests/fixtures/no-color/multiline_annotation.toml b/tests/fixtures/no-color/multiline_annotation.toml index 604e04b..48c0725 100644 --- a/tests/fixtures/no-color/multiline_annotation.toml +++ b/tests/fixtures/no-color/multiline_annotation.toml @@ -34,7 +34,7 @@ range = [5, 19] label = "expected enum `std::option::Option`, found ()" annotation_type = "Error" range = [22, 766] -[snippet.title] -label = "mismatched types" + +[snippet] +title = { annotation_type = "Error", label = "mismatched types" } id = "E0308" -annotation_type = "Error" diff --git a/tests/fixtures/no-color/multiline_annotation2.toml b/tests/fixtures/no-color/multiline_annotation2.toml index 3287fdc..89294bc 100644 --- a/tests/fixtures/no-color/multiline_annotation2.toml +++ b/tests/fixtures/no-color/multiline_annotation2.toml @@ -12,7 +12,6 @@ label = "missing fields `lineno`, `content`" annotation_type = "Error" range = [31, 128] -[snippet.title] -label = "pattern does not mention fields `lineno`, `content`" +[snippet] +title = { annotation_type = "Error", label = "pattern does not mention fields `lineno`, `content`" } id = "E0027" -annotation_type = "Error" diff --git a/tests/fixtures/no-color/multiline_annotation3.toml b/tests/fixtures/no-color/multiline_annotation3.toml index 9fe85fb..efc1f5f 100644 --- a/tests/fixtures/no-color/multiline_annotation3.toml +++ b/tests/fixtures/no-color/multiline_annotation3.toml @@ -12,7 +12,6 @@ label = "this should not be on separate lines" annotation_type = "Error" range = [11, 18] -[snippet.title] -label = "spacing error found" +[snippet] +title = { annotation_type = "Error", label = "spacing error found" } id = "E####" -annotation_type = "Error" diff --git a/tests/fixtures/no-color/multiple_annotations.svg b/tests/fixtures/no-color/multiple_annotations.svg index 3f15144..18bca93 100644 --- a/tests/fixtures/no-color/multiple_annotations.svg +++ b/tests/fixtures/no-color/multiple_annotations.svg @@ -1,4 +1,4 @@ - +