/rust/registry/src/index.crates.io-6f17d22bba15001f/pulldown-cmark-0.13.0/src/html.rs
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright 2015 Google Inc. All rights reserved. |
2 | | // |
3 | | // Permission is hereby granted, free of charge, to any person obtaining a copy |
4 | | // of this software and associated documentation files (the "Software"), to deal |
5 | | // in the Software without restriction, including without limitation the rights |
6 | | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
7 | | // copies of the Software, and to permit persons to whom the Software is |
8 | | // furnished to do so, subject to the following conditions: |
9 | | // |
10 | | // The above copyright notice and this permission notice shall be included in |
11 | | // all copies or substantial portions of the Software. |
12 | | // |
13 | | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
14 | | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
15 | | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
16 | | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
17 | | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
18 | | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
19 | | // THE SOFTWARE. |
20 | | |
21 | | //! HTML renderer that takes an iterator of events as input. |
22 | | |
23 | | use std::collections::HashMap; |
24 | | |
25 | | use crate::strings::CowStr; |
26 | | use crate::Event::*; |
27 | | use crate::{Alignment, BlockQuoteKind, CodeBlockKind, Event, LinkType, Tag, TagEnd}; |
28 | | use pulldown_cmark_escape::{ |
29 | | escape_href, escape_html, escape_html_body_text, FmtWriter, IoWriter, StrWrite, |
30 | | }; |
31 | | |
32 | | enum TableState { |
33 | | Head, |
34 | | Body, |
35 | | } |
36 | | |
37 | | struct HtmlWriter<'a, I, W> { |
38 | | /// Iterator supplying events. |
39 | | iter: I, |
40 | | |
41 | | /// Writer to write to. |
42 | | writer: W, |
43 | | |
44 | | /// Whether or not the last write wrote a newline. |
45 | | end_newline: bool, |
46 | | |
47 | | /// Whether if inside a metadata block (text should not be written) |
48 | | in_non_writing_block: bool, |
49 | | |
50 | | table_state: TableState, |
51 | | table_alignments: Vec<Alignment>, |
52 | | table_cell_index: usize, |
53 | | numbers: HashMap<CowStr<'a>, usize>, |
54 | | } |
55 | | |
56 | | impl<'a, I, W> HtmlWriter<'a, I, W> |
57 | | where |
58 | | I: Iterator<Item = Event<'a>>, |
59 | | W: StrWrite, |
60 | | { |
61 | 0 | fn new(iter: I, writer: W) -> Self { |
62 | 0 | Self { |
63 | 0 | iter, |
64 | 0 | writer, |
65 | 0 | end_newline: true, |
66 | 0 | in_non_writing_block: false, |
67 | 0 | table_state: TableState::Head, |
68 | 0 | table_alignments: vec![], |
69 | 0 | table_cell_index: 0, |
70 | 0 | numbers: HashMap::new(), |
71 | 0 | } |
72 | 0 | } |
73 | | |
74 | | /// Writes a new line. |
75 | | #[inline] |
76 | 0 | fn write_newline(&mut self) -> Result<(), W::Error> { |
77 | 0 | self.end_newline = true; |
78 | 0 | self.writer.write_str("\n") |
79 | 0 | } |
80 | | |
81 | | /// Writes a buffer, and tracks whether or not a newline was written. |
82 | | #[inline] |
83 | 0 | fn write(&mut self, s: &str) -> Result<(), W::Error> { |
84 | 0 | self.writer.write_str(s)?; |
85 | | |
86 | 0 | if !s.is_empty() { |
87 | 0 | self.end_newline = s.ends_with('\n'); |
88 | 0 | } |
89 | 0 | Ok(()) |
90 | 0 | } |
91 | | |
92 | 0 | fn run(mut self) -> Result<(), W::Error> { |
93 | 0 | while let Some(event) = self.iter.next() { |
94 | 0 | match event { |
95 | 0 | Start(tag) => { |
96 | 0 | self.start_tag(tag)?; |
97 | | } |
98 | 0 | End(tag) => { |
99 | 0 | self.end_tag(tag)?; |
100 | | } |
101 | 0 | Text(text) => { |
102 | 0 | if !self.in_non_writing_block { |
103 | 0 | escape_html_body_text(&mut self.writer, &text)?; |
104 | 0 | self.end_newline = text.ends_with('\n'); |
105 | 0 | } |
106 | | } |
107 | 0 | Code(text) => { |
108 | 0 | self.write("<code>")?; |
109 | 0 | escape_html_body_text(&mut self.writer, &text)?; |
110 | 0 | self.write("</code>")?; |
111 | | } |
112 | 0 | InlineMath(text) => { |
113 | 0 | self.write(r#"<span class="math math-inline">"#)?; |
114 | 0 | escape_html(&mut self.writer, &text)?; |
115 | 0 | self.write("</span>")?; |
116 | | } |
117 | 0 | DisplayMath(text) => { |
118 | 0 | self.write(r#"<span class="math math-display">"#)?; |
119 | 0 | escape_html(&mut self.writer, &text)?; |
120 | 0 | self.write("</span>")?; |
121 | | } |
122 | 0 | Html(html) | InlineHtml(html) => { |
123 | 0 | self.write(&html)?; |
124 | | } |
125 | | SoftBreak => { |
126 | 0 | self.write_newline()?; |
127 | | } |
128 | | HardBreak => { |
129 | 0 | self.write("<br />\n")?; |
130 | | } |
131 | | Rule => { |
132 | 0 | if self.end_newline { |
133 | 0 | self.write("<hr />\n")?; |
134 | | } else { |
135 | 0 | self.write("\n<hr />\n")?; |
136 | | } |
137 | | } |
138 | 0 | FootnoteReference(name) => { |
139 | 0 | let len = self.numbers.len() + 1; |
140 | 0 | self.write("<sup class=\"footnote-reference\"><a href=\"#")?; |
141 | 0 | escape_html(&mut self.writer, &name)?; |
142 | 0 | self.write("\">")?; |
143 | 0 | let number = *self.numbers.entry(name).or_insert(len); |
144 | 0 | write!(&mut self.writer, "{}", number)?; |
145 | 0 | self.write("</a></sup>")?; |
146 | | } |
147 | | TaskListMarker(true) => { |
148 | 0 | self.write("<input disabled=\"\" type=\"checkbox\" checked=\"\"/>\n")?; |
149 | | } |
150 | | TaskListMarker(false) => { |
151 | 0 | self.write("<input disabled=\"\" type=\"checkbox\"/>\n")?; |
152 | | } |
153 | | } |
154 | | } |
155 | 0 | Ok(()) |
156 | 0 | } |
157 | | |
158 | | /// Writes the start of an HTML tag. |
159 | 0 | fn start_tag(&mut self, tag: Tag<'a>) -> Result<(), W::Error> { |
160 | 0 | match tag { |
161 | 0 | Tag::HtmlBlock => Ok(()), |
162 | | Tag::Paragraph => { |
163 | 0 | if self.end_newline { |
164 | 0 | self.write("<p>") |
165 | | } else { |
166 | 0 | self.write("\n<p>") |
167 | | } |
168 | | } |
169 | | Tag::Heading { |
170 | 0 | level, |
171 | 0 | id, |
172 | 0 | classes, |
173 | 0 | attrs, |
174 | 0 | } => { |
175 | 0 | if self.end_newline { |
176 | 0 | self.write("<")?; |
177 | | } else { |
178 | 0 | self.write("\n<")?; |
179 | | } |
180 | 0 | write!(&mut self.writer, "{}", level)?; |
181 | 0 | if let Some(id) = id { |
182 | 0 | self.write(" id=\"")?; |
183 | 0 | escape_html(&mut self.writer, &id)?; |
184 | 0 | self.write("\"")?; |
185 | 0 | } |
186 | 0 | let mut classes = classes.iter(); |
187 | 0 | if let Some(class) = classes.next() { |
188 | 0 | self.write(" class=\"")?; |
189 | 0 | escape_html(&mut self.writer, class)?; |
190 | 0 | for class in classes { |
191 | 0 | self.write(" ")?; |
192 | 0 | escape_html(&mut self.writer, class)?; |
193 | | } |
194 | 0 | self.write("\"")?; |
195 | 0 | } |
196 | 0 | for (attr, value) in attrs { |
197 | 0 | self.write(" ")?; |
198 | 0 | escape_html(&mut self.writer, &attr)?; |
199 | 0 | if let Some(val) = value { |
200 | 0 | self.write("=\"")?; |
201 | 0 | escape_html(&mut self.writer, &val)?; |
202 | 0 | self.write("\"")?; |
203 | | } else { |
204 | 0 | self.write("=\"\"")?; |
205 | | } |
206 | | } |
207 | 0 | self.write(">") |
208 | | } |
209 | 0 | Tag::Table(alignments) => { |
210 | 0 | self.table_alignments = alignments; |
211 | 0 | self.write("<table>") |
212 | | } |
213 | | Tag::TableHead => { |
214 | 0 | self.table_state = TableState::Head; |
215 | 0 | self.table_cell_index = 0; |
216 | 0 | self.write("<thead><tr>") |
217 | | } |
218 | | Tag::TableRow => { |
219 | 0 | self.table_cell_index = 0; |
220 | 0 | self.write("<tr>") |
221 | | } |
222 | | Tag::TableCell => { |
223 | 0 | match self.table_state { |
224 | | TableState::Head => { |
225 | 0 | self.write("<th")?; |
226 | | } |
227 | | TableState::Body => { |
228 | 0 | self.write("<td")?; |
229 | | } |
230 | | } |
231 | 0 | match self.table_alignments.get(self.table_cell_index) { |
232 | 0 | Some(&Alignment::Left) => self.write(" style=\"text-align: left\">"), |
233 | 0 | Some(&Alignment::Center) => self.write(" style=\"text-align: center\">"), |
234 | 0 | Some(&Alignment::Right) => self.write(" style=\"text-align: right\">"), |
235 | 0 | _ => self.write(">"), |
236 | | } |
237 | | } |
238 | 0 | Tag::BlockQuote(kind) => { |
239 | 0 | let class_str = match kind { |
240 | 0 | None => "", |
241 | 0 | Some(kind) => match kind { |
242 | 0 | BlockQuoteKind::Note => " class=\"markdown-alert-note\"", |
243 | 0 | BlockQuoteKind::Tip => " class=\"markdown-alert-tip\"", |
244 | 0 | BlockQuoteKind::Important => " class=\"markdown-alert-important\"", |
245 | 0 | BlockQuoteKind::Warning => " class=\"markdown-alert-warning\"", |
246 | 0 | BlockQuoteKind::Caution => " class=\"markdown-alert-caution\"", |
247 | | }, |
248 | | }; |
249 | 0 | if self.end_newline { |
250 | 0 | self.write(&format!("<blockquote{}>\n", class_str)) |
251 | | } else { |
252 | 0 | self.write(&format!("\n<blockquote{}>\n", class_str)) |
253 | | } |
254 | | } |
255 | 0 | Tag::CodeBlock(info) => { |
256 | 0 | if !self.end_newline { |
257 | 0 | self.write_newline()?; |
258 | 0 | } |
259 | 0 | match info { |
260 | 0 | CodeBlockKind::Fenced(info) => { |
261 | 0 | let lang = info.split(' ').next().unwrap(); |
262 | 0 | if lang.is_empty() { |
263 | 0 | self.write("<pre><code>") |
264 | | } else { |
265 | 0 | self.write("<pre><code class=\"language-")?; |
266 | 0 | escape_html(&mut self.writer, lang)?; |
267 | 0 | self.write("\">") |
268 | | } |
269 | | } |
270 | 0 | CodeBlockKind::Indented => self.write("<pre><code>"), |
271 | | } |
272 | | } |
273 | | Tag::List(Some(1)) => { |
274 | 0 | if self.end_newline { |
275 | 0 | self.write("<ol>\n") |
276 | | } else { |
277 | 0 | self.write("\n<ol>\n") |
278 | | } |
279 | | } |
280 | 0 | Tag::List(Some(start)) => { |
281 | 0 | if self.end_newline { |
282 | 0 | self.write("<ol start=\"")?; |
283 | | } else { |
284 | 0 | self.write("\n<ol start=\"")?; |
285 | | } |
286 | 0 | write!(&mut self.writer, "{}", start)?; |
287 | 0 | self.write("\">\n") |
288 | | } |
289 | | Tag::List(None) => { |
290 | 0 | if self.end_newline { |
291 | 0 | self.write("<ul>\n") |
292 | | } else { |
293 | 0 | self.write("\n<ul>\n") |
294 | | } |
295 | | } |
296 | | Tag::Item => { |
297 | 0 | if self.end_newline { |
298 | 0 | self.write("<li>") |
299 | | } else { |
300 | 0 | self.write("\n<li>") |
301 | | } |
302 | | } |
303 | | Tag::DefinitionList => { |
304 | 0 | if self.end_newline { |
305 | 0 | self.write("<dl>\n") |
306 | | } else { |
307 | 0 | self.write("\n<dl>\n") |
308 | | } |
309 | | } |
310 | | Tag::DefinitionListTitle => { |
311 | 0 | if self.end_newline { |
312 | 0 | self.write("<dt>") |
313 | | } else { |
314 | 0 | self.write("\n<dt>") |
315 | | } |
316 | | } |
317 | | Tag::DefinitionListDefinition => { |
318 | 0 | if self.end_newline { |
319 | 0 | self.write("<dd>") |
320 | | } else { |
321 | 0 | self.write("\n<dd>") |
322 | | } |
323 | | } |
324 | 0 | Tag::Subscript => self.write("<sub>"), |
325 | 0 | Tag::Superscript => self.write("<sup>"), |
326 | 0 | Tag::Emphasis => self.write("<em>"), |
327 | 0 | Tag::Strong => self.write("<strong>"), |
328 | 0 | Tag::Strikethrough => self.write("<del>"), |
329 | | Tag::Link { |
330 | | link_type: LinkType::Email, |
331 | 0 | dest_url, |
332 | 0 | title, |
333 | 0 | id: _, |
334 | 0 | } => { |
335 | 0 | self.write("<a href=\"mailto:")?; |
336 | 0 | escape_href(&mut self.writer, &dest_url)?; |
337 | 0 | if !title.is_empty() { |
338 | 0 | self.write("\" title=\"")?; |
339 | 0 | escape_html(&mut self.writer, &title)?; |
340 | 0 | } |
341 | 0 | self.write("\">") |
342 | | } |
343 | | Tag::Link { |
344 | | link_type: _, |
345 | 0 | dest_url, |
346 | 0 | title, |
347 | 0 | id: _, |
348 | 0 | } => { |
349 | 0 | self.write("<a href=\"")?; |
350 | 0 | escape_href(&mut self.writer, &dest_url)?; |
351 | 0 | if !title.is_empty() { |
352 | 0 | self.write("\" title=\"")?; |
353 | 0 | escape_html(&mut self.writer, &title)?; |
354 | 0 | } |
355 | 0 | self.write("\">") |
356 | | } |
357 | | Tag::Image { |
358 | | link_type: _, |
359 | 0 | dest_url, |
360 | 0 | title, |
361 | 0 | id: _, |
362 | 0 | } => { |
363 | 0 | self.write("<img src=\"")?; |
364 | 0 | escape_href(&mut self.writer, &dest_url)?; |
365 | 0 | self.write("\" alt=\"")?; |
366 | 0 | self.raw_text()?; |
367 | 0 | if !title.is_empty() { |
368 | 0 | self.write("\" title=\"")?; |
369 | 0 | escape_html(&mut self.writer, &title)?; |
370 | 0 | } |
371 | 0 | self.write("\" />") |
372 | | } |
373 | 0 | Tag::FootnoteDefinition(name) => { |
374 | 0 | if self.end_newline { |
375 | 0 | self.write("<div class=\"footnote-definition\" id=\"")?; |
376 | | } else { |
377 | 0 | self.write("\n<div class=\"footnote-definition\" id=\"")?; |
378 | | } |
379 | 0 | escape_html(&mut self.writer, &name)?; |
380 | 0 | self.write("\"><sup class=\"footnote-definition-label\">")?; |
381 | 0 | let len = self.numbers.len() + 1; |
382 | 0 | let number = *self.numbers.entry(name).or_insert(len); |
383 | 0 | write!(&mut self.writer, "{}", number)?; |
384 | 0 | self.write("</sup>") |
385 | | } |
386 | | Tag::MetadataBlock(_) => { |
387 | 0 | self.in_non_writing_block = true; |
388 | 0 | Ok(()) |
389 | | } |
390 | | } |
391 | 0 | } |
392 | | |
393 | 0 | fn end_tag(&mut self, tag: TagEnd) -> Result<(), W::Error> { |
394 | 0 | match tag { |
395 | 0 | TagEnd::HtmlBlock => {} |
396 | | TagEnd::Paragraph => { |
397 | 0 | self.write("</p>\n")?; |
398 | | } |
399 | 0 | TagEnd::Heading(level) => { |
400 | 0 | self.write("</")?; |
401 | 0 | write!(&mut self.writer, "{}", level)?; |
402 | 0 | self.write(">\n")?; |
403 | | } |
404 | | TagEnd::Table => { |
405 | 0 | self.write("</tbody></table>\n")?; |
406 | | } |
407 | | TagEnd::TableHead => { |
408 | 0 | self.write("</tr></thead><tbody>\n")?; |
409 | 0 | self.table_state = TableState::Body; |
410 | | } |
411 | | TagEnd::TableRow => { |
412 | 0 | self.write("</tr>\n")?; |
413 | | } |
414 | | TagEnd::TableCell => { |
415 | 0 | match self.table_state { |
416 | | TableState::Head => { |
417 | 0 | self.write("</th>")?; |
418 | | } |
419 | | TableState::Body => { |
420 | 0 | self.write("</td>")?; |
421 | | } |
422 | | } |
423 | 0 | self.table_cell_index += 1; |
424 | | } |
425 | | TagEnd::BlockQuote(_) => { |
426 | 0 | self.write("</blockquote>\n")?; |
427 | | } |
428 | | TagEnd::CodeBlock => { |
429 | 0 | self.write("</code></pre>\n")?; |
430 | | } |
431 | | TagEnd::List(true) => { |
432 | 0 | self.write("</ol>\n")?; |
433 | | } |
434 | | TagEnd::List(false) => { |
435 | 0 | self.write("</ul>\n")?; |
436 | | } |
437 | | TagEnd::Item => { |
438 | 0 | self.write("</li>\n")?; |
439 | | } |
440 | | TagEnd::DefinitionList => { |
441 | 0 | self.write("</dl>\n")?; |
442 | | } |
443 | | TagEnd::DefinitionListTitle => { |
444 | 0 | self.write("</dt>\n")?; |
445 | | } |
446 | | TagEnd::DefinitionListDefinition => { |
447 | 0 | self.write("</dd>\n")?; |
448 | | } |
449 | | TagEnd::Emphasis => { |
450 | 0 | self.write("</em>")?; |
451 | | } |
452 | | TagEnd::Superscript => { |
453 | 0 | self.write("</sup>")?; |
454 | | } |
455 | | TagEnd::Subscript => { |
456 | 0 | self.write("</sub>")?; |
457 | | } |
458 | | TagEnd::Strong => { |
459 | 0 | self.write("</strong>")?; |
460 | | } |
461 | | TagEnd::Strikethrough => { |
462 | 0 | self.write("</del>")?; |
463 | | } |
464 | | TagEnd::Link => { |
465 | 0 | self.write("</a>")?; |
466 | | } |
467 | 0 | TagEnd::Image => (), // shouldn't happen, handled in start |
468 | | TagEnd::FootnoteDefinition => { |
469 | 0 | self.write("</div>\n")?; |
470 | | } |
471 | 0 | TagEnd::MetadataBlock(_) => { |
472 | 0 | self.in_non_writing_block = false; |
473 | 0 | } |
474 | | } |
475 | 0 | Ok(()) |
476 | 0 | } |
477 | | |
478 | | // run raw text, consuming end tag |
479 | 0 | fn raw_text(&mut self) -> Result<(), W::Error> { |
480 | 0 | let mut nest = 0; |
481 | 0 | while let Some(event) = self.iter.next() { |
482 | 0 | match event { |
483 | 0 | Start(_) => nest += 1, |
484 | | End(_) => { |
485 | 0 | if nest == 0 { |
486 | 0 | break; |
487 | 0 | } |
488 | 0 | nest -= 1; |
489 | | } |
490 | 0 | Html(_) => {} |
491 | 0 | InlineHtml(text) | Code(text) | Text(text) => { |
492 | | // Don't use escape_html_body_text here. |
493 | | // The output of this function is used in the `alt` attribute. |
494 | 0 | escape_html(&mut self.writer, &text)?; |
495 | 0 | self.end_newline = text.ends_with('\n'); |
496 | | } |
497 | 0 | InlineMath(text) => { |
498 | 0 | self.write("$")?; |
499 | 0 | escape_html(&mut self.writer, &text)?; |
500 | 0 | self.write("$")?; |
501 | | } |
502 | 0 | DisplayMath(text) => { |
503 | 0 | self.write("$$")?; |
504 | 0 | escape_html(&mut self.writer, &text)?; |
505 | 0 | self.write("$$")?; |
506 | | } |
507 | | SoftBreak | HardBreak | Rule => { |
508 | 0 | self.write(" ")?; |
509 | | } |
510 | 0 | FootnoteReference(name) => { |
511 | 0 | let len = self.numbers.len() + 1; |
512 | 0 | let number = *self.numbers.entry(name).or_insert(len); |
513 | 0 | write!(&mut self.writer, "[{}]", number)?; |
514 | | } |
515 | 0 | TaskListMarker(true) => self.write("[x]")?, |
516 | 0 | TaskListMarker(false) => self.write("[ ]")?, |
517 | | } |
518 | | } |
519 | 0 | Ok(()) |
520 | 0 | } |
521 | | } |
522 | | |
523 | | /// Iterate over an `Iterator` of `Event`s, generate HTML for each `Event`, and |
524 | | /// push it to a `String`. |
525 | | /// |
526 | | /// # Examples |
527 | | /// |
528 | | /// ``` |
529 | | /// use pulldown_cmark::{html, Parser}; |
530 | | /// |
531 | | /// let markdown_str = r#" |
532 | | /// hello |
533 | | /// ===== |
534 | | /// |
535 | | /// * alpha |
536 | | /// * beta |
537 | | /// "#; |
538 | | /// let parser = Parser::new(markdown_str); |
539 | | /// |
540 | | /// let mut html_buf = String::new(); |
541 | | /// html::push_html(&mut html_buf, parser); |
542 | | /// |
543 | | /// assert_eq!(html_buf, r#"<h1>hello</h1> |
544 | | /// <ul> |
545 | | /// <li>alpha</li> |
546 | | /// <li>beta</li> |
547 | | /// </ul> |
548 | | /// "#); |
549 | | /// ``` |
550 | 0 | pub fn push_html<'a, I>(s: &mut String, iter: I) |
551 | 0 | where |
552 | 0 | I: Iterator<Item = Event<'a>>, |
553 | 0 | { |
554 | 0 | write_html_fmt(s, iter).unwrap() |
555 | 0 | } |
556 | | |
557 | | /// Iterate over an `Iterator` of `Event`s, generate HTML for each `Event`, and |
558 | | /// write it out to an I/O stream. |
559 | | /// |
560 | | /// **Note**: using this function with an unbuffered writer like a file or socket |
561 | | /// will result in poor performance. Wrap these in a |
562 | | /// [`BufWriter`](https://doc.rust-lang.org/std/io/struct.BufWriter.html) to |
563 | | /// prevent unnecessary slowdowns. |
564 | | /// |
565 | | /// # Examples |
566 | | /// |
567 | | /// ``` |
568 | | /// use pulldown_cmark::{html, Parser}; |
569 | | /// use std::io::Cursor; |
570 | | /// |
571 | | /// let markdown_str = r#" |
572 | | /// hello |
573 | | /// ===== |
574 | | /// |
575 | | /// * alpha |
576 | | /// * beta |
577 | | /// "#; |
578 | | /// let mut bytes = Vec::new(); |
579 | | /// let parser = Parser::new(markdown_str); |
580 | | /// |
581 | | /// html::write_html_io(Cursor::new(&mut bytes), parser); |
582 | | /// |
583 | | /// assert_eq!(&String::from_utf8_lossy(&bytes)[..], r#"<h1>hello</h1> |
584 | | /// <ul> |
585 | | /// <li>alpha</li> |
586 | | /// <li>beta</li> |
587 | | /// </ul> |
588 | | /// "#); |
589 | | /// ``` |
590 | 0 | pub fn write_html_io<'a, I, W>(writer: W, iter: I) -> std::io::Result<()> |
591 | 0 | where |
592 | 0 | I: Iterator<Item = Event<'a>>, |
593 | 0 | W: std::io::Write, |
594 | 0 | { |
595 | 0 | HtmlWriter::new(iter, IoWriter(writer)).run() |
596 | 0 | } |
597 | | |
598 | | /// Iterate over an `Iterator` of `Event`s, generate HTML for each `Event`, and |
599 | | /// write it into Unicode-accepting buffer or stream. |
600 | | /// |
601 | | /// # Examples |
602 | | /// |
603 | | /// ``` |
604 | | /// use pulldown_cmark::{html, Parser}; |
605 | | /// |
606 | | /// let markdown_str = r#" |
607 | | /// hello |
608 | | /// ===== |
609 | | /// |
610 | | /// * alpha |
611 | | /// * beta |
612 | | /// "#; |
613 | | /// let mut buf = String::new(); |
614 | | /// let parser = Parser::new(markdown_str); |
615 | | /// |
616 | | /// html::write_html_fmt(&mut buf, parser); |
617 | | /// |
618 | | /// assert_eq!(buf, r#"<h1>hello</h1> |
619 | | /// <ul> |
620 | | /// <li>alpha</li> |
621 | | /// <li>beta</li> |
622 | | /// </ul> |
623 | | /// "#); |
624 | | /// ``` |
625 | 0 | pub fn write_html_fmt<'a, I, W>(writer: W, iter: I) -> std::fmt::Result |
626 | 0 | where |
627 | 0 | I: Iterator<Item = Event<'a>>, |
628 | 0 | W: std::fmt::Write, |
629 | 0 | { |
630 | 0 | HtmlWriter::new(iter, FmtWriter(writer)).run() |
631 | 0 | } |