diff options
Diffstat (limited to 'renderer/src/html.rs')
| -rw-r--r-- | renderer/src/html.rs | 393 |
1 files changed, 393 insertions, 0 deletions
diff --git a/renderer/src/html.rs b/renderer/src/html.rs new file mode 100644 index 0000000..73b994d --- /dev/null +++ b/renderer/src/html.rs @@ -0,0 +1,393 @@ +#[cfg(test)] +pub mod tests; + +use std::io::Write; + +use failure::Error; + +// use crate::url::Url; +use document_tree::{ + Document,Element,HasChildren,ExtraAttributes, + elements as e, + element_categories as c, +}; + + +// static FOOTNOTE_SYMBOLS: [char; 10] = ['*', '†', '‡', '§', '¶', '#', '♠', '♥', '♦', '♣']; + +pub fn render_html<W>(document: &Document, stream: W, standalone: bool) -> Result<(), Error> where W: Write { + let mut renderer = HTMLRenderer { stream, level: 0 }; + if standalone { + document.render_html(&mut renderer) + } else { + for c in document.children() { + (*c).render_html(&mut renderer)?; + writeln!(renderer.stream)?; + } + Ok(()) + } +} + +fn escape_html(text: &str) -> String { + text.replace('&', "&") + .replace('<', "<") + .replace('>', ">") + .replace('"', """) +} + +struct HTMLRenderer<W> where W: Write { + stream: W, + level: u8, +} + +trait HTMLRender { + fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write; +} + +macro_rules! impl_html_render_cat {($cat:ident { $($member:ident),+ }) => { + impl HTMLRender for c::$cat { + fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { + match self {$( + c::$cat::$member(elem) => (**elem).render_html(renderer), + )+} + } + } +}} + +macro_rules! impl_html_render_simple { + ( + $type1:ident => $tag1:ident $( [$($post1:tt)+] )?, + $( $type:ident => $tag:ident $( [$($post:tt)+] )? ),+ + ) => { + impl_html_render_simple!($type1 => $tag1 $([$($post1)+])?); + $( impl_html_render_simple!($type => $tag $([$($post)+])?); )+ + }; + ( $type:ident => $tag:ident ) => { + impl_html_render_simple!($type => $tag[""]); + }; + ( $type:ident => $tag:ident [ $post:expr ] ) => { + impl HTMLRender for e::$type { + fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { + let multiple_children = self.children().len() > 1; + write!(renderer.stream, "<{}>", stringify!($tag))?; + if multiple_children { write!(renderer.stream, $post)?; } + for c in self.children() { + (*c).render_html(renderer)?; + if multiple_children { write!(renderer.stream, $post)?; } + } + write!(renderer.stream, "</{}>", stringify!($tag))?; + Ok(()) + } + } + }; +} + +macro_rules! impl_html_render_simple_nochildren {( $($type:ident => $tag:ident),+ ) => { $( + impl HTMLRender for e::$type { + fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { + write!(renderer.stream, "<{0}></{0}>", stringify!($tag))?; + Ok(()) + } + } +)+ }} + +// Impl + +impl HTMLRender for Document { + fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { + writeln!(renderer.stream, "<!doctype html><html>")?; + for c in self.children() { + (*c).render_html(renderer)?; + writeln!(renderer.stream)?; + } + writeln!(renderer.stream, "</html>")?; + Ok(()) + } +} + +impl_html_render_cat!(StructuralSubElement { Title, Subtitle, Decoration, Docinfo, SubStructure }); +impl_html_render_simple!(Subtitle => h2); + +impl HTMLRender for e::Title { + fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { + let level = if renderer.level > 6 { 6 } else { renderer.level }; + write!(renderer.stream, "<h{0}>", level)?; + for c in self.children() { + (*c).render_html(renderer)?; + } + write!(renderer.stream, "</h{0}>", level)?; + Ok(()) + } +} + +impl HTMLRender for e::Docinfo { + fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { + // Like “YAML frontmatter” in Markdown + unimplemented!(); + } +} + +impl HTMLRender for e::Decoration { + fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { + // Header or footer + unimplemented!(); + } +} + +impl_html_render_cat!(SubStructure { Topic, Sidebar, Transition, Section, BodyElement }); +impl_html_render_simple!(Sidebar => aside); + +impl HTMLRender for e::Section { + fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { + renderer.level += 1; + writeln!(renderer.stream, "<section id=\"{0}\">", self.ids()[0].0)?; + for c in self.children() { + (*c).render_html(renderer)?; + writeln!(renderer.stream)?; + } + write!(renderer.stream, "</section>")?; + Ok(()) + } +} + +impl HTMLRender for e::Transition { + fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { + write!(renderer.stream, "<hr/>")?; + Ok(()) + } +} + +impl HTMLRender for e::Topic { + fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { + // A mini section with title + unimplemented!(); + } +} + +impl_html_render_cat!(BodyElement { Paragraph, LiteralBlock, DoctestBlock, MathBlock, Rubric, SubstitutionDefinition, Comment, Pending, Target, Raw, Image, Compound, Container, BulletList, EnumeratedList, DefinitionList, FieldList, OptionList, LineBlock, BlockQuote, Admonition, Attention, Hint, Note, Caution, Danger, Error, Important, Tip, Warning, Footnote, Citation, SystemMessage, Figure, Table }); +impl_html_render_simple!(Paragraph => p, LiteralBlock => pre, MathBlock => math, Rubric => a, Compound => p, Container => div, BulletList => ul["\n"], EnumeratedList => ol["\n"], DefinitionList => dl["\n"], FieldList => dl["\n"], OptionList => pre, LineBlock => div["\n"], BlockQuote => blockquote, Admonition => aside, Attention => aside, Hint => aside, Note => aside, Caution => aside, Danger => aside, Error => aside, Important => aside, Tip => aside, Warning => aside, Figure => figure); +impl_html_render_simple_nochildren!(Table => table); //TODO: after implementing the table, move it to elems with children + +//impl<I> HTMLRender for I where I: e::Element + a::ExtraAttributes<a::Image> +macro_rules! impl_render_html_image { ($t:ty) => { impl HTMLRender for $t { + fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { + let extra = self.extra(); + if let Some(ref target) = extra.target { + write!(renderer.stream, "<a href=\"{}\">", escape_html(target.as_str()))?; + } + write!(renderer.stream, "<img")?; + if let Some(ref alt) = extra.alt { + write!(renderer.stream, " alt=\"{}\"", escape_html(alt))?; + } + // TODO: align: Option<AlignHV> + // TODO: height: Option<Measure> + // TODO: width: Option<Measure> + // TODO: scale: Option<u8> + write!(renderer.stream, " src=\"{}\" />", escape_html(extra.uri.as_str()))?; + if extra.target.is_some() { + write!(renderer.stream, "</a>")?; + } + Ok(()) + } +}}} +impl_render_html_image!(e::Image); +impl_render_html_image!(e::ImageInline); + +impl HTMLRender for e::DoctestBlock { + fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { + // TODO + unimplemented!(); + } +} + +impl HTMLRender for e::SubstitutionDefinition { + fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { + // TODO: Should those be removed after resolving them + Ok(()) + } +} + +impl HTMLRender for e::Comment { + fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { + write!(renderer.stream, "<!--")?; + for c in self.children() { + (*c).render_html(renderer)?; + } + write!(renderer.stream, "-->")?; + Ok(()) + } +} + +impl HTMLRender for e::Pending { + fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { + // Will those be resolved by the time we get here? + unimplemented!(); + } +} + +impl HTMLRender for e::Target { + fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { + // Should be resolved by now + Ok(()) + } +} + +impl HTMLRender for e::Raw { + fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { + for c in self.children() { + write!(renderer.stream, "{}", c)?; + } + Ok(()) + } +} + +impl HTMLRender for e::Footnote { + fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { + unimplemented!(); + } +} + +impl HTMLRender for e::Citation { + fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { + unimplemented!(); + } +} + +impl HTMLRender for e::SystemMessage { + fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { + write!(renderer.stream, "<figure><caption>System Message</caption>")?; + for c in self.children() { + (*c).render_html(renderer)?; + } + write!(renderer.stream, "</figure>")?; + Ok(()) + } +} + +impl_html_render_cat!(TextOrInlineElement { String, Emphasis, Strong, Literal, Reference, FootnoteReference, CitationReference, SubstitutionReference, TitleReference, Abbreviation, Acronym, Superscript, Subscript, Inline, Problematic, Generated, Math, TargetInline, RawInline, ImageInline }); +impl_html_render_simple!(Emphasis => em, Strong => strong, Literal => code, FootnoteReference => a, CitationReference => a, TitleReference => a, Abbreviation => abbr, Acronym => acronym, Superscript => sup, Subscript => sub, Inline => span, Math => math, TargetInline => a); + +impl HTMLRender for String { + fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { + write!(renderer.stream, "{}", escape_html(self))?; + Ok(()) + } +} + +impl HTMLRender for e::Reference { + fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { + let extra = self.extra(); + write!(renderer.stream, "<a")?; + if let Some(ref target) = extra.refuri { + write!(renderer.stream, " href=\"{}\"", escape_html(target.as_str()))?; + } + /* + if let Some(ref name) = extra.name { + write!(renderer.stream, " title=\"{}\"", escape_html(&name.0))?; + } + */ + write!(renderer.stream, ">")?; + for c in self.children() { + (*c).render_html(renderer)?; + } + write!(renderer.stream, "</a>")?; + Ok(()) + } +} + +impl HTMLRender for e::SubstitutionReference { + fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { + // Will those be resolved by the time we get here? + unimplemented!(); + } +} + +impl HTMLRender for e::Problematic { + fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { + // Broken inline markup leads to insertion of this in docutils + unimplemented!(); + } +} + +impl HTMLRender for e::Generated { + fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { + // Section numbers and so on + unimplemented!(); + } +} + +impl HTMLRender for e::RawInline { + fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { + for c in self.children() { + write!(renderer.stream, "{}", c)?; + } + Ok(()) + } +} + + +//--------------\\ +//Content Models\\ +//--------------\\ + +impl_html_render_cat!(SubTopic { Title, BodyElement }); +impl_html_render_cat!(SubSidebar { Topic, Title, Subtitle, BodyElement }); +impl_html_render_simple!(ListItem => li); + +impl HTMLRender for e::DefinitionListItem { + fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { + // Term→dt, Definition→dd, Classifier→??? + unimplemented!(); + } +} + +impl HTMLRender for e::Field { + fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { + // FieldName→dt, FieldBody→dd + unimplemented!(); + } +} + +impl HTMLRender for e::OptionListItem { + fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { + // OptionGroup→dt(s), Description→dd + unimplemented!(); + } +} + +impl_html_render_cat!(SubLineBlock { LineBlock, Line }); + +impl HTMLRender for e::Line { + fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { + for c in self.children() { + (*c).render_html(renderer)?; + } + write!(renderer.stream, "<br>")?; + Ok(()) + } +} + +impl_html_render_cat!(SubBlockQuote { Attribution, BodyElement }); +impl_html_render_simple!(Attribution => cite); //TODO: correct? + +impl_html_render_cat!(SubFigure { Caption, Legend, BodyElement }); +impl_html_render_simple!(Caption => caption); + +impl HTMLRender for e::Legend { + fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { + unimplemented!(); + } +} + +//------------\\ +//Things to do\\ +//------------\\ + +//TODO: prettyprint option list +//TODO: render admonitions: Admonition, Attention, Hint, Note, Caution, Danger, Error, Important, Tip, Warning +//TODO: properly render tables + +//TODO: add reference target: FootnoteReference, CitationReference, TitleReference +//TODO: add title: Abbr, Acronym +//TODO: convert math, set display attr +//TODO: add id: Rubric, Target, TargetInline |
