rst_renderer-0.4.0/.cargo_vcs_info.json0000644000000001121374755472100136020ustar { "git": { "sha1": "33e0418e0a23d0b592104d657cafa0978375cfa6" } } rst_renderer-0.4.0/Cargo.toml0000644000000021501374755472100116040ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] edition = "2018" name = "rst_renderer" version = "0.4.0" authors = ["Philipp A. "] description = "a reStructuredText renderer" homepage = "https://github.com/flying-sheep/rust-rst" documentation = "https://docs.rs/rst_renderer" readme = "README.md" license = "MIT OR Apache-2.0" repository = "https://github.com/flying-sheep/rust-rst" [dependencies.document_tree] version = "0.4.0" [dependencies.failure] version = "0.1.6" [dependencies.serde-xml-rs] version = "0.3.1" [dependencies.serde_json] version = "1.0.44" [dev-dependencies.pretty_assertions] version = "0.6.1" rst_renderer-0.4.0/Cargo.toml.orig010064400017500001750000000011251374755430000152700ustar 00000000000000[package] name = 'rst_renderer' version = '0.4.0' authors = ['Philipp A. '] edition = '2018' description = 'a reStructuredText renderer' license = 'MIT OR Apache-2.0' readme = 'README.md' documentation = 'https://docs.rs/rst_renderer' homepage = 'https://github.com/flying-sheep/rust-rst' repository = 'https://github.com/flying-sheep/rust-rst' [dependencies] document_tree = { path = '../document_tree', version = '0.4.0' } failure = '0.1.6' serde_json = '1.0.44' serde-xml-rs = '0.3.1' [dev-dependencies] rst_parser = { path = '../parser' } pretty_assertions = '0.6.1' rst_renderer-0.4.0/README.md010064400017500001750000000010411360542441600136520ustar 00000000000000`rst_renderer` ============== Part of the [`rst`][rst] crate family. This crate contains the HTML renderer (which supports most of what the parser supports), as well as the broken XML and JSON renderers. Suggestions and PRs welcome on how to get them right! ```rust let document = Document::with_children(vec![...]); // or rst_parser::parse() let stream = std::io::stdout(); let standalone = true; // wrap in render_html(document, stream, standalone); ``` [rst]: https://github.com/flying-sheep/rust-rst/#readme rst_renderer-0.4.0/src/html/tests.rs010064400017500001750000000137231374755412300156560ustar 00000000000000use pretty_assertions::assert_eq; use rst_parser::parse; use crate::html::render_html; fn check_renders_to(rst: &str, expected: &str) { println!("Rendering:\n{}\n---", rst); let doc = parse(rst).expect("Cannot parse"); let mut result_data: Vec = vec![]; render_html(&doc, &mut result_data, false).expect("Render error"); let result = String::from_utf8(result_data).expect("Could not decode"); assert_eq!(result.as_str().trim(), expected); println!("{}", expected); } #[test] fn simple_string() { check_renders_to( "Simple String", "

Simple String

", ); } #[test] fn simple_string_with_markup() { check_renders_to( "Simple String with *emph* and **strong**", "

Simple String with emph and strong

", ); } #[test] fn inline_literal() { check_renders_to( "Simple String with an even simpler ``inline literal``", "

Simple String with an even simpler inline literal

", ); } /* #[test] fn test_reference_anonymous() { check_renders_to("\ A simple `anonymous reference`__ __ http://www.test.com/test_url ", "\

A simple anonymous reference

\ "); } */ #[test] fn two_paragraphs() { check_renders_to( "One paragraph.\n\nTwo paragraphs.", "

One paragraph.

\n

Two paragraphs.

", ); } #[test] fn named_reference() { check_renders_to("\ A simple `named reference`_ with stuff in between the reference and the target. .. _`named reference`: http://www.test.com/test_url ", "\

A simple named reference with stuff in between the \ reference and the target.

\ "); } #[test] fn standalone_hyperlinks() { check_renders_to("\ Some http://url and a not_url_scheme:foo that is not supposed to be an url. ", "\

Some http://url and a not_url_scheme:foo that is not supposed to be an url.

\ "); } #[test] fn substitution() { check_renders_to("\ A |subst|. .. |subst| replace:: text substitution ", "

A text substitution.

"); } #[test] fn not_substitution_literal() { check_renders_to("\ hello ``foo.map(|world| world + 42)`` .. |world| replace:: something different .. code:: foo.map(|world| world + 42) :: hay! |x| ", "

hello foo.map(|world| world + 42)

foo.map(|world| world + 42)\n
hay! |x|\n
"); } /* #[test] fn test_section_hierarchy() { check_renders_to("\ +++++ Title +++++ Subtitle ======== Some stuff Section ------- Some more stuff Another Section ............... And even more stuff ", "\

Some stuff

Section

Some more stuff

Another Section

And even more stuff

\ "); } #[test] fn test_docinfo_title() { check_renders_to("\ +++++ Title +++++ :author: me Some stuff ", "\

Title

Author

me

Some stuff

\ "); } */ #[test] fn section_hierarchy() { check_renders_to("\ +++++ Title +++++ Not A Subtitle ============== Some stuff Section ------- Some more stuff Another Section ............... And even more stuff ", "\

Title

Not A Subtitle

Some stuff

Section

Some more stuff

Another Section

And even more stuff

\ "); } #[test] fn many_sections() { check_renders_to("\ +++++++++ heading 1 +++++++++ heading 2 ========= First stuff heading 2a ---------- First detail heading 3 ========= Second stuff heading 3a ---------- Second detail ", "\

heading 1

heading 2

First stuff

heading 2a

First detail

heading 3

Second stuff

heading 3a

Second detail

\ "); } #[test] fn bullet_list() { check_renders_to("\ * bullet * list ", "\
  • bullet

  • list

\ "); } /* #[test] fn test_table() { check_renders_to("\ .. table:: :align: right +-----+-----+ | 1 | 2 | +-----+-----+ | 3 | 4 | +-----+-----+ ", "\

1

2

3

4

\ "); } */ #[test] fn code() { check_renders_to("\ .. code:: python def foo(): print('Hi!') # comment ", "\
def foo():
    print('Hi!')

    # comment
\ "); } #[test] fn raw_html() { check_renders_to("\ .. raw:: html hello world

paragraph paragraph

after .. raw:: something_else into a black hole this goes ", "\ hello world

paragraph paragraph

after

\ "); } #[test] fn comments() { check_renders_to("\ .. Run-in comment .. block-like with blank lines ", "\ \ "); } /* #[test] fn test_field_list() { check_renders_to("\ Not a docinfo. :This: .. _target: is :a: :simple: :field: list ", "\

Not a docinfo.

This

is

a

simple

field

list

\ "); } */ /* #[test] fn test_field_list_long() { check_renders_to("\ Not a docinfo. :This is: a :simple field list with loooong field: names ", "\

Not a docinfo.

This is

a

simple field list with loooong field

names

\ "); } */ rst_renderer-0.4.0/src/html.rs010064400017500001750000000324631374755412300145160ustar 00000000000000#[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, extra_attributes as a, element_categories as c, attribute_types as at, }; // static FOOTNOTE_SYMBOLS: [char; 10] = ['*', '†', '‡', '§', '¶', '#', '♠', '♥', '♦', '♣']; pub fn render_html(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 where W: Write { stream: W, level: u8, } trait HTMLRender { fn render_html(&self, renderer: &mut HTMLRenderer) -> Result<(), Error> where W: Write; } macro_rules! impl_html_render_cat {($cat:ident { $($member:ident),+ }) => { impl HTMLRender for c::$cat { fn render_html(&self, renderer: &mut HTMLRenderer) -> 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(&self, renderer: &mut HTMLRenderer) -> Result<(), Error> where W: Write { let multiple_children = self.children().len() > 1; write!(renderer.stream, "<{}", stringify!($tag))?; if self.classes().len() > 0 { write!(renderer.stream, " class=\"{}\"", self.classes().join(" "))?; } write!(renderer.stream, ">")?; 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(&self, renderer: &mut HTMLRenderer) -> Result<(), Error> where W: Write { write!(renderer.stream, "<{0}>", stringify!($tag))?; Ok(()) } } )+ }} // Impl impl HTMLRender for Document { fn render_html(&self, renderer: &mut HTMLRenderer) -> Result<(), Error> where W: Write { writeln!(renderer.stream, "")?; for c in self.children() { (*c).render_html(renderer)?; writeln!(renderer.stream)?; } writeln!(renderer.stream, "")?; 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(&self, renderer: &mut HTMLRenderer) -> Result<(), Error> where W: Write { let level = if renderer.level > 6 { 6 } else { renderer.level }; write!(renderer.stream, "", level)?; for c in self.children() { (*c).render_html(renderer)?; } write!(renderer.stream, "", level)?; Ok(()) } } impl HTMLRender for e::Docinfo { fn render_html(&self, _renderer: &mut HTMLRenderer) -> Result<(), Error> where W: Write { // Like “YAML frontmatter” in Markdown unimplemented!(); } } impl HTMLRender for e::Decoration { fn render_html(&self, _renderer: &mut HTMLRenderer) -> 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(&self, renderer: &mut HTMLRenderer) -> Result<(), Error> where W: Write { renderer.level += 1; writeln!(renderer.stream, "
", self.ids()[0].0)?; for c in self.children() { (*c).render_html(renderer)?; writeln!(renderer.stream)?; } write!(renderer.stream, "
")?; renderer.level -= 1; Ok(()) } } impl HTMLRender for e::Transition { fn render_html(&self, renderer: &mut HTMLRenderer) -> Result<(), Error> where W: Write { write!(renderer.stream, "
")?; Ok(()) } } impl HTMLRender for e::Topic { fn render_html(&self, _renderer: &mut HTMLRenderer) -> 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, 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 // circumvent E0119 trait IMark {} impl IMark for e::Image {} impl IMark for e::ImageInline {} impl HTMLRender for I where I: e::Element + a::ExtraAttributes + IMark { fn render_html(&self, renderer: &mut HTMLRenderer) -> Result<(), Error> where W: Write { let extra = self.extra(); if let Some(ref target) = extra.target { write!(renderer.stream, "", escape_html(target.as_str()))?; } write!(renderer.stream, " // TODO: height: Option // TODO: width: Option // TODO: scale: Option write!(renderer.stream, " src=\"{}\" />", escape_html(extra.uri.as_str()))?; if extra.target.is_some() { write!(renderer.stream, "")?; } Ok(()) } } impl HTMLRender for e::LiteralBlock { fn render_html(&self, renderer: &mut HTMLRenderer) -> Result<(), Error> where W: Write { let mut cls_iter = self.classes().iter(); let is_code = cls_iter.next() == Some(&"code".to_owned()); write!(renderer.stream, "
")?;
		if is_code {  // TODO: support those classes not being at the start
			if let Some(lang) = cls_iter.next() {
				write!(renderer.stream, "", lang)?;
			} else {
				write!(renderer.stream, "")?;
			}
		}
		for c in self.children() {
			(*c).render_html(renderer)?;
		}
		if is_code { write!(renderer.stream, "")?; }
		write!(renderer.stream, "
")?; Ok(()) } } impl HTMLRender for e::DoctestBlock { fn render_html(&self, _renderer: &mut HTMLRenderer) -> Result<(), Error> where W: Write { // TODO unimplemented!(); } } impl HTMLRender for e::SubstitutionDefinition { fn render_html(&self, _renderer: &mut HTMLRenderer) -> Result<(), Error> where W: Write { // TODO: Should those be removed after resolving them Ok(()) } } impl HTMLRender for e::Comment { fn render_html(&self, renderer: &mut HTMLRenderer) -> Result<(), Error> where W: Write { write!(renderer.stream, "")?; Ok(()) } } impl HTMLRender for e::Pending { fn render_html(&self, _renderer: &mut HTMLRenderer) -> Result<(), Error> where W: Write { // Will those be resolved by the time we get here? unimplemented!(); } } impl HTMLRender for e::Target { fn render_html(&self, _renderer: &mut HTMLRenderer) -> Result<(), Error> where W: Write { // Should be resolved by now Ok(()) } } impl HTMLRender for e::Raw { fn render_html(&self, renderer: &mut HTMLRenderer) -> Result<(), Error> where W: Write { let extra = self.extra(); if extra.format.contains(&at::NameToken("html".to_owned())) { for c in self.children() { write!(renderer.stream, "{}", c)?; } } Ok(()) } } impl HTMLRender for e::Footnote { fn render_html(&self, _renderer: &mut HTMLRenderer) -> Result<(), Error> where W: Write { unimplemented!(); } } impl HTMLRender for e::Citation { fn render_html(&self, _renderer: &mut HTMLRenderer) -> Result<(), Error> where W: Write { unimplemented!(); } } impl HTMLRender for e::SystemMessage { fn render_html(&self, renderer: &mut HTMLRenderer) -> Result<(), Error> where W: Write { write!(renderer.stream, "
System Message")?; for c in self.children() { (*c).render_html(renderer)?; } write!(renderer.stream, "
")?; 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(&self, renderer: &mut HTMLRenderer) -> Result<(), Error> where W: Write { write!(renderer.stream, "{}", escape_html(self))?; Ok(()) } } impl HTMLRender for e::Reference { fn render_html(&self, renderer: &mut HTMLRenderer) -> Result<(), Error> where W: Write { let extra = self.extra(); write!(renderer.stream, "")?; for c in self.children() { (*c).render_html(renderer)?; } write!(renderer.stream, "")?; Ok(()) } } impl HTMLRender for e::SubstitutionReference { fn render_html(&self, _renderer: &mut HTMLRenderer) -> Result<(), Error> where W: Write { // Will those be resolved by the time we get here? unimplemented!(); } } impl HTMLRender for e::Problematic { fn render_html(&self, _renderer: &mut HTMLRenderer) -> Result<(), Error> where W: Write { // Broken inline markup leads to insertion of this in docutils unimplemented!(); } } impl HTMLRender for e::Generated { fn render_html(&self, _renderer: &mut HTMLRenderer) -> Result<(), Error> where W: Write { // Section numbers and so on unimplemented!(); } } impl HTMLRender for e::RawInline { fn render_html(&self, renderer: &mut HTMLRenderer) -> 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(&self, _renderer: &mut HTMLRenderer) -> Result<(), Error> where W: Write { // Term→dt, Definition→dd, Classifier→??? unimplemented!(); } } impl HTMLRender for e::Field { fn render_html(&self, _renderer: &mut HTMLRenderer) -> Result<(), Error> where W: Write { // FieldName→dt, FieldBody→dd unimplemented!(); } } impl HTMLRender for e::OptionListItem { fn render_html(&self, _renderer: &mut HTMLRenderer) -> 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(&self, renderer: &mut HTMLRenderer) -> Result<(), Error> where W: Write { for c in self.children() { (*c).render_html(renderer)?; } write!(renderer.stream, "
")?; 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(&self, _renderer: &mut HTMLRenderer) -> 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 rst_renderer-0.4.0/src/lib.rs010064400017500001750000000006721374725432400143150ustar 00000000000000mod html; use std::io::Write; use failure::Error; use document_tree::Document; pub fn render_json(document: &Document, stream: W) -> Result<(), Error> where W: Write { serde_json::to_writer(stream, &document)?; Ok(()) } pub fn render_xml(document: &Document, stream: W) -> Result<(), Error> where W: Write { serde_xml_rs::to_writer(stream, &document).map_err(failure::SyncFailure::new)?; Ok(()) } pub use html::render_html;