rst_renderer-0.4.2/.cargo_vcs_info.json0000644000000001460000000000100135360ustar { "git": { "sha1": "a59c330d2109e823b0c9ac791e5216951987711d" }, "path_in_vcs": "renderer" }rst_renderer-0.4.2/CHANGELOG.md000064400000000000000000000024241046102023000141400ustar 00000000000000# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] ## [0.4.2](https://github.com/flying-sheep/rust-rst/compare/rst_renderer-v0.4.1...rst_renderer-v0.4.2) - 2025-04-13 ### Other - Footnote refs ([#67](https://github.com/flying-sheep/rust-rst/pull/67)) - Footnotes ([#65](https://github.com/flying-sheep/rust-rst/pull/65)) - clippy ([#66](https://github.com/flying-sheep/rust-rst/pull/66)) - bump edition again ([#64](https://github.com/flying-sheep/rust-rst/pull/64)) - Clippy ([#62](https://github.com/flying-sheep/rust-rst/pull/62)) ## [0.4.1](https://github.com/flying-sheep/rust-rst/compare/rst_renderer-v0.4.0...rst_renderer-v0.4.1) - 2024-11-20 ### Other - Bump deps and refresh token ([#59](https://github.com/flying-sheep/rust-rst/pull/59)) - Bump serde-xml-rs to 0.5 ([#51](https://github.com/flying-sheep/rust-rst/pull/51)) - Migrate from quicli/failure to plain clap/anyhow ([#50](https://github.com/flying-sheep/rust-rst/pull/50)) - Format ([#38](https://github.com/flying-sheep/rust-rst/pull/38)) - Add CI ([#36](https://github.com/flying-sheep/rust-rst/pull/36)) rst_renderer-0.4.2/Cargo.lock0000644000000317070000000000100115200ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "aho-corasick" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "anyhow" version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" [[package]] name = "diff" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" [[package]] name = "displaydoc" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "document_tree" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6742722dd3e6cd908bc522283cb5502e25f696d1c9904fb251ec266b6b3f9cce" dependencies = [ "anyhow", "regex", "serde", "serde_derive", "url", ] [[package]] name = "form_urlencoded" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] [[package]] name = "icu_collections" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" dependencies = [ "displaydoc", "yoke", "zerofrom", "zerovec", ] [[package]] name = "icu_locid" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" dependencies = [ "displaydoc", "litemap", "tinystr", "writeable", "zerovec", ] [[package]] name = "icu_locid_transform" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" dependencies = [ "displaydoc", "icu_locid", "icu_locid_transform_data", "icu_provider", "tinystr", "zerovec", ] [[package]] name = "icu_locid_transform_data" version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" [[package]] name = "icu_normalizer" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" dependencies = [ "displaydoc", "icu_collections", "icu_normalizer_data", "icu_properties", "icu_provider", "smallvec", "utf16_iter", "utf8_iter", "write16", "zerovec", ] [[package]] name = "icu_normalizer_data" version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" [[package]] name = "icu_properties" version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" dependencies = [ "displaydoc", "icu_collections", "icu_locid_transform", "icu_properties_data", "icu_provider", "tinystr", "zerovec", ] [[package]] name = "icu_properties_data" version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" [[package]] name = "icu_provider" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" dependencies = [ "displaydoc", "icu_locid", "icu_provider_macros", "stable_deref_trait", "tinystr", "writeable", "yoke", "zerofrom", "zerovec", ] [[package]] name = "icu_provider_macros" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "idna" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" dependencies = [ "idna_adapter", "smallvec", "utf8_iter", ] [[package]] name = "idna_adapter" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" dependencies = [ "icu_normalizer", "icu_properties", ] [[package]] name = "itoa" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "litemap" version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" [[package]] name = "log" version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "percent-encoding" version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pretty_assertions" version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" dependencies = [ "diff", "yansi", ] [[package]] name = "proc-macro2" version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] [[package]] name = "regex" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "rst_renderer" version = "0.4.2" dependencies = [ "anyhow", "document_tree", "pretty_assertions", "serde-xml-rs", "serde_json", ] [[package]] name = "ryu" version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "serde" version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] [[package]] name = "serde-xml-rs" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65162e9059be2f6a3421ebbb4fef3e74b7d9e7c60c50a0e292c6239f19f1edfa" dependencies = [ "log", "serde", "thiserror", "xml-rs", ] [[package]] name = "serde_derive" version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_json" version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ "itoa", "memchr", "ryu", "serde", ] [[package]] name = "smallvec" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" [[package]] name = "stable_deref_trait" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "syn" version = "2.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "synstructure" version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "thiserror" version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "tinystr" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" dependencies = [ "displaydoc", "zerovec", ] [[package]] name = "unicode-ident" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "url" version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", "idna", "percent-encoding", ] [[package]] name = "utf16_iter" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" [[package]] name = "utf8_iter" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "write16" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" [[package]] name = "writeable" version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" [[package]] name = "xml-rs" version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a62ce76d9b56901b19a74f19431b0d8b3bc7ca4ad685a746dfd78ca8f4fc6bda" [[package]] name = "yansi" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" [[package]] name = "yoke" version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" dependencies = [ "serde", "stable_deref_trait", "yoke-derive", "zerofrom", ] [[package]] name = "yoke-derive" version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", "syn", "synstructure", ] [[package]] name = "zerofrom" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", "syn", "synstructure", ] [[package]] name = "zerovec" version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" dependencies = [ "yoke", "zerofrom", "zerovec-derive", ] [[package]] name = "zerovec-derive" version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", "syn", ] rst_renderer-0.4.2/Cargo.toml0000644000000023300000000000100115310ustar # 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 are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2024" name = "rst_renderer" version = "0.4.2" authors = ["Philipp A. "] build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false 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" resolver = "2" [lib] name = "rst_renderer" path = "src/lib.rs" [dependencies.anyhow] version = "1.0.86" [dependencies.document_tree] version = "0.4.2" [dependencies.serde-xml-rs] version = "0.5" [dependencies.serde_json] version = "1.0.44" [dev-dependencies.pretty_assertions] version = "1.4.1" rst_renderer-0.4.2/Cargo.toml.orig000064400000000000000000000011231046102023000152110ustar 00000000000000[package] name = 'rst_renderer' version = "0.4.2" authors = ['Philipp A. '] edition = '2024' 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.2" } anyhow = '1.0.86' serde_json = '1.0.44' serde-xml-rs = '0.5' [dev-dependencies] rst_parser = { path = '../parser' } pretty_assertions = '1.4.1' rst_renderer-0.4.2/README.md000064400000000000000000000010411046102023000136000ustar 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.2/src/html/elems_cats.rs000064400000000000000000000375521046102023000165610ustar 00000000000000use std::io::Write; use anyhow::{Error, bail}; // use crate::url::Url; use super::{HTMLRender, HTMLRenderer, escape_html, footnote_symbol}; use document_tree::{ Element, ExtraAttributes, HasChildren, LabelledFootnote as _, attribute_types as at, element_categories as c, elements as e, extra_attributes::{self as a, FootnoteType}, }; 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, $( $type:ident => $tag:ident ),+ ) => { impl_html_render_simple!($type1 => $tag1); $( impl_html_render_simple!($type => $tag); )+ }; ( $type:ident => $tag:ident ) => { impl HTMLRender for e::$type { fn render_html(&self, renderer: &mut HTMLRenderer) -> Result<(), Error> where W: Write { write!(renderer.stream, "<{}", stringify!($tag))?; if self.classes().len() > 0 { write!(renderer.stream, " class=\"{}\"", self.classes().join(" "))?; } write!(renderer.stream, ">")?; self.children().render_html(renderer)?; 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_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, "")?; self.children().render_html(renderer)?; write!(renderer.stream, "")?; 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; write!(renderer.stream, "
", self.ids()[0].0)?; self.children().render_html(renderer)?; 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, EnumeratedList => ol, DefinitionList => dl, FieldList => dl, OptionList => pre, LineBlock => div, 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(target) = extra.target.as_ref() { 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, "")?;
            } else {
                write!(renderer.stream, "")?;
            }
        }
        self.children().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, { use c::SubFootnote::BodyElement; // open
  • let id = self.ids().first().unwrap().0.as_str(); let mut children = self.children().iter(); write!(renderer.stream, "
  • [")?; // TODO: render

    here instead // render backrefs for refid in &self.extra().backrefs { let label = if self.is_symbol() { footnote_symbol(n).to_string() } else { label.to_string() }; write!( renderer.stream, "{1}", refid.0.as_str(), label // TODO: differentiate? make independent from `label`? )?; } } else { write!(renderer.stream, ">")?; } write!(renderer.stream, "]")?; // render children for child in children { let BodyElement(child) = child else { bail!("Cannot have a footnote label anywhere but as first child node"); }; child.render_html(renderer)?; } // close

  • write!(renderer.stream, "
  • ")?; Ok(()) } } 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")?; self.children().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, 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, "")?; self.children().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::FootnoteReference { fn render_html(&self, renderer: &mut HTMLRenderer) -> Result<(), Error> where W: Write, { // open tag write!( renderer.stream, "")?; // render label if self.is_symbol() { let n: usize = self.get_label().unwrap().parse().unwrap(); // TODO: handle duplication as CSS “symbolic” counters do let sym = footnote_symbol(n); write!(renderer.stream, "{sym}")?; } else { self.children().render_html(renderer)?; } // close tag write!(renderer.stream, "")?; Ok(()) } } 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, { self.children().render_html(renderer) } } //--------------\\ //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, { self.children().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!(); } } rst_renderer-0.4.2/src/html/multi.rs000064400000000000000000000065141046102023000155660ustar 00000000000000use std::{borrow::Borrow, io::Write}; use anyhow::Error; use super::{HTMLRender, HTMLRenderer}; use document_tree::{element_categories as c, elements as e}; macro_rules! impl_html_render_multi { ( $type1:path $( [$($post1:tt)+] )?, $( $type:path $( [$($post:tt)+] )? ),+ ) => { impl_html_render_multi!($type1 $([$($post1)+])?); $( impl_html_render_multi!($type $([$($post)+])?); )+ }; ( $type:path ) => { impl_html_render_multi!($type[""]); }; ( $type:path [ $post:expr ] ) => { impl HTMLRender for [&$type] { fn render_html(&self, renderer: &mut HTMLRenderer) -> Result<(), Error> { write_optional_newlines::<$type, _, _>(renderer, self, $post) } } impl HTMLRender for [$type] { fn render_html(&self, renderer: &mut HTMLRenderer) -> Result<(), Error> { write_optional_newlines::<$type, _, _>(renderer, self, $post) } } }; } fn write_optional_newlines( renderer: &mut HTMLRenderer, elems: &[E], post: &str, ) -> Result<(), Error> where R: HTMLRender, E: Borrow, W: Write, { let many = elems.len() > 1; if many { write!(renderer.stream, "{post}")?; } for c in elems { c.borrow().render_html(renderer)?; if many { write!(renderer.stream, "{post}")?; } } Ok(()) } macro_rules! impl_html_render_multi_body { ( $type1:path, $( $type:path ),+ ) => { impl_html_render_multi_body!($type1); $( impl_html_render_multi_body!($type); )+ }; ( $type:path ) => { impl HTMLRender for [$type] { fn render_html(&self, renderer: &mut HTMLRenderer) -> Result<(), Error> where W: Write, { let many = self.len() > 1; if many { writeln!(renderer.stream)?; } let mut footnotes: Vec<&e::Footnote> = vec![]; for c in self { if let Ok(&c::BodyElement::Footnote(ref f)) = c.try_into() { footnotes.push(f.as_ref()); continue; } write_footnotes(renderer, &footnotes)?; c.render_html(renderer)?; if many { writeln!(renderer.stream)?; } } write_footnotes(renderer, &footnotes)?; Ok(()) } } }; } fn write_footnotes( renderer: &mut HTMLRenderer, footnotes: &[&e::Footnote], ) -> Result<(), Error> where W: Write, { if footnotes.is_empty() { return Ok(()); } writeln!(renderer.stream, "
      ")?; for f in footnotes { f.render_html(renderer)?; writeln!(renderer.stream)?; } writeln!(renderer.stream, "
    ")?; Ok(()) } // Impl impl_html_render_multi!( c::TextOrInlineElement, c::SubSidebar, c::SubLineBlock, c::SubBlockQuote, c::SubTopic, c::SubFigure, e::ListItem["\n"], e::DefinitionListItem, e::Field, e::OptionListItem, e::Footnote["\n"], String ); impl_html_render_multi_body!(c::StructuralSubElement, c::SubStructure, c::BodyElement); rst_renderer-0.4.2/src/html/tests.rs000064400000000000000000000173551046102023000156030ustar 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{rst}\n---"); 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 footnote() { // TODO: reference resolution // TODO: auto-numbering over separate blocks check_renders_to( "\ Footnotes 1: [#]_, 3: [#]_, 33: [33]_, \\*: [*]_, 2: [#named]_, 4: [#]_. .. [#] Footnote *1* .. [33] Footnote *33* More .. [#named] Footnote *2* .. [#] Footnote *3* .. [*] Symbol .. [#] Footnote *4* ", "\

    Footnotes \ 1: 1, \ 3: 3, \ 33: 33, \ *: *, \ 2: 2, \ 4: 4.

    1. [1]

      Footnote 1

    2. [33]

      Footnote 33

      More

    3. [2]

      Footnote 2

    4. [3]

      Footnote 3

    5. [*]

      Symbol

    6. [4]

      Footnote 4

    \ ", ); } /* #[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.2/src/html.rs000064400000000000000000000044751046102023000144400ustar 00000000000000mod elems_cats; mod multi; #[cfg(test)] pub mod tests; use std::io::Write; use anyhow::Error; // use crate::url::Url; use document_tree::{Document, HasChildren}; /// Render document as HTML /// /// # Errors /// Returns error if serialization fails 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 { document.children().render_html(&mut renderer) } } 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; } pub const FOOTNOTE_SYMBOLS: [char; 10] = ['*', '†', '‡', '§', '¶', '#', '♠', '♥', '♦', '♣']; pub fn footnote_symbol(n: usize) -> String { FOOTNOTE_SYMBOLS .iter() .cycle() .nth(n - 1) .unwrap() .to_string() } const HEAD: &str = r#" "#; impl HTMLRender for Document { fn render_html(&self, renderer: &mut HTMLRenderer) -> Result<(), Error> where W: Write, { writeln!(renderer.stream, "\n\n{HEAD}\n")?; self.children().render_html(renderer)?; writeln!(renderer.stream, "\n")?; Ok(()) } } //------------\\ //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.2/src/lib.rs000064400000000000000000000013231046102023000142270ustar 00000000000000#![warn(clippy::pedantic)] mod html; use std::io::Write; use anyhow::{Error, anyhow}; use document_tree::Document; /// Render a document tree as JSON. /// /// # Errors /// Returns an error if serialization fails. pub fn render_json(document: &Document, stream: W) -> Result<(), Error> where W: Write, { serde_json::to_writer(stream, &document)?; Ok(()) } /// Render a document tree as XML. /// /// # Errors /// Returns an error if serialization fails. pub fn render_xml(document: &Document, stream: W) -> Result<(), Error> where W: Write, { serde_xml_rs::to_writer(stream, &document) .map_err(|e| anyhow!("Failed to serialize XML: {}", e))?; Ok(()) } pub use html::render_html;