1//! Structures used as an input for the library.
2//!
3//! Example:
4//!
5//! ```
6//! use annotate_snippets::*;
7//!
8//! Level::Error.title("mismatched types")
9//! .snippet(Snippet::source("Foo").line_start(51).origin("src/format.rs"))
10//! .snippet(Snippet::source("Faa").line_start(129).origin("src/display.rs"));
11//! ```
12
13use std::ops::Range;
14
15/// Primary structure provided for formatting
16///
17/// See [`Level::title`] to create a [`Message`]
18#[derive(Debug)]
19pub struct Message<'a> {
20 pub(crate) level: Level,
21 pub(crate) id: Option<&'a str>,
22 pub(crate) title: &'a str,
23 pub(crate) snippets: Vec<Snippet<'a>>,
24 pub(crate) footer: Vec<Message<'a>>,
25}
26
27impl<'a> Message<'a> {
28 pub fn id(mut self, id: &'a str) -> Self {
29 self.id = Some(id);
30 self
31 }
32
33 pub fn snippet(mut self, slice: Snippet<'a>) -> Self {
34 self.snippets.push(slice);
35 self
36 }
37
38 pub fn snippets(mut self, slice: impl IntoIterator<Item = Snippet<'a>>) -> Self {
39 self.snippets.extend(slice);
40 self
41 }
42
43 pub fn footer(mut self, footer: Message<'a>) -> Self {
44 self.footer.push(footer);
45 self
46 }
47
48 pub fn footers(mut self, footer: impl IntoIterator<Item = Message<'a>>) -> Self {
49 self.footer.extend(footer);
50 self
51 }
52}
53
54/// Structure containing the slice of text to be annotated and
55/// basic information about the location of the slice.
56///
57/// One `Snippet` is meant to represent a single, continuous,
58/// slice of source code that you want to annotate.
59#[derive(Debug)]
60pub struct Snippet<'a> {
61 pub(crate) origin: Option<&'a str>,
62 pub(crate) line_start: usize,
63
64 pub(crate) source: &'a str,
65 pub(crate) annotations: Vec<Annotation<'a>>,
66
67 pub(crate) fold: bool,
68}
69
70impl<'a> Snippet<'a> {
71 pub fn source(source: &'a str) -> Self {
72 Self {
73 origin: None,
74 line_start: 1,
75 source,
76 annotations: vec![],
77 fold: false,
78 }
79 }
80
81 pub fn line_start(mut self, line_start: usize) -> Self {
82 self.line_start = line_start;
83 self
84 }
85
86 pub fn origin(mut self, origin: &'a str) -> Self {
87 self.origin = Some(origin);
88 self
89 }
90
91 pub fn annotation(mut self, annotation: Annotation<'a>) -> Self {
92 self.annotations.push(annotation);
93 self
94 }
95
96 pub fn annotations(mut self, annotation: impl IntoIterator<Item = Annotation<'a>>) -> Self {
97 self.annotations.extend(annotation);
98 self
99 }
100
101 /// Hide lines without [`Annotation`]s
102 pub fn fold(mut self, fold: bool) -> Self {
103 self.fold = fold;
104 self
105 }
106}
107
108/// An annotation for a [`Snippet`].
109///
110/// See [`Level::span`] to create a [`Annotation`]
111#[derive(Debug)]
112pub struct Annotation<'a> {
113 /// The byte range of the annotation in the `source` string
114 pub(crate) range: Range<usize>,
115 pub(crate) label: Option<&'a str>,
116 pub(crate) level: Level,
117}
118
119impl<'a> Annotation<'a> {
120 pub fn label(mut self, label: &'a str) -> Self {
121 self.label = Some(label);
122 self
123 }
124}
125
126/// Types of annotations.
127#[derive(Debug, Clone, Copy, PartialEq)]
128pub enum Level {
129 /// Error annotations are displayed using red color and "^" character.
130 Error,
131 /// Warning annotations are displayed using blue color and "-" character.
132 Warning,
133 Info,
134 Note,
135 Help,
136}
137
138impl Level {
139 pub fn title(self, title: &str) -> Message<'_> {
140 Message {
141 level: self,
142 id: None,
143 title,
144 snippets: vec![],
145 footer: vec![],
146 }
147 }
148
149 /// Create a [`Annotation`] with the given span for a [`Snippet`]
150 pub fn span<'a>(self, span: Range<usize>) -> Annotation<'a> {
151 Annotation {
152 range: span,
153 label: None,
154 level: self,
155 }
156 }
157}
158