use std::borrow::Cow;
use std::collections::HashMap;
use std::fmt::Write;
use parse::{Event, Tag};
use parse::Event::{Start, End, Text, Html, InlineHtml, SoftBreak, HardBreak, FootnoteReference};
use escape::{escape_html, escape_href};
struct Ctx<'b, I> {
iter: I,
buf: &'b mut String,
}
impl<'a, 'b, I: Iterator<Item=Event<'a>>> Ctx<'b, I> {
fn fresh_line(&mut self) {
if !(self.buf.is_empty() || self.buf.ends_with('\n')) {
self.buf.push('\n');
}
}
pub fn run(&mut self) {
let mut numbers = HashMap::new();
loop {
match self.iter.next() {
Some(event) => {
match event {
Start(tag) => self.start_tag(tag, &mut numbers),
End(tag) => self.end_tag(tag),
Text(text) => escape_html(self.buf, &text, false),
Html(html) => self.buf.push_str(&html),
InlineHtml(html) => self.buf.push_str(&html),
SoftBreak => self.buf.push('\n'),
HardBreak => self.buf.push_str("<br />\n"),
FootnoteReference(name) => {
let len = numbers.len() + 1;
self.buf.push_str("<sup class=\"footnote-reference\"><a href=\"#");
escape_html(self.buf, &*name, false);
self.buf.push_str("\">");
let number = numbers.entry(name).or_insert(len);
self.buf.push_str(&*format!("{}", number));
self.buf.push_str("</a></sup>");
},
}
}
None => break
}
}
}
fn start_tag(&mut self, tag: Tag<'a>, numbers: &mut HashMap<Cow<'a, str>, usize>) {
match tag {
Tag::Paragraph => {
self.fresh_line();
self.buf.push_str("<p>");
}
Tag::Rule => {
self.fresh_line();
self.buf.push_str("<hr />\n")
}
Tag::Header(level) => {
self.fresh_line();
self.buf.push_str("<h");
self.buf.push((b'0' + level as u8) as char);
self.buf.push('>');
}
Tag::Table(_) => {
self.buf.push_str("<table>");
}
Tag::TableHead => {
self.buf.push_str("<thead><tr>");
}
Tag::TableRow => {
self.buf.push_str("<tr>");
}
Tag::TableCell => {
self.buf.push_str("<td>");
}
Tag::BlockQuote => {
self.fresh_line();
self.buf.push_str("<blockquote>\n");
}
Tag::CodeBlock(info) => {
self.fresh_line();
let lang = info.split(' ').next().unwrap();
if lang.is_empty() {
self.buf.push_str("<pre><code>");
} else {
self.buf.push_str("<pre><code class=\"language-");
escape_html(self.buf, lang, false);
self.buf.push_str("\">");
}
}
Tag::List(Some(1)) => {
self.fresh_line();
self.buf.push_str("<ol>\n");
}
Tag::List(Some(start)) => {
self.fresh_line();
let _ = write!(self.buf, "<ol start=\"{}\">\n", start);
}
Tag::List(None) => {
self.fresh_line();
self.buf.push_str("<ul>\n");
}
Tag::Item => {
self.fresh_line();
self.buf.push_str("<li>");
}
Tag::Emphasis => self.buf.push_str("<em>"),
Tag::Strong => self.buf.push_str("<strong>"),
Tag::Code => self.buf.push_str("<code>"),
Tag::Link(dest, title) => {
self.buf.push_str("<a href=\"");
escape_href(self.buf, &dest);
if !title.is_empty() {
self.buf.push_str("\" title=\"");
escape_html(self.buf, &title, false);
}
self.buf.push_str("\">");
}
Tag::Image(dest, title) => {
self.buf.push_str("<img src=\"");
escape_href(self.buf, &dest);
self.buf.push_str("\" alt=\"");
self.raw_text(numbers);
if !title.is_empty() {
self.buf.push_str("\" title=\"");
escape_html(self.buf, &title, false);
}
self.buf.push_str("\" />")
}
Tag::FootnoteDefinition(name) => {
self.fresh_line();
let len = numbers.len() + 1;
self.buf.push_str("<div class=\"footnote-definition\" id=\"");
escape_html(self.buf, &*name, false);
self.buf.push_str("\"><sup class=\"footnote-definition-label\">");
let number = numbers.entry(name).or_insert(len);
self.buf.push_str(&*format!("{}", number));
self.buf.push_str("</sup>");
}
}
}
fn end_tag<'c>(&mut self, tag: Tag<'c>) {
match tag {
Tag::Paragraph => self.buf.push_str("</p>\n"),
Tag::Rule => (),
Tag::Header(level) => {
self.buf.push_str("</h");
self.buf.push((b'0' + level as u8) as char);
self.buf.push_str(">\n");
}
Tag::Table(_) => {
self.buf.push_str("</table>\n");
}
Tag::TableHead => {
self.buf.push_str("</tr></thead>\n");
}
Tag::TableRow => {
self.buf.push_str("</tr>\n");
}
Tag::TableCell => {
self.buf.push_str("</td>");
}
Tag::BlockQuote => self.buf.push_str("</blockquote>\n"),
Tag::CodeBlock(_) => self.buf.push_str("</code></pre>\n"),
Tag::List(Some(_)) => self.buf.push_str("</ol>\n"),
Tag::List(None) => self.buf.push_str("</ul>\n"),
Tag::Item => self.buf.push_str("</li>\n"),
Tag::Emphasis => self.buf.push_str("</em>"),
Tag::Strong => self.buf.push_str("</strong>"),
Tag::Code => self.buf.push_str("</code>"),
Tag::Link(_, _) => self.buf.push_str("</a>"),
Tag::Image(_, _) => (), Tag::FootnoteDefinition(_) => self.buf.push_str("</div>\n"),
}
}
fn raw_text<'c>(&mut self, numbers: &'c mut HashMap<Cow<'a, str>, usize>) {
let mut nest = 0;
loop {
match self.iter.next() {
Some(event) => {
match event {
Start(_) => nest += 1,
End(_) => {
if nest == 0 { break; }
nest -= 1;
}
Text(text) => escape_html(self.buf, &text, false),
Html(_) => (),
InlineHtml(html) => escape_html(self.buf, &html, false),
SoftBreak | HardBreak => self.buf.push(' '),
FootnoteReference(name) => {
let len = numbers.len() + 1;
let number = numbers.entry(name).or_insert(len);
self.buf.push_str(&*format!("[{}]", number));
}
}
}
None => break
}
}
}
}
pub fn push_html<'a, I: Iterator<Item=Event<'a>>>(buf: &mut String, iter: I) {
let mut ctx = Ctx {
iter: iter,
buf: buf,
};
ctx.run();
}