diff --git a/examples/footer.rs b/examples/footer.rs index 8b4d078..3580905 100644 --- a/examples/footer.rs +++ b/examples/footer.rs @@ -1,4 +1,4 @@ -use annotate_snippets::{Label, Level, Renderer, Snippet}; +use annotate_snippets::{Level, Renderer, Snippet}; fn main() { let message = @@ -13,7 +13,7 @@ fn main() { "expected struct `annotate_snippets::snippet::Slice`, found reference", )), ) - .footer(Label::note( + .footer(Level::Note.title( "expected type: `snippet::Annotation`\n found type: `__&__snippet::Annotation`", )); diff --git a/src/renderer/display_list.rs b/src/renderer/display_list.rs index d54282f..d0444a3 100644 --- a/src/renderer/display_list.rs +++ b/src/renderer/display_list.rs @@ -106,39 +106,12 @@ impl<'a> DisplayList<'a> { const WARNING_TXT: &'static str = "warning"; pub(crate) fn new( - snippet::Message { - level, - id, - title, - footer, - snippets, - }: snippet::Message<'a>, + message: snippet::Message<'a>, stylesheet: &'a Stylesheet, anonymized_line_numbers: bool, margin: Option, ) -> DisplayList<'a> { - let mut body = vec![]; - - body.push(format_title( - snippet::Label { - level, - label: title, - }, - id, - )); - - for (idx, snippet) in snippets.into_iter().enumerate() { - body.append(&mut format_slice( - snippet, - idx == 0, - !footer.is_empty(), - margin, - )); - } - - for annotation in footer { - body.append(&mut format_footer(annotation)); - } + let body = format_message(message, margin, true); Self { body, @@ -725,40 +698,64 @@ impl<'a> Iterator for CursorLines<'a> { } } -fn format_label( - label: Option<&str>, - style: Option, -) -> Vec> { - let mut result = vec![]; - if let Some(label) = label { - let element_style = style.unwrap_or(DisplayTextStyle::Regular); - result.push(DisplayTextFragment { - content: label, - style: element_style, - }); +fn format_message( + snippet::Message { + level, + id, + title, + footer, + snippets, + }: snippet::Message<'_>, + margin: Option, + primary: bool, +) -> Vec> { + let mut body = vec![]; + + if !snippets.is_empty() || primary { + body.push(format_title(level, id, title)); + } else { + body.extend(format_footer(level, id, title)); } - result + + for (idx, snippet) in snippets.into_iter().enumerate() { + body.extend(format_snippet( + snippet, + idx == 0, + !footer.is_empty(), + margin, + )); + } + + for annotation in footer { + body.extend(format_message(annotation, margin, false)); + } + + body } -fn format_title<'a>(title: snippet::Label<'a>, id: Option<&'a str>) -> DisplayLine<'a> { +fn format_title<'a>(level: crate::Level, id: Option<&'a str>, label: &'a str) -> DisplayLine<'a> { DisplayLine::Raw(DisplayRawLine::Annotation { annotation: Annotation { - annotation_type: DisplayAnnotationType::from(title.level), + annotation_type: DisplayAnnotationType::from(level), id, - label: format_label(Some(title.label), Some(DisplayTextStyle::Emphasis)), + label: format_label(Some(label), Some(DisplayTextStyle::Emphasis)), }, source_aligned: false, continuation: false, }) } -fn format_footer(footer: snippet::Label<'_>) -> Vec> { +fn format_footer<'a>( + level: crate::Level, + id: Option<&'a str>, + label: &'a str, +) -> Vec> { let mut result = vec![]; - for (i, line) in footer.label.lines().enumerate() { + for (i, line) in label.lines().enumerate() { result.push(DisplayLine::Raw(DisplayRawLine::Annotation { annotation: Annotation { - annotation_type: DisplayAnnotationType::from(footer.level), - id: None, + annotation_type: DisplayAnnotationType::from(level), + id, label: format_label(Some(line), None), }, source_aligned: true, @@ -768,7 +765,22 @@ fn format_footer(footer: snippet::Label<'_>) -> Vec> { result } -fn format_slice( +fn format_label( + label: Option<&str>, + style: Option, +) -> Vec> { + let mut result = vec![]; + if let Some(label) = label { + let element_style = style.unwrap_or(DisplayTextStyle::Regular); + result.push(DisplayTextFragment { + content: label, + style: element_style, + }); + } + result +} + +fn format_snippet( snippet: snippet::Snippet<'_>, is_first: bool, has_footer: bool, @@ -777,14 +789,14 @@ fn format_slice( let main_range = snippet.annotations.first().map(|x| x.range.start); let origin = snippet.origin; let need_empty_header = origin.is_some() || is_first; - let mut body = format_body(snippet, need_empty_header, has_footer, margin); + let body = format_body(snippet, need_empty_header, has_footer, margin); let header = format_header(origin, main_range, &body, is_first); let mut result = vec![]; if let Some(header) = header { result.push(header); } - result.append(&mut body); + result.extend(body); result } @@ -1444,7 +1456,7 @@ mod tests { fn test_format_label() { let input = snippet::Level::Error .title("") - .footer(snippet::Label::error("This __is__ a title")); + .footer(snippet::Level::Error.title("This __is__ a title")); let output = from_display_lines(vec![ DisplayLine::Raw(DisplayRawLine::Annotation { annotation: Annotation { diff --git a/src/snippet.rs b/src/snippet.rs index 6ba2d2d..e7d4bef 100644 --- a/src/snippet.rs +++ b/src/snippet.rs @@ -20,7 +20,7 @@ pub struct Message<'a> { pub(crate) id: Option<&'a str>, pub(crate) title: &'a str, pub(crate) snippets: Vec>, - pub(crate) footer: Vec>, + pub(crate) footer: Vec>, } impl<'a> Message<'a> { @@ -39,52 +39,17 @@ impl<'a> Message<'a> { self } - pub fn footer(mut self, footer: Label<'a>) -> Self { + pub fn footer(mut self, footer: Message<'a>) -> Self { self.footer.push(footer); self } - pub fn footers(mut self, footer: impl IntoIterator>) -> Self { + pub fn footers(mut self, footer: impl IntoIterator>) -> Self { self.footer.extend(footer); self } } -pub struct Label<'a> { - pub(crate) level: Level, - pub(crate) label: &'a str, -} - -impl<'a> Label<'a> { - pub fn new(level: Level, label: &'a str) -> Self { - Self { level, label } - } - pub fn error(label: &'a str) -> Self { - Self::new(Level::Error, label) - } - - pub fn warning(label: &'a str) -> Self { - Self::new(Level::Warning, label) - } - - pub fn info(label: &'a str) -> Self { - Self::new(Level::Info, label) - } - - pub fn note(label: &'a str) -> Self { - Self::new(Level::Note, label) - } - - pub fn help(label: &'a str) -> Self { - Self::new(Level::Help, label) - } - - pub fn label(mut self, label: &'a str) -> Self { - self.label = label; - self - } -} - /// Structure containing the slice of text to be annotated and /// basic information about the location of the slice. /// diff --git a/tests/fixtures/deserialize.rs b/tests/fixtures/deserialize.rs index 6bfe76f..165c341 100644 --- a/tests/fixtures/deserialize.rs +++ b/tests/fixtures/deserialize.rs @@ -1,7 +1,7 @@ use serde::{Deserialize, Deserializer, Serialize}; use std::ops::Range; -use annotate_snippets::{renderer::Margin, Annotation, Label, Level, Message, Renderer, Snippet}; +use annotate_snippets::{renderer::Margin, Annotation, Level, Message, Renderer, Snippet}; #[derive(Deserialize)] pub struct Fixture<'a> { @@ -20,10 +20,9 @@ pub struct MessageDef<'a> { #[serde(default)] #[serde(borrow)] pub id: Option<&'a str>, - #[serde(deserialize_with = "deserialize_labels")] #[serde(default)] #[serde(borrow)] - pub footer: Vec>, + pub footer: Vec>, #[serde(deserialize_with = "deserialize_snippets")] #[serde(borrow)] pub snippets: Vec>, @@ -43,28 +42,11 @@ impl<'a> From> for Message<'a> { message = message.id(id); } message = message.snippets(snippets); - message = message.footers(footer); + message = message.footers(footer.into_iter().map(Into::into)); message } } -fn deserialize_labels<'de, D>(deserializer: D) -> Result>, D::Error> -where - D: Deserializer<'de>, -{ - #[derive(Deserialize)] - struct Wrapper<'a>( - #[serde(with = "LabelDef")] - #[serde(borrow)] - LabelDef<'a>, - ); - - let v = Vec::deserialize(deserializer)?; - Ok(v.into_iter() - .map(|Wrapper(a)| Label::new(a.level, a.label)) - .collect()) -} - fn deserialize_snippets<'de, D>(deserializer: D) -> Result>, D::Error> where D: Deserializer<'de>,