Skip to content

Commit 6c23a41

Browse files
authored
Merge pull request #98 from epage/footer
fix!: Generalize footer to be any Message
2 parents 0d1364d + 535e146 commit 6c23a41

File tree

4 files changed

+72
-113
lines changed

4 files changed

+72
-113
lines changed

examples/footer.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use annotate_snippets::{Label, Level, Renderer, Snippet};
1+
use annotate_snippets::{Level, Renderer, Snippet};
22

33
fn main() {
44
let message =
@@ -13,7 +13,7 @@ fn main() {
1313
"expected struct `annotate_snippets::snippet::Slice`, found reference",
1414
)),
1515
)
16-
.footer(Label::note(
16+
.footer(Level::Note.title(
1717
"expected type: `snippet::Annotation`\n found type: `__&__snippet::Annotation`",
1818
));
1919

src/renderer/display_list.rs

+64-52
Original file line numberDiff line numberDiff line change
@@ -106,39 +106,12 @@ impl<'a> DisplayList<'a> {
106106
const WARNING_TXT: &'static str = "warning";
107107

108108
pub(crate) fn new(
109-
snippet::Message {
110-
level,
111-
id,
112-
title,
113-
footer,
114-
snippets,
115-
}: snippet::Message<'a>,
109+
message: snippet::Message<'a>,
116110
stylesheet: &'a Stylesheet,
117111
anonymized_line_numbers: bool,
118112
margin: Option<Margin>,
119113
) -> DisplayList<'a> {
120-
let mut body = vec![];
121-
122-
body.push(format_title(
123-
snippet::Label {
124-
level,
125-
label: title,
126-
},
127-
id,
128-
));
129-
130-
for (idx, snippet) in snippets.into_iter().enumerate() {
131-
body.append(&mut format_slice(
132-
snippet,
133-
idx == 0,
134-
!footer.is_empty(),
135-
margin,
136-
));
137-
}
138-
139-
for annotation in footer {
140-
body.append(&mut format_footer(annotation));
141-
}
114+
let body = format_message(message, margin, true);
142115

143116
Self {
144117
body,
@@ -725,40 +698,64 @@ impl<'a> Iterator for CursorLines<'a> {
725698
}
726699
}
727700

728-
fn format_label(
729-
label: Option<&str>,
730-
style: Option<DisplayTextStyle>,
731-
) -> Vec<DisplayTextFragment<'_>> {
732-
let mut result = vec![];
733-
if let Some(label) = label {
734-
let element_style = style.unwrap_or(DisplayTextStyle::Regular);
735-
result.push(DisplayTextFragment {
736-
content: label,
737-
style: element_style,
738-
});
701+
fn format_message(
702+
snippet::Message {
703+
level,
704+
id,
705+
title,
706+
footer,
707+
snippets,
708+
}: snippet::Message<'_>,
709+
margin: Option<Margin>,
710+
primary: bool,
711+
) -> Vec<DisplayLine<'_>> {
712+
let mut body = vec![];
713+
714+
if !snippets.is_empty() || primary {
715+
body.push(format_title(level, id, title));
716+
} else {
717+
body.extend(format_footer(level, id, title));
739718
}
740-
result
719+
720+
for (idx, snippet) in snippets.into_iter().enumerate() {
721+
body.extend(format_snippet(
722+
snippet,
723+
idx == 0,
724+
!footer.is_empty(),
725+
margin,
726+
));
727+
}
728+
729+
for annotation in footer {
730+
body.extend(format_message(annotation, margin, false));
731+
}
732+
733+
body
741734
}
742735

743-
fn format_title<'a>(title: snippet::Label<'a>, id: Option<&'a str>) -> DisplayLine<'a> {
736+
fn format_title<'a>(level: crate::Level, id: Option<&'a str>, label: &'a str) -> DisplayLine<'a> {
744737
DisplayLine::Raw(DisplayRawLine::Annotation {
745738
annotation: Annotation {
746-
annotation_type: DisplayAnnotationType::from(title.level),
739+
annotation_type: DisplayAnnotationType::from(level),
747740
id,
748-
label: format_label(Some(title.label), Some(DisplayTextStyle::Emphasis)),
741+
label: format_label(Some(label), Some(DisplayTextStyle::Emphasis)),
749742
},
750743
source_aligned: false,
751744
continuation: false,
752745
})
753746
}
754747

755-
fn format_footer(footer: snippet::Label<'_>) -> Vec<DisplayLine<'_>> {
748+
fn format_footer<'a>(
749+
level: crate::Level,
750+
id: Option<&'a str>,
751+
label: &'a str,
752+
) -> Vec<DisplayLine<'a>> {
756753
let mut result = vec![];
757-
for (i, line) in footer.label.lines().enumerate() {
754+
for (i, line) in label.lines().enumerate() {
758755
result.push(DisplayLine::Raw(DisplayRawLine::Annotation {
759756
annotation: Annotation {
760-
annotation_type: DisplayAnnotationType::from(footer.level),
761-
id: None,
757+
annotation_type: DisplayAnnotationType::from(level),
758+
id,
762759
label: format_label(Some(line), None),
763760
},
764761
source_aligned: true,
@@ -768,7 +765,22 @@ fn format_footer(footer: snippet::Label<'_>) -> Vec<DisplayLine<'_>> {
768765
result
769766
}
770767

771-
fn format_slice(
768+
fn format_label(
769+
label: Option<&str>,
770+
style: Option<DisplayTextStyle>,
771+
) -> Vec<DisplayTextFragment<'_>> {
772+
let mut result = vec![];
773+
if let Some(label) = label {
774+
let element_style = style.unwrap_or(DisplayTextStyle::Regular);
775+
result.push(DisplayTextFragment {
776+
content: label,
777+
style: element_style,
778+
});
779+
}
780+
result
781+
}
782+
783+
fn format_snippet(
772784
snippet: snippet::Snippet<'_>,
773785
is_first: bool,
774786
has_footer: bool,
@@ -777,14 +789,14 @@ fn format_slice(
777789
let main_range = snippet.annotations.first().map(|x| x.range.start);
778790
let origin = snippet.origin;
779791
let need_empty_header = origin.is_some() || is_first;
780-
let mut body = format_body(snippet, need_empty_header, has_footer, margin);
792+
let body = format_body(snippet, need_empty_header, has_footer, margin);
781793
let header = format_header(origin, main_range, &body, is_first);
782794
let mut result = vec![];
783795

784796
if let Some(header) = header {
785797
result.push(header);
786798
}
787-
result.append(&mut body);
799+
result.extend(body);
788800
result
789801
}
790802

@@ -1444,7 +1456,7 @@ mod tests {
14441456
fn test_format_label() {
14451457
let input = snippet::Level::Error
14461458
.title("")
1447-
.footer(snippet::Label::error("This __is__ a title"));
1459+
.footer(snippet::Level::Error.title("This __is__ a title"));
14481460
let output = from_display_lines(vec![
14491461
DisplayLine::Raw(DisplayRawLine::Annotation {
14501462
annotation: Annotation {

src/snippet.rs

+3-38
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ pub struct Message<'a> {
2020
pub(crate) id: Option<&'a str>,
2121
pub(crate) title: &'a str,
2222
pub(crate) snippets: Vec<Snippet<'a>>,
23-
pub(crate) footer: Vec<Label<'a>>,
23+
pub(crate) footer: Vec<Message<'a>>,
2424
}
2525

2626
impl<'a> Message<'a> {
@@ -39,52 +39,17 @@ impl<'a> Message<'a> {
3939
self
4040
}
4141

42-
pub fn footer(mut self, footer: Label<'a>) -> Self {
42+
pub fn footer(mut self, footer: Message<'a>) -> Self {
4343
self.footer.push(footer);
4444
self
4545
}
4646

47-
pub fn footers(mut self, footer: impl IntoIterator<Item = Label<'a>>) -> Self {
47+
pub fn footers(mut self, footer: impl IntoIterator<Item = Message<'a>>) -> Self {
4848
self.footer.extend(footer);
4949
self
5050
}
5151
}
5252

53-
pub struct Label<'a> {
54-
pub(crate) level: Level,
55-
pub(crate) label: &'a str,
56-
}
57-
58-
impl<'a> Label<'a> {
59-
pub fn new(level: Level, label: &'a str) -> Self {
60-
Self { level, label }
61-
}
62-
pub fn error(label: &'a str) -> Self {
63-
Self::new(Level::Error, label)
64-
}
65-
66-
pub fn warning(label: &'a str) -> Self {
67-
Self::new(Level::Warning, label)
68-
}
69-
70-
pub fn info(label: &'a str) -> Self {
71-
Self::new(Level::Info, label)
72-
}
73-
74-
pub fn note(label: &'a str) -> Self {
75-
Self::new(Level::Note, label)
76-
}
77-
78-
pub fn help(label: &'a str) -> Self {
79-
Self::new(Level::Help, label)
80-
}
81-
82-
pub fn label(mut self, label: &'a str) -> Self {
83-
self.label = label;
84-
self
85-
}
86-
}
87-
8853
/// Structure containing the slice of text to be annotated and
8954
/// basic information about the location of the slice.
9055
///

tests/fixtures/deserialize.rs

+3-21
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use serde::{Deserialize, Deserializer, Serialize};
22
use std::ops::Range;
33

4-
use annotate_snippets::{renderer::Margin, Annotation, Label, Level, Message, Renderer, Snippet};
4+
use annotate_snippets::{renderer::Margin, Annotation, Level, Message, Renderer, Snippet};
55

66
#[derive(Deserialize)]
77
pub struct Fixture<'a> {
@@ -20,10 +20,9 @@ pub struct MessageDef<'a> {
2020
#[serde(default)]
2121
#[serde(borrow)]
2222
pub id: Option<&'a str>,
23-
#[serde(deserialize_with = "deserialize_labels")]
2423
#[serde(default)]
2524
#[serde(borrow)]
26-
pub footer: Vec<Label<'a>>,
25+
pub footer: Vec<MessageDef<'a>>,
2726
#[serde(deserialize_with = "deserialize_snippets")]
2827
#[serde(borrow)]
2928
pub snippets: Vec<Snippet<'a>>,
@@ -43,28 +42,11 @@ impl<'a> From<MessageDef<'a>> for Message<'a> {
4342
message = message.id(id);
4443
}
4544
message = message.snippets(snippets);
46-
message = message.footers(footer);
45+
message = message.footers(footer.into_iter().map(Into::into));
4746
message
4847
}
4948
}
5049

51-
fn deserialize_labels<'de, D>(deserializer: D) -> Result<Vec<Label<'de>>, D::Error>
52-
where
53-
D: Deserializer<'de>,
54-
{
55-
#[derive(Deserialize)]
56-
struct Wrapper<'a>(
57-
#[serde(with = "LabelDef")]
58-
#[serde(borrow)]
59-
LabelDef<'a>,
60-
);
61-
62-
let v = Vec::deserialize(deserializer)?;
63-
Ok(v.into_iter()
64-
.map(|Wrapper(a)| Label::new(a.level, a.label))
65-
.collect())
66-
}
67-
6850
fn deserialize_snippets<'de, D>(deserializer: D) -> Result<Vec<Snippet<'de>>, D::Error>
6951
where
7052
D: Deserializer<'de>,

0 commit comments

Comments
 (0)