substrait_validator/output/
comment.rs1use crate::output::path;
11
12#[derive(Clone, Debug, PartialEq, Eq, Default)]
15pub struct Comment {
16 elements: Vec<Element>,
18}
19
20impl Comment {
21 pub fn new() -> Self {
23 Self::default()
24 }
25
26 pub fn plain<S: ToString>(mut self, text: S) -> Self {
28 self.push(Element::Span(text.to_string().into()));
29 self
30 }
31
32 pub fn link<S: ToString>(mut self, text: S, path: path::PathBuf) -> Self {
34 self.push(Element::Span(Span {
35 text: text.to_string(),
36 link: Some(Link::Path(path)),
37 }));
38 self
39 }
40
41 pub fn url<S: ToString, U: ToString>(mut self, text: S, url: U) -> Self {
43 self.push(Element::Span(Span {
44 text: text.to_string(),
45 link: Some(Link::Url(url.to_string())),
46 }));
47 self
48 }
49
50 pub fn nl(mut self) -> Self {
52 self.push(Element::NewLine);
53 self
54 }
55
56 pub fn lo(mut self) -> Self {
58 self.push(Element::ListOpen);
59 self
60 }
61
62 pub fn li(mut self) -> Self {
64 self.push(Element::ListNext);
65 self
66 }
67
68 pub fn lc(mut self) -> Self {
70 self.push(Element::ListClose);
71 self
72 }
73
74 pub fn push(&mut self, element: Element) {
76 match self.elements.pop() {
79 None => self.elements.push(element),
80 Some(Element::Span(s1)) => {
81 if let Element::Span(s2) = element {
82 let (s1, maybe_s2) = merge_spans(s1, s2);
83 self.elements.push(Element::Span(s1));
84 if let Some(s2) = maybe_s2 {
85 self.elements.push(Element::Span(s2));
86 }
87 } else {
88 self.elements.push(Element::Span(s1));
89 self.elements.push(element);
90 }
91 }
92 Some(Element::NewLine) => {
93 if matches!(element, Element::Span(_)) {
94 self.elements.push(Element::NewLine);
95 }
96 self.elements.push(element);
97 }
98 Some(Element::ListOpen) => {
99 self.elements.push(Element::ListOpen);
100 if !matches!(element, Element::ListNext) {
101 self.elements.push(element);
102 }
103 }
104 Some(Element::ListNext) => {
105 self.elements.push(Element::ListNext);
106 if !matches!(element, Element::ListNext) {
107 self.elements.push(element);
108 }
109 }
110 Some(Element::ListClose) => {
111 self.elements.push(Element::ListClose);
112 if !matches!(element, Element::NewLine) {
113 self.elements.push(element);
114 }
115 }
116 }
117 }
118
119 pub fn extend(&mut self, other: Comment) {
121 let mut it = other.elements.into_iter();
122
123 if let Some(element) = it.next() {
126 self.push(element);
127 }
128
129 self.elements.extend(it);
132 }
133
134 pub fn elements(&self) -> &[Element] {
142 &self.elements
143 }
144}
145
146impl std::fmt::Display for Comment {
147 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
148 let mut indent = 0;
149 for element in self.elements.iter() {
150 match element {
151 Element::Span(span) => span.fmt(f),
152 Element::NewLine => write!(f, "\n\n{: >1$}", "", indent),
153 Element::ListOpen => {
154 indent += 3;
155 write!(f, "\n\n{: >1$}", "- ", indent)
156 }
157 Element::ListNext => {
158 write!(f, "\n\n{: >1$}", "- ", indent)
159 }
160 Element::ListClose => {
161 indent -= 3;
162 write!(f, "\n\n{: >1$}", "", indent)
163 }
164 }?;
165 }
166 Ok(())
167 }
168}
169
170impl From<String> for Comment {
171 fn from(text: String) -> Self {
172 Self {
173 elements: vec![Element::Span(text.into())],
174 }
175 }
176}
177
178#[derive(Clone, Debug, PartialEq, Eq)]
180pub enum Element {
181 Span(Span),
183
184 NewLine,
186
187 ListOpen,
189
190 ListNext,
192
193 ListClose,
195}
196
197#[derive(Clone, Debug, PartialEq, Default, Eq)]
199pub struct Brief {
200 spans: Vec<Span>,
203}
204
205impl Brief {
206 pub fn new() -> Self {
208 Self::default()
209 }
210
211 pub fn plain<S: ToString>(mut self, text: S) -> Self {
213 self.push(text.to_string().into());
214 self
215 }
216
217 pub fn link<S: ToString>(mut self, text: S, path: path::PathBuf) -> Self {
219 self.push(Span {
220 text: text.to_string(),
221 link: Some(Link::Path(path)),
222 });
223 self
224 }
225
226 pub fn url<S: ToString, U: ToString>(mut self, text: S, url: U) -> Self {
228 self.push(Span {
229 text: text.to_string(),
230 link: Some(Link::Url(url.to_string())),
231 });
232 self
233 }
234
235 pub fn push(&mut self, span: Span) {
237 if let Some(s1) = self.spans.pop() {
238 let s2 = span;
239 let (s1, maybe_s2) = merge_spans(s1, s2);
240 self.spans.push(s1);
241 if let Some(s2) = maybe_s2 {
242 self.spans.push(s2);
243 }
244 } else {
245 self.spans.push(span);
246 }
247 }
248
249 pub fn extend(&mut self, other: Brief) {
251 let mut it = other.spans.into_iter();
252
253 if let Some(element) = it.next() {
256 self.push(element);
257 }
258
259 self.spans.extend(it);
262 }
263
264 pub fn spans(&self) -> &[Span] {
266 &self.spans
267 }
268}
269
270impl std::fmt::Display for Brief {
271 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
272 for span in self.spans.iter() {
273 span.fmt(f)?;
274 }
275 Ok(())
276 }
277}
278
279impl From<String> for Brief {
280 fn from(text: String) -> Self {
281 Self {
282 spans: vec![text.into()],
283 }
284 }
285}
286
287impl From<Brief> for Comment {
288 fn from(brief: Brief) -> Self {
289 Self {
290 elements: brief.spans.into_iter().map(Element::Span).collect(),
291 }
292 }
293}
294
295#[derive(Clone, Debug, PartialEq, Eq)]
297pub struct Span {
298 pub text: String,
300
301 pub link: Option<Link>,
303}
304
305impl std::fmt::Display for Span {
306 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
307 write!(f, "{}", self.text)
308 }
309}
310
311impl From<String> for Span {
312 fn from(text: String) -> Self {
313 Span { text, link: None }
314 }
315}
316
317fn merge_spans(mut a: Span, b: Span) -> (Span, Option<Span>) {
320 if b.text.is_empty() {
321 return (a, None);
322 }
323 if !a.text.ends_with(' ') && !b.text.starts_with(' ') {
324 a.text.push(' ');
325 }
326 if a.link == b.link {
327 a.text += &b.text;
328 return (a, None);
329 }
330 (a, Some(b))
331}
332
333#[derive(Clone, Debug, PartialEq, Eq)]
335pub enum Link {
336 Path(path::PathBuf),
338
339 Url(String),
341}