use crate::htmlrenderer::HtmlRenderer;
use crate::url::Converter as LinkConverter;
use pulldown_cmark::*;
use std::fmt;
use std::io;
use url::{ParseError as UrlParseError, Url};
pub fn to_html<W: escape::StrWrite>(
w: &mut W,
posts_url: &Url,
source_path: &str,
markdown: &str,
footnote_prefix: &str,
) -> Result<(), Error> {
let mut options = Options::empty();
options.insert(Options::ENABLE_FOOTNOTES);
options.insert(Options::ENABLE_SMART_PUNCTUATION);
options.insert(Options::ENABLE_STRIKETHROUGH);
options.insert(Options::ENABLE_TABLES);
options.insert(Options::ENABLE_TASKLISTS);
let event_converter = EventConverter {
link_converter: LinkConverter::new(posts_url, source_path)?,
};
let mut html_renderer =
HtmlRenderer::with_footnote_prefix(footnote_prefix);
for ev in Parser::new_ext(markdown, options)
.map(|ev| event_converter.convert(ev))
{
let ev = ev?;
html_renderer.on_event(w, ev)?;
}
Ok(())
}
struct EventConverter<'a> {
link_converter: LinkConverter<'a>,
}
impl<'a> EventConverter<'a> {
fn convert_tag<'b>(&self, tag: Tag<'b>) -> Result<Tag<'b>, UrlParseError> {
Ok(match tag {
Tag::Heading(s) => Tag::Heading(s + 2),
Tag::Link(
link @ (LinkType::Inline
| LinkType::Reference
| LinkType::ReferenceUnknown
| LinkType::Shortcut
| LinkType::Autolink
| LinkType::Collapsed
| LinkType::CollapsedUnknown),
url,
title,
) => Tag::Link(
link,
CowStr::Boxed(
self.link_converter.convert(&url)?.into_boxed_str(),
),
title,
),
_ => tag,
})
}
fn convert<'b>(&self, ev: Event<'b>) -> Result<Event<'b>, UrlParseError> {
Ok(match ev {
Event::Start(tag) => Event::Start(self.convert_tag(tag)?),
_ => ev,
})
}
}
#[derive(Debug)]
pub enum Error {
Io(std::io::Error),
UrlParse(UrlParseError),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::Io(err) => err.fmt(f),
Error::UrlParse(err) => err.fmt(f),
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Error::Io(err) => Some(err),
Error::UrlParse(err) => Some(err),
}
}
}
impl From<url::ParseError> for Error {
fn from(err: url::ParseError) -> Error {
Error::UrlParse(err)
}
}
impl From<io::Error> for Error {
fn from(err: io::Error) -> Error {
Error::Io(err)
}
}