rst_parser-0.4.2/.cargo_vcs_info.json0000644000000001440000000000100132220ustar { "git": { "sha1": "a59c330d2109e823b0c9ac791e5216951987711d" }, "path_in_vcs": "parser" }rst_parser-0.4.2/CHANGELOG.md000064400000000000000000000030211046102023000136200ustar 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_parser-v0.4.1...rst_parser-v0.4.2) - 2025-04-13 ### Other - Footnote refs ([#67](https://github.com/flying-sheep/rust-rst/pull/67)) - Visitor ([#68](https://github.com/flying-sheep/rust-rst/pull/68)) - Footnotes ([#65](https://github.com/flying-sheep/rust-rst/pull/65)) - clippy ([#66](https://github.com/flying-sheep/rust-rst/pull/66)) - Support block quote ([#61](https://github.com/flying-sheep/rust-rst/pull/61)) - 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_parser-v0.4.0...rst_parser-v0.4.1) - 2024-11-20 ### Other - Handle images with multiple options ([#53](https://github.com/flying-sheep/rust-rst/pull/53)) - Fix parsing of percentage in image scale attributes ([#54](https://github.com/flying-sheep/rust-rst/pull/54)) - Migrate from quicli/failure to plain clap/anyhow ([#50](https://github.com/flying-sheep/rust-rst/pull/50)) - Trailing newlines ([#31](https://github.com/flying-sheep/rust-rst/pull/31)) - Format ([#38](https://github.com/flying-sheep/rust-rst/pull/38)) - Add CI ([#36](https://github.com/flying-sheep/rust-rst/pull/36)) rst_parser-0.4.2/Cargo.lock0000644000000351360000000000100112060ustar # 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 = "block-buffer" version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ "generic-array", ] [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cpufeatures" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" dependencies = [ "libc", ] [[package]] name = "crypto-common" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", "typenum", ] [[package]] name = "digest" version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", ] [[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 = "generic-array" version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", ] [[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 = "libc" version = "0.2.171" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" [[package]] name = "litemap" version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "once_cell" version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "percent-encoding" version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "198db74531d58c70a361c42201efde7e2591e976d518caf7662a47dc5720e7b6" dependencies = [ "memchr", "thiserror", "ucd-trie", ] [[package]] name = "pest_derive" version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d725d9cfd79e87dccc9341a2ef39d1b6f6353d68c4b33c177febbe1a402c97c5" dependencies = [ "pest", "pest_generator", ] [[package]] name = "pest_generator" version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db7d01726be8ab66ab32f9df467ae8b1148906685bbe75c82d1e65d7f5b3f841" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", "syn", ] [[package]] name = "pest_meta" version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f9f832470494906d1fca5329f8ab5791cc60beb230c74815dff541cbd2b5ca0" dependencies = [ "once_cell", "pest", "sha2", ] [[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_parser" version = "0.4.2" dependencies = [ "anyhow", "document_tree", "pest", "pest_derive", ] [[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_derive" version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "sha2" version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", "cpufeatures", "digest", ] [[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 = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" 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 = "typenum" version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] name = "ucd-trie" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" [[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 = "version_check" version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[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 = "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_parser-0.4.2/Cargo.toml0000644000000022220000000000100112170ustar # 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_parser" version = "0.4.2" authors = ["Philipp A. "] build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "a reStructuredText parser" homepage = "https://github.com/flying-sheep/rust-rst" documentation = "https://docs.rs/rst_parser" readme = "README.md" license = "MIT OR Apache-2.0" repository = "https://github.com/flying-sheep/rust-rst" resolver = "2" [lib] name = "rst_parser" path = "src/lib.rs" [dependencies.anyhow] version = "1.0.86" [dependencies.document_tree] version = "0.4.2" [dependencies.pest] version = "2.1.2" [dependencies.pest_derive] version = "2.1.0" rst_parser-0.4.2/Cargo.toml.orig000064400000000000000000000007621046102023000147070ustar 00000000000000[package] name = 'rst_parser' version = "0.4.2" authors = ['Philipp A. '] edition = '2024' description = 'a reStructuredText parser' license = 'MIT OR Apache-2.0' readme = 'README.md' documentation = 'https://docs.rs/rst_parser' 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' pest = '2.1.2' pest_derive = '2.1.0' rst_parser-0.4.2/README.md000064400000000000000000000004511046102023000132720ustar 00000000000000`rst_parser` ============ Part of the [`rst`][rst] crate family. Offers the functions `parse` and `parse_only`, which try to create a `document_tree::Document`. `parse` simplifies this document and resolves references before returning it. [rst]: https://github.com/flying-sheep/rust-rst/#readme rst_parser-0.4.2/src/conversion/block.rs000064400000000000000000000311141046102023000164270ustar 00000000000000use anyhow::{Error, bail}; use pest::iterators::Pair; use document_tree::{ Element, ExtraAttributes, HasChildren, attribute_types as at, element_categories as c, elements as e, extra_attributes as a, }; use super::{inline::convert_inlines, whitespace_normalize_name}; use crate::{pair_ext_parse::PairExt, pest_rst::Rule}; #[derive(PartialEq)] pub(super) enum TitleKind { Double(char), Single(char), } pub(super) enum TitleOrSsubel { Title(e::Title, TitleKind), Ssubel(c::StructuralSubElement), } pub(super) fn convert_ssubel(pair: Pair) -> Result, Error> { use self::TitleOrSsubel::{Ssubel, Title}; Ok(Some(match pair.as_rule() { Rule::title => { let (t, k) = convert_title(pair)?; Title(t, k) } //TODO: subtitle, decoration, docinfo Rule::EOI => return Ok(None), _ => Ssubel(convert_substructure(pair)?.into()), })) } fn convert_substructure(pair: Pair) -> Result { #[allow(clippy::match_single_binding)] Ok(match pair.as_rule() { // TODO: Topic, Sidebar, Transition // no section here, as it’s constructed from titles _ => convert_body_elem(pair)?.into(), }) } fn convert_body_elem(pair: Pair) -> Result { Ok(match pair.as_rule() { Rule::paragraph => convert_paragraph(pair)?.into(), Rule::target => convert_target(pair)?.into(), Rule::footnote => convert_footnote(pair)?.into(), Rule::substitution_def => convert_substitution_def(pair)?.into(), Rule::block_quote_directive => convert_block_quote_directive(pair)?.into(), Rule::admonition_gen => convert_admonition_gen(pair), Rule::image => convert_image::(pair)?.into(), Rule::bullet_list => convert_bullet_list(pair)?.into(), Rule::block_quote => convert_block_quote(pair)?.into(), Rule::literal_block => convert_literal_block(pair).into(), Rule::code_directive => convert_code_directive(pair).into(), Rule::raw_directive => convert_raw_directive(pair).into(), Rule::block_comment => convert_comment(pair).into(), rule => unimplemented!("unhandled rule {:?}", rule), }) } fn convert_title(pair: Pair) -> Result<(e::Title, TitleKind), Error> { let mut title: Option = None; let mut title_inlines: Option> = None; let mut adornment_char: Option = None; // title_double or title_single. Extract kind before consuming let inner_pair = pair.into_inner().next().unwrap(); let kind = inner_pair.as_rule(); for p in inner_pair.into_inner() { match p.as_rule() { Rule::line => { title = Some(p.as_str().to_owned()); title_inlines = Some(convert_inlines(p)?); } Rule::adornments => { adornment_char = Some(p.as_str().chars().next().expect("Empty adornment?")); } rule => unimplemented!("Unexpected rule in title: {:?}", rule), } } // now we encountered one line of text and one of adornments // TODO: emit error if the adornment line is too short (has to match title length) let mut elem = e::Title::with_children(title_inlines.expect("No text in title")); if let Some(title) = title { //TODO: slugify properly let slug = title.to_lowercase().replace('\n', "").replace(' ', "-"); elem.names_mut().push(at::NameToken(slug)); } let title_kind = match kind { Rule::title_double => TitleKind::Double(adornment_char.unwrap()), Rule::title_single => TitleKind::Single(adornment_char.unwrap()), _ => unreachable!(), }; Ok((elem, title_kind)) } fn convert_paragraph(pair: Pair) -> Result { Ok(e::Paragraph::with_children(convert_inlines(pair)?)) } fn convert_target(pair: Pair) -> Result { let mut elem = e::Target::default(); elem.extra_mut().anonymous = false; for p in pair.into_inner() { match p.as_rule() { Rule::target_name_uq | Rule::target_name_qu => { elem.ids_mut().push(p.as_str().into()); elem.names_mut().push(p.as_str().into()); } // TODO: also handle non-urls Rule::link_target => elem.extra_mut().refuri = Some(p.parse()?), rule => panic!("Unexpected rule in target: {rule:?}"), } } Ok(elem) } /// Converts a footnote. /// - named auto-numbered footnotes get their name set /// - explicitly numbered footnotes get their label set fn convert_footnote(pair: Pair) -> Result { let mut pairs = pair.into_inner(); let label = pairs.next().unwrap().as_str(); let mut children: Vec = vec![]; // turn `line` into paragraph children.push(convert_paragraph(pairs.next().unwrap())?.into()); for p in pairs { children.push(convert_body_elem(p)?.into()); } let mut footnote = e::Footnote::with_children(children); footnote.extra_mut().auto = label.chars().next().unwrap().try_into().ok(); match footnote.extra().auto { Some(at::AutoFootnoteType::Number) => { if label.len() > 1 { let name = whitespace_normalize_name(&label[1..]); footnote.names_mut().push(at::NameToken(name)); } } Some(at::AutoFootnoteType::Symbol) => {} None => { footnote .children_mut() .insert(0, e::Label::with_children(vec![label.into()]).into()); } } Ok(footnote) } fn convert_substitution_def(pair: Pair) -> Result { let mut pairs = pair.into_inner(); let name = whitespace_normalize_name(pairs.next().unwrap().as_str()); // Rule::substitution_name let inner_pair = pairs.next().unwrap(); let inner: Vec = match inner_pair.as_rule() { Rule::replace => convert_replace(inner_pair)?, Rule::image => vec![convert_image::(inner_pair)?.into()], rule => panic!("Unknown substitution rule {rule:?}"), }; let mut subst_def = e::SubstitutionDefinition::with_children(inner); subst_def.names_mut().push(at::NameToken(name)); Ok(subst_def) } fn convert_replace(pair: Pair) -> Result, Error> { let mut pairs = pair.into_inner(); let paragraph = pairs.next().unwrap(); convert_inlines(paragraph) } fn convert_image(pair: Pair) -> Result where I: Element + ExtraAttributes, { let mut pairs = pair.into_inner(); let mut image = I::with_extra(a::Image::new( pairs.next().unwrap().as_str().trim().parse()?, // line )); for opt in pairs { let mut opt_iter = opt.into_inner(); let opt_name = opt_iter.next().unwrap(); let opt_val = opt_iter.next().unwrap(); match opt_name.as_str() { "class" => image.classes_mut().push(opt_val.as_str().to_owned()), "name" => image.names_mut().push(opt_val.as_str().into()), "alt" => image.extra_mut().alt = Some(opt_val.as_str().to_owned()), "height" => image.extra_mut().height = Some(opt_val.parse()?), "width" => image.extra_mut().width = Some(opt_val.parse()?), "scale" => image.extra_mut().scale = Some(parse_scale(&opt_val)?), "align" => image.extra_mut().align = Some(opt_val.parse()?), "target" => image.extra_mut().target = Some(opt_val.parse()?), name => bail!("Unknown Image option {}", name), } } Ok(image) } fn parse_scale(pair: &Pair) -> Result { use pest::error::{Error, ErrorVariant}; let input = pair.as_str().trim(); let input = if let Some(percentage) = input.strip_suffix('%') { percentage.trim_end() } else { input }; Ok(input.parse().map_err(|e: std::num::ParseIntError| { let var: ErrorVariant = ErrorVariant::CustomError { message: e.to_string(), }; Error::new_from_span(var, pair.as_span()) })?) } fn convert_admonition_gen(pair: Pair) -> document_tree::element_categories::BodyElement { let mut iter = pair.into_inner(); let typ = iter.next().unwrap().as_str(); // TODO: in reality it contains body elements. let children: Vec = iter .map(|p| e::Paragraph::with_children(vec![p.as_str().into()]).into()) .collect(); match typ { "attention" => e::Attention::with_children(children).into(), "hint" => e::Hint::with_children(children).into(), "note" => e::Note::with_children(children).into(), "caution" => e::Caution::with_children(children).into(), "danger" => e::Danger::with_children(children).into(), "error" => e::Error::with_children(children).into(), "important" => e::Important::with_children(children).into(), "tip" => e::Tip::with_children(children).into(), "warning" => e::Warning::with_children(children).into(), typ => panic!("Unknown admontion type {typ}!"), } } fn convert_bullet_list(pair: Pair) -> Result { Ok(e::BulletList::with_children( pair.into_inner() .map(convert_bullet_item) .collect::>()?, )) } fn convert_bullet_item(pair: Pair) -> Result { let mut iter = pair.into_inner(); let mut children: Vec = vec![convert_paragraph(iter.next().unwrap())?.into()]; for p in iter { children.push(convert_body_elem(p)?); } Ok(e::ListItem::with_children(children)) } fn convert_block_quote(pair: Pair) -> Result { Ok(e::BlockQuote::with_children( pair.into_inner() .map(convert_block_quote_inner) .collect::>()?, )) } fn convert_block_quote_directive(pair: Pair) -> Result { let mut iter = pair.into_inner(); let typ = iter.next().unwrap().as_str(); let children: Vec = iter .map(convert_block_quote_inner) .collect::>()?; let mut bq = e::BlockQuote::with_children(children); bq.classes_mut().push(typ.to_owned()); Ok(bq) } fn convert_block_quote_inner(pair: Pair) -> Result { Ok(if pair.as_rule() == Rule::attribution { e::Attribution::with_children(convert_inlines(pair)?).into() } else { convert_body_elem(pair)?.into() }) } fn convert_literal_block(pair: Pair) -> e::LiteralBlock { convert_literal_lines(pair.into_inner().next().unwrap()) } fn convert_literal_lines(pair: Pair) -> e::LiteralBlock { let children = pair .into_inner() .map(|l| { match l.as_rule() { Rule::literal_line => l.as_str(), Rule::literal_line_blank => "\n", _ => unreachable!(), } .into() }) .collect(); e::LiteralBlock::with_children(children) } fn convert_code_directive(pair: Pair) -> e::LiteralBlock { let mut iter = pair.into_inner(); let (lang, code) = match (iter.next().unwrap(), iter.next()) { (lang, Some(code)) => (Some(lang), code), (code, None) => (None, code), }; let mut code_block = convert_literal_lines(code); code_block.classes_mut().push("code".to_owned()); if let Some(lang) = lang { code_block.classes_mut().push(lang.as_str().to_owned()); } code_block } fn convert_raw_directive(pair: Pair) -> e::Raw { let mut iter = pair.into_inner(); let format = iter.next().unwrap(); let block = iter.next().unwrap(); let children = block .into_inner() .map(|l| { match l.as_rule() { Rule::raw_line => l.as_str(), Rule::raw_line_blank => "\n", _ => unreachable!(), } .into() }) .collect(); let mut raw_block = e::Raw::with_children(children); raw_block .extra_mut() .format .push(at::NameToken(format.as_str().to_owned())); raw_block } fn convert_comment(pair: Pair) -> e::Comment { let lines = pair .into_inner() .map(|l| { match l.as_rule() { Rule::comment_line_blank => "\n", Rule::comment_line => l.as_str(), _ => unreachable!(), } .into() }) .collect(); e::Comment::with_children(lines) } rst_parser-0.4.2/src/conversion/inline.rs000064400000000000000000000150531046102023000166170ustar 00000000000000use anyhow::Error; use pest::iterators::Pair; use document_tree::{ CommonAttributes, Element, ExtraAttributes, HasChildren, attribute_types as at, element_categories as c, elements as e, extra_attributes::{self as a, FootnoteType}, url::Url, }; use super::whitespace_normalize_name; use crate::pest_rst::Rule; pub fn convert_inline(pair: Pair) -> Result { Ok(match pair.as_rule() { Rule::str | Rule::str_nested => pair.as_str().into(), Rule::escaped_char => pair.as_str()[1..].into(), Rule::ws_newline => " ".to_owned().into(), Rule::reference => convert_reference(pair)?, Rule::substitution_name => convert_substitution_ref(&pair).into(), Rule::emph => e::Emphasis::with_children(convert_inlines(pair)?).into(), Rule::strong => e::Strong::with_children(convert_inlines(pair)?).into(), Rule::literal => e::Literal::with_children(vec![pair.as_str().to_owned()]).into(), Rule::footnote_reference => convert_footnote_reference(pair).into(), rule => unimplemented!("unknown rule {:?}", rule), }) } pub fn convert_inlines(pair: Pair) -> Result, Error> { pair.into_inner().map(convert_inline).collect() } fn convert_reference(pair: Pair) -> Result { let concrete = pair.into_inner().next().unwrap(); match concrete.as_rule() { Rule::reference_target => convert_reference_target(concrete).map(Into::into), Rule::reference_explicit => unimplemented!("explicit reference"), Rule::reference_auto => Ok(convert_reference_auto(concrete)), _ => unreachable!(), } } fn convert_reference_target(concrete: Pair<'_, Rule>) -> Result { let rt_inner = concrete.into_inner().next().unwrap(); Ok(match rt_inner.as_rule() { Rule::reference_target_uq => e::Reference::new( CommonAttributes::default(), a::Reference { name: Some(rt_inner.as_str().into()), refuri: None, refid: None, refname: vec![rt_inner.as_str().into()], }, vec![rt_inner.as_str().into()], ), Rule::reference_target_qu => { let (text, reference) = { let mut text = None; let mut reference = None; for inner in rt_inner.clone().into_inner() { match inner.as_rule() { Rule::reference_text => text = Some(inner), Rule::reference_bracketed => reference = Some(inner), _ => unreachable!(), } } (text, reference) }; let trimmed_text = match (&text, &reference) { (Some(text), None) => text.as_str(), (_, Some(reference)) => text .map(|text| text.as_str().trim_end_matches(|ch| " \n\r".contains(ch))) .filter(|text| !text.is_empty()) .unwrap_or_else(|| reference.clone().into_inner().next().unwrap().as_str()), (None, None) => unreachable!(), }; let (refuri, refname): (Option, Vec) = if let Some(reference) = reference { let inner = reference.into_inner().next().unwrap(); match inner.as_rule() { // The URL rules in our parser accept a narrow superset of // valid URLs, so we need to handle false positives. Rule::url => { if let Ok(target) = Url::parse_absolute(inner.as_str()) { (Some(target), Vec::new()) } else if inner.as_str().ends_with('_') { // like target_name_qu (minus the final underscore) let full_str = inner.as_str(); (None, vec![full_str[0..full_str.len() - 1].into()]) } else { // like relative_reference (Some(Url::parse_relative(inner.as_str())?), Vec::new()) } } Rule::target_name_qu => (None, vec![inner.as_str().into()]), Rule::relative_reference => { (Some(Url::parse_relative(inner.as_str())?), Vec::new()) } _ => unreachable!(), } } else { (None, vec![trimmed_text.into()]) }; e::Reference::new( CommonAttributes::default(), a::Reference { name: Some(trimmed_text.into()), refuri, refid: None, refname, }, vec![trimmed_text.into()], ) } _ => unreachable!(), }) } fn convert_reference_auto(concrete: Pair<'_, Rule>) -> c::TextOrInlineElement { let rt_inner = concrete.into_inner().next().unwrap(); let str: c::TextOrInlineElement = rt_inner.as_str().into(); let Ok(target) = (match rt_inner.as_rule() { Rule::url_auto => Url::parse_absolute(rt_inner.as_str()), Rule::email => Url::parse_absolute(&format!("mailto:{}", rt_inner.as_str())), _ => unreachable!(), }) else { // if our parser got a URL wrong, return it as a string return str; }; e::Reference::new( CommonAttributes::default(), a::Reference { name: None, refuri: Some(target), refid: None, refname: Vec::new(), }, vec![str], ) .into() } fn convert_substitution_ref(pair: &Pair) -> e::SubstitutionReference { let name = whitespace_normalize_name(pair.as_str()); a::ExtraAttributes::with_extra(a::SubstitutionReference { refname: vec![at::NameToken(name)], }) } fn convert_footnote_reference(pair: Pair) -> e::FootnoteReference { let label = pair.into_inner().next().unwrap().as_str(); let mut fr = e::FootnoteReference::default(); if label.len() > 1 { let name = whitespace_normalize_name(&label[1..]); fr.names_mut().push(at::NameToken(name)); } fr.extra_mut().auto = label.chars().next().unwrap().try_into().ok(); if !fr.is_auto() { fr.children_mut().push(label.into()); } fr } rst_parser-0.4.2/src/conversion/tests.rs000064400000000000000000000062161046102023000165040ustar 00000000000000use document_tree::{ HasChildren, element_categories as c, elements as e, extra_attributes::ExtraAttributes, }; use crate::parse; fn ssubel_to_section(ssubel: &c::StructuralSubElement) -> &e::Section { match ssubel { c::StructuralSubElement::SubStructure(b) => match b.as_ref() { c::SubStructure::Section(s) => s, c => panic!("Expected section, not {c:?}"), }, c => panic!("Expected SubStructure, not {c:?}"), } } fn ssubel_to_body_element(ssubel: &c::StructuralSubElement) -> &c::BodyElement { match ssubel { c::StructuralSubElement::SubStructure(b) => match b.as_ref() { c::SubStructure::BodyElement(b) => b, c => panic!("Expected BodyElement, not {c:?}"), }, c => panic!("Expected SubStructure, not {c:?}"), } } fn body_element_to_image(bodyel: &c::BodyElement) -> &e::Image { match bodyel { c::BodyElement::Image(i) => i, c => panic!("Expected Image, not {c:?}"), } } const SECTIONS: &str = "\ Intro before first section title Level 1 ******* ------- Level 2 ------- Level 3 ======= L1 again ******** L3 again, skipping L2 ===================== "; #[test] fn convert_skipped_section() { let doctree = parse(SECTIONS).unwrap(); let lvl0 = doctree.children(); assert_eq!( lvl0.len(), 3, "Should be a paragraph and 2 sections: {lvl0:?}" ); assert_eq!( lvl0[0], e::Paragraph::with_children(vec!["Intro before first section title".to_owned().into()]) .into(), "The intro text should fit" ); let lvl1_a = ssubel_to_section(&lvl0[1]).children(); assert_eq!( lvl1_a.len(), 2, "The 1st lvl1 section should have (a title and) a single lvl2 section as child: {lvl1_a:?}" ); //TODO: test title lvl1a[0] let lvl2 = ssubel_to_section(&lvl1_a[1]).children(); assert_eq!( lvl2.len(), 2, "The lvl2 section should have (a title and) a single lvl3 section as child: {lvl2:?}" ); //TODO: test title lvl2[0] let lvl3_a = ssubel_to_section(&lvl2[1]).children(); assert_eq!( lvl3_a.len(), 1, "The 1st lvl3 section should just a title: {lvl3_a:?}" ); //TODO: test title lvl3a[0] let lvl1_b = ssubel_to_section(&lvl0[2]).children(); assert_eq!( lvl1_b.len(), 2, "The 2nd lvl1 section should have (a title and) a single lvl2 section as child: {lvl1_b:?}" ); //TODO: test title lvl1b[0] let lvl3_b = ssubel_to_section(&lvl1_b[1]).children(); assert_eq!( lvl3_b.len(), 1, "The 2nd lvl3 section should have just a title: {lvl3_b:?}" ); //TODO: test title lvl3b[0] } #[test] fn test_convert_image_scale() { let doctree = parse(".. image:: /path/to/img.jpg\n :scale: 90%\n\n").unwrap(); let lvl0 = doctree.children(); assert_eq!(lvl0.len(), 1, "Should be a single image: {lvl0:?}"); let be = ssubel_to_body_element(&lvl0[0]); let img = body_element_to_image(be); assert_eq!(img.extra().scale, Some(90)); assert_eq!(img.extra().uri, "/path/to/img.jpg".parse().unwrap()); } rst_parser-0.4.2/src/conversion.rs000064400000000000000000000072271046102023000153450ustar 00000000000000mod block; mod inline; #[cfg(test)] mod tests; use anyhow::Error; use pest::iterators::Pairs; use document_tree::{ Element, HasChildren, attribute_types as at, element_categories as c, elements as e, }; use crate::pest_rst::Rule; fn ssubel_to_section_unchecked_mut(ssubel: &mut c::StructuralSubElement) -> &mut e::Section { match ssubel { c::StructuralSubElement::SubStructure(b) => match b.as_mut() { c::SubStructure::Section(s) => s, _ => unreachable!(), }, _ => unreachable!(), } } fn get_level<'tl>( toplevel: &'tl mut Vec, section_idxs: &[Option], ) -> &'tl mut Vec { let mut level = toplevel; for i in section_idxs.iter().flatten().copied() { level = ssubel_to_section_unchecked_mut(&mut level[i]).children_mut(); } level } pub fn convert_document(pairs: Pairs) -> Result { use self::block::TitleOrSsubel::{Ssubel, Title}; let mut toplevel: Vec = vec![]; // The kinds of section titles encountered. // `section_idx[x]` has the kind `kinds[x]`, but `kinds` can be longer let mut kinds: Vec = vec![]; // Recursive indices into the tree, pointing at the active sections. // `None`s indicate skipped section levels: // toplevel[section_idxs.flatten()[0]].children[section_idxs.flatten()[1]]... let mut section_idxs: Vec> = vec![]; for pair in pairs { if let Some(ssubel) = block::convert_ssubel(pair)? { match ssubel { Title(title, kind) => { match kinds.iter().position(|k| k == &kind) { // Idx points to the level we want to add, // so idx-1 needs to be the last valid index. Some(idx) => { // If idx < len: Remove found section and all below section_idxs.truncate(idx); // If idx > len: Add None for skipped levels // TODO: test skipped levels while section_idxs.len() < idx { section_idxs.push(None); } } None => kinds.push(kind), } let super_level = get_level(&mut toplevel, §ion_idxs); let slug = title .names() .iter() .next() .map(|at::NameToken(name)| at::ID(name.to_owned())); let mut section = e::Section::with_children(vec![title.into()]); section.ids_mut().extend(slug.into_iter()); super_level.push(section.into()); section_idxs.push(Some(super_level.len() - 1)); } Ssubel(elem) => get_level(&mut toplevel, §ion_idxs).push(elem), } } } Ok(e::Document::with_children(toplevel)) } /// Normalizes a name in terms of whitespace. Equivalent to docutils's /// `docutils.nodes.whitespace_normalize_name`. pub fn whitespace_normalize_name(name: &str) -> String { // Python's string.split() defines whitespace differently than Rust does. let split_iter = name .split(|ch: char| ch.is_whitespace() || ('\x1C'..='\x1F').contains(&ch)) .filter(|split| !split.is_empty()); let mut ret = String::new(); for split in split_iter { if !ret.is_empty() { ret.push(' '); } ret.push_str(split); } ret } rst_parser-0.4.2/src/lib.rs000064400000000000000000000015111046102023000137140ustar 00000000000000#![warn(clippy::pedantic)] mod conversion; mod pair_ext_parse; mod pest_rst; #[cfg(test)] pub mod tests; pub mod token; pub mod transforms; use anyhow::Error; use pest::Parser; use document_tree::Document; use self::conversion::convert_document; use self::pest_rst::{RstParser, Rule}; use self::transforms::standard_transform; /// Parse into a document tree and resolve sections, but not references. /// /// # Errors /// Returns an error if parsing fails. pub fn parse_only(source: &str) -> Result { let pairs = RstParser::parse(Rule::document, source)?; convert_document(pairs) } /// Parse into a document tree and resolve sections and references. /// /// # Errors /// Returns an error if parsing fails. pub fn parse(source: &str) -> Result { parse_only(source).map(standard_transform) } rst_parser-0.4.2/src/pair_ext_parse.rs000064400000000000000000000015111046102023000161530ustar 00000000000000use std::str::FromStr; use pest::Span; use pest::error::{Error, ErrorVariant}; use pest::iterators::Pair; pub trait PairExt where R: pest::RuleType, { fn parse(&self) -> Result>> where T: FromStr, E: ToString; } impl PairExt for Pair<'_, R> where R: pest::RuleType, { fn parse(&self) -> Result>> where T: FromStr, E: ToString, { self.as_str() .parse() .map_err(|e| to_parse_error(self.as_span(), &e)) } } pub(crate) fn to_parse_error(span: Span, e: &E) -> Box> where E: ToString, R: pest::RuleType, { let var: ErrorVariant = ErrorVariant::CustomError { message: e.to_string(), }; Box::new(Error::new_from_span(var, span)) } rst_parser-0.4.2/src/pest_rst.rs000064400000000000000000000002001046102023000150030ustar 00000000000000#![allow(clippy::redundant_closure)] use pest_derive::Parser; #[derive(Parser)] #[grammar = "rst.pest"] pub struct RstParser; rst_parser-0.4.2/src/rst.pest000064400000000000000000000607061046102023000143200ustar 00000000000000// Entry point: the document. // This grammar is aligned to the doctree names when possible. // It will however contain blocks, as we can’t parse sections: // Section headers define the hierarchy by their delimiters, // and pest only has one stack that we need for indentation. document = _{ SOI ~ blank_line* ~ blocks ~ EOI } blocks = _{ block ~ (blank_line* ~ block)* ~ blank_line? } block = _{ PEEK[..] ~ hanging_block } hanging_blocks = _{ hanging_block ~ blank_line* ~ blocks? } // This is the list of all block-level elements // They’re defined hanging, i.e. without the first PEEK[..] hanging_block = _{ substitution_def | image_directive | code_directive | raw_directive | admonition | admonition_gen | block_quote_directive | target | footnote | literal_block // Comments should be below the directives to try to match them first, but // above the title that will interpret ".." as a title marker. | block_comment | title | bullet_list | block_quote | paragraph // TODO: implement all those things: // | verbatim // | doctest_block // | horizontal_rule // | table // | ordered_list // | plain } // Substitution definition. A block type substitution_def = { ".." ~ PUSH(" "+) ~ "|" ~ substitution_name ~ "|" ~ " "+ ~ inline_dirblock ~ DROP } substitution_name = { !" " ~ (!(" "|"|") ~ ANY)+ ~ (" "+ ~ (!(" "|"|") ~ ANY)+)* } inline_dirblock = _{ replace | image } // TODO: implement others // Target. A block type target = { target_qu | target_uq } target_uq = _{ ".." ~ " "+ ~ "_" ~ target_name_uq ~ ":" ~ (" " ~ link_target)? ~ " "* ~ NEWLINE } target_qu = _{ ".." ~ " "+ ~ "_`" ~ !"``" ~ target_name_qu ~ !"``:" ~ "`:" ~ (" " ~ link_target)? ~ " "* ~ NEWLINE } target_name_uq = { ( !("_"|":"|"`"|NEWLINE) ~ ANY )* } target_name_qu = { ( !(":"|"`"|"_>") ~ ANY )* } link_target = { nonspacechar+ } // Footnote. A block type. https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#footnotes footnote = { ".." ~ PUSH(" "+) ~ "[" ~ footnote_label ~ "]" ~ " "+ ~ line ~ blank_line* ~ footnote_body? ~ DROP } footnote_label = { "#" ~ (!("]"|NEWLINE) ~ ANY)* | "*" | ASCII_DIGIT+ } footnote_body = _{ PEEK[..-1] ~ PUSH(" " ~ POP) ~ hanging_blocks } // Title. A block type title = { title_double | title_single } title_double = { PUSH(adornments) ~ NEWLINE ~ PEEK[..-1] ~ " "* ~ line ~ PEEK[..-1] ~ POP } title_single = { line ~ PEEK[..] ~ adornments ~ NEWLINE } // Bullet list. A block type. bullet_list = { bullet_item ~ (PEEK[..] ~ bullet_item)* } bullet_item = { bullet_marker ~ PUSH(" "+) ~ line ~ blank_line* ~ blist_body? ~ DROP } blist_body = _{ PEEK[..-1] ~ PUSH(" " ~ POP) ~ hanging_blocks } // Block quote. A block type. block_quote = { PUSH(" " ~ " "+) ~ block_quote_content ~ DROP } block_quote_content = _{ hanging_block ~ (blank_line* ~ !attribution ~ block)* ~ blank_line* ~ attribution? } // https://github.com/docutils/docutils/blob/f704fb58904d62bf5c6d7db82a9628f29e4ed246/docutils/docutils/parsers/rst/states.py#L1201 attribution = { PEEK[..] ~ (("--" ~ "-"? ~ !"-") | "\u{2014}") ~ " "* ~ line+ } // paragraph. A block type. paragraph = { inlines } // literal_block literal_block = { "::" ~ " "* ~ NEWLINE ~ blank_line+ ~ PUSH(" "+) ~ literal_lines ~ DROP } literal_lines = { literal_line ~ (literal_line_blank* ~ PEEK[..] ~ literal_line)* } literal_line_blank = { " "* ~ NEWLINE } literal_line = { (!NEWLINE ~ ANY)+ ~ NEWLINE } /* Directives: https://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#directives * .. name:: arguments ~ :options: ~ blank_line+ ~ content * Everything except for the first argument has to be indented */ // Directives with options can have these or specific ones: common_opt_name = { "class" | "name" } // Replace. A directive only usable in substitutions. replace = { ^"replace::" ~ " "* ~ paragraph } // Image. A directive. image_directive = _{ ".." ~ PUSH(" "+) ~ image ~ DROP } image = { ^"image::" ~ line ~ image_opt_block? } image_opt_block = _{ PEEK[..-1] ~ PUSH(" " ~ POP) ~ image_option ~ (PEEK[..] ~ image_option)* } image_option = { ":" ~ image_opt_name ~ ":" ~ line } image_opt_name = { common_opt_name | "alt" | "height" | "width" | "scale" | "align" | "target" } // Code block. A directive that allows adding a language to a literal block code_directive = { ".." ~ PUSH(" "+) ~ "code" ~ "-block"? ~ "::" ~ (" "+ ~ source)? ~ NEWLINE ~ blank_line+ ~ PEEK[..-1] ~ PUSH(" " ~ POP) ~ literal_lines ~ DROP } source = { (!NEWLINE ~ ANY)+ } // Raw block. A directive raw_directive = { ".." ~ PUSH(" "+) ~ "raw::" ~ " "+ ~ raw_output_format ~ NEWLINE ~ blank_line+ ~ PEEK[..-1] ~ PUSH(" " ~ POP) ~ raw_block ~ DROP } raw_output_format = { (!NEWLINE ~ ANY)+ } raw_block = { raw_line ~ (raw_line_blank* ~ PEEK[..] ~ raw_line)* } raw_line_blank = { " "* ~ NEWLINE } raw_line = { (!NEWLINE ~ ANY)+ ~ NEWLINE } // Admonition. A directive. The generic one has a title admonition = { ".." ~ PUSH(" "+) ~ ^"admonition::" ~ line ~ blank_line* ~ admonition_content? ~ DROP } admonition_gen = { ".." ~ PUSH(" "+) ~ admonition_type ~ "::" ~ (blank_line | line) ~ blank_line* ~ admonition_content? ~ DROP } admonition_type = { ^"attention" | ^"caution" | ^"danger" | ^"error" | ^"hint" | ^"important" | ^"note" | ^"tip" | ^"warning" } admonition_content = _{ PEEK[..-1] ~ PUSH(" " ~ POP) ~ hanging_blocks } //TODO: merge with other directives? // Block quote directives. The generic one is defined by simply indenting a block block_quote_directive = { ".." ~ PUSH(" "+) ~ block_quote_type ~ "::" ~ (blank_line | line) ~ blank_line* ~ PEEK[..-1] ~ PUSH(" " ~ POP) ~ block_quote_content ~ DROP } block_quote_type = { ^"epigraph" | ^"highlights" | ^"pull-quote" } // Comments. block_comment = { ".." ~ ( // Without title (" "* ~ NEWLINE)+ ~ PUSH(" "+) ~ comment_hanging ~ DROP // or with title | PUSH(" "+) ~ comment_line ~ (comment_line_blank* ~ PEEK[..-1] ~ PUSH(" " ~ POP) ~ comment_hanging)? ~ DROP // or empty | " "* ~ NEWLINE ) ~ (" "* ~ NEWLINE)* } comment_hanging = _{ comment_line ~ (comment_line_blank* ~ PEEK[..] ~ comment_line)* } comment_line_blank = { " "* ~ NEWLINE } comment_line = { (!NEWLINE ~ ANY)+ ~ NEWLINE } /* * inlines */ line = { !marker ~ inline+ ~ NEWLINE } blank_line = _{ !marker ~ !inline ~ " "* ~ NEWLINE } inlines = _{ !marker ~ inline+ ~ ( ( ws_newline ~ PEEK[..] ~ !marker ~ inline+ )+ ~ NEWLINE )? } ws_newline = { NEWLINE } inline = _{ inline_special | str } inline_special = _{ footnote_reference | reference | substitution_ref | emph_outer | strong_outer | literal_outer // | ul_or_star_line // | space // //| citation // | code // | application_depent // | entity | escaped_char // | smart // | symbol } str = { (!(NEWLINE | inline_special) ~ ANY)+ } // simple formatting inline_nested = _{ inline_special | str_nested } str_nested = { word_nested ~ ( " "+ ~ word_nested)* } // TODO: allow ` in emph word_nested = _{ (!(NEWLINE | " " | inline_special | "*" | "`") ~ ANY)+ } emph_outer = _{ "*" ~ emph ~ "*" } emph = { (!("*"|" ") ~ inline_nested)+ ~ (" "+ ~ (!("*"|" ") ~ inline_nested)+)* } strong_outer = _{ "**" ~ strong ~ "**" } strong = { (!("*"|" ") ~ inline_nested)+ ~ (" "+ ~ (!("*"|" ") ~ inline_nested)+)* } literal_outer = _{ "``" ~ literal ~ "``" } literal = { (!"``" ~ ANY)+ } // inline links footnote_reference = { "[" ~ footnote_label ~ "]_" } reference = { reference_target | reference_explicit | reference_auto } reference_target = { (reference_target_uq ~ "_" | reference_target_qu) ~ !(LETTER|NUMBER) } reference_target_uq = { (!("_"|":"|"`"|"[") ~ nonspacechar)+ } reference_target_qu = { ( !("`"? ~ "`_") ~ "`" ~ !"``" ) ~ reference_text? ~ ("<" ~ reference_bracketed ~ ">")? ~ ( "`" ~ !"``" ) ~ "_" } reference_text = { !"<" ~ ( !("`"|"<") ~ ANY )+ } reference_bracketed = { url | (target_name_qu ~ "_") | relative_reference } relative_reference = { (!("`"|">") ~ ANY)+ } reference_explicit = { reference_label ~ "(" ~ " "* ~ reference_source ~ " "* ~ (NEWLINE ~ PEEK[..])? ~ reference_title ~ " "* ~ ")" } reference_label = { "[" ~ !"^" ~ (!"]" ~ inline)* ~ "]" } reference_source = { reference_source_contents } reference_source_contents = _{ ( (!("("|")"|">") ~ nonspacechar)+ | "(" ~ reference_source_contents ~ ")" )* } reference_title = { ( reference_title_single | reference_title_double | "" ) } reference_title_single = { "'" ~ ( !("'" ~ " "+ ~ (")" | NEWLINE)) ~ ANY )* ~ "'" } reference_title_double = { "\"" ~ ( !("\"" ~ " "+ ~ (")" | NEWLINE)) ~ ANY )* ~ "\"" } // Emails can't end with punctuation, but URLs must use a separate rule. reference_auto = { url_auto | email } //reference_embedded = { "`" ~ reference_embedded_source ~ "<" ~ absolute_url_with_fragment ~ ">`_" ~ "_"? } //reference_embedded_source = { ( !("<"|":"|"`") ~ ( " " | nonspacechar | blank_line ) )* } substitution_ref = _{ "|" ~ substitution_name ~ "|" } /* URLs as defined by the WHATWG URL standard. */ url = { absolute_url_no_query ~ ("?" ~ url_unit*)? ~ ("#" ~ url_unit*)? } absolute_url_no_query = { ( special_url_scheme ~ ":" ~ scheme_relative_special_url ) | ( ^"file:" ~ scheme_relative_file_url ) | ( arbitrary_scheme ~ ":" ~ relative_url ) } scheme_relative_special_url = { "//" ~ host ~ (":" ~ url_port)? ~ path_absolute_url? } path_absolute_url = { "/" ~ path_relative_url } path_relative_url = { ( url_path_segment_unit* ~ "/" )* ~ url_path_segment_unit* } url_path_segment_unit = { !("/"|"?") ~ url_unit } url_port = { ASCII_DIGIT* } scheme_relative_file_url = { "//" ~ ( host ~ !("/:/"|"/|/") )? ~ path_absolute_url } relative_url = { ( "//" ~ host ~ (":" ~ url_port)? ~ path_absolute_url? ) | path_absolute_url | (!(arbitrary_scheme ~ ":") ~ path_relative_url) } // this is approximately a superset of valid hosts and opaque hosts host = { ( !(":"|"/"|"?"|"#") ~ url_unit)+ | ("["~(ASCII_HEX_DIGIT|"."|":")+~"]") } special_url_scheme = { ^"ftp" | (^"http" | ^"ws") ~ ^"s"? } /* doesn't include "file" */ arbitrary_scheme = { ASCII_ALPHA ~ ASCII_ALPHANUMERIC* } // taken at 2020-09-06 from https://www.iana.org/assignments/uri-schemes/uri-schemes.xhtml known_scheme = { "aaa"|"aaas"|"about"|"acap"|"acct"|"acd"|"acr"|"adiumxtra"|"adt"|"afp"|"afs"|"aim"|"amss"|"android"|"appdata"|"apt"|"ark"|"attachment"|"aw"| "barion"|"beshare"|"bitcoin"|"bitcoincash"|"blob"|"bolo"|"browserext"|"cabal"|"calculator"|"callto"|"cap"|"cast"|"casts"|"chrome"| "chrome-extension"|"cid"|"coap"|"coap+tcp"|"coap+ws"|"coaps"|"coaps+tcp"|"coaps+ws"|"com-eventbrite-attendee"|"content"|"conti"|"crid"|"cvs"| "dab"|"dat"|"data"|"dav"|"diaspora"|"dict"|"did"|"dis"|"dlna-playcontainer"|"dlna-playsingle"|"dns"|"dntp"|"doi"|"dpp"|"drm"|"drop"|"dtmi"| "dtn"|"dvb"|"dweb"|"ed2k"|"elsi"|"ens"|"ethereum"|"example"|"facetime"|"fax"|"feed"|"feedready"|"file"|"filesystem"|"finger"| "first-run-pen-experience"|"fish"|"fm"|"ftp"|"fuchsia-pkg"|"geo"|"gg"|"git"|"gizmoproject"|"go"|"gopher"|"graph"|"gtalk"|"h323"|"ham"|"hcap"| "hcp"|"http"|"https"|"hxxp"|"hxxps"|"hydrazone"|"hyper"|"iax"|"icap"|"icon"|"im"|"imap"|"info"|"iotdisco"|"ipfs"|"ipn"|"ipns"|"ipp"|"ipps"| "irc"|"irc6"|"ircs"|"iris"|"iris.beep"|"iris.lwz"|"iris.xpc"|"iris.xpcs"|"isostore"|"itms"|"jabber"|"jar"|"jms"|"keyparc"|"lastfm"|"lbry"| "ldap"|"ldaps"|"leaptofrogans"|"lorawan"|"lvlt"|"magnet"|"mailserver"|"mailto"|"maps"|"market"|"matrix"|"message"|"microsoft.windows.camera"| "microsoft.windows.camera.multipicker"|"microsoft.windows.camera.picker"|"mid"|"mms"|"modem"|"mongodb"|"moz"|"ms-access"| "ms-browser-extension"|"ms-calculator"|"ms-drive-to"|"ms-enrollment"|"ms-excel"|"ms-eyecontrolspeech"|"ms-gamebarservices"| "ms-gamingoverlay"|"ms-getoffice"|"ms-help"|"ms-infopath"|"ms-inputapp"|"ms-lockscreencomponent-config"|"ms-media-stream-id"| "ms-mixedrealitycapture"|"ms-mobileplans"|"ms-officeapp"|"ms-people"|"ms-project"|"ms-powerpoint"|"ms-publisher"|"ms-restoretabcompanion"| "ms-screenclip"|"ms-screensketch"|"ms-search"|"ms-search-repair"|"ms-secondary-screen-controller"|"ms-secondary-screen-setup"|"ms-settings"| "ms-settings-airplanemode"|"ms-settings-bluetooth"|"ms-settings-camera"|"ms-settings-cellular"|"ms-settings-cloudstorage"| "ms-settings-connectabledevices"|"ms-settings-displays-topology"|"ms-settings-emailandaccounts"|"ms-settings-language"| "ms-settings-location"|"ms-settings-lock"|"ms-settings-nfctransactions"|"ms-settings-notifications"|"ms-settings-power"| "ms-settings-privacy"|"ms-settings-proximity"|"ms-settings-screenrotation"|"ms-settings-wifi"|"ms-settings-workplace"|"ms-spd"| "ms-sttoverlay"|"ms-transit-to"|"ms-useractivityset"|"ms-virtualtouchpad"|"ms-visio"|"ms-walk-to"|"ms-whiteboard"|"ms-whiteboard-cmd"| "ms-word"|"msnim"|"msrp"|"msrps"|"mss"|"mtqp"|"mumble"|"mupdate"|"mvn"|"news"|"nfs"|"ni"|"nih"|"nntp"|"notes"|"ocf"|"oid"|"onenote"| "onenote-cmd"|"opaquelocktoken"|"openpgp4fpr"|"otpauth"|"pack"|"palm"|"paparazzi"|"payment"|"payto"|"pkcs11"|"platform"|"pop"|"pres"| "prospero"|"proxy"|"pwid"|"psyc"|"pttp"|"qb"|"query"|"quic-transport"|"redis"|"rediss"|"reload"|"res"|"resource"|"rmi"|"rsync"|"rtmfp"| "rtmp"|"rtsp"|"rtsps"|"rtspu"|"secondlife"|"service"|"session"|"sftp"|"sgn"|"shttp"|"sieve"|"simpleledger"|"sip"|"sips"|"skype"|"smb"|"sms"| "smtp"|"snews"|"snmp"|"soap.beep"|"soap.beeps"|"soldat"|"spiffe"|"spotify"|"ssb"|"ssh"|"steam"|"stun"|"stuns"|"submit"|"swh"|"svn"|"tag"| "teamspeak"|"tel"|"teliaeid"|"telnet"|"tftp"|"things"|"thismessage"|"tip"|"tn3270"|"tool"|"turn"|"turns"|"tv"|"udp"|"unreal"|"upt"|"urn"| "ut2004"|"v-event"|"vemmi"|"ventrilo"|"videotex"|"vnc"|"view-source"|"vscode"|"vscode-insiders"|"vsls"|"wais"|"webcal"|"wifi"|"wpid"|"ws"| "wss"|"wtai"|"wyciwyg"|"xcon"|"xcon-userid"|"xfire"|"xmlrpc.beep"|"xmlrpc.beeps"|"xmpp"|"xri"|"ymsgr"|"z39.50"|"z39.50r"|"z39.50s" } url_unit = { ASCII_ALPHANUMERIC | "!"|"$"|"&"|"'"|"("|")"|"*"|"+"|","|"-"|"."|"/"|":"|";"|"="|"?"|"@"|"_"|"~" | (!(SURROGATE|NONCHARACTER_CODE_POINT) ~ '\u{A0}'..'\u{10FFFD}') | ("%" ~ ASCII_HEX_DIGIT{2}) } /* * Rules for URLs that don't end in punctuation. * This is a modification of the rules above to incorporate the docutils rules * for the final character in an auto URL and for the character after it. * The patterns used here to emulate the behavior of docutils' regex are taken * from . */ url_auto = { ( absolute_url_no_query ~ ("?" ~ url_unit*)? ~ "#" ~ url_units_auto ) | ( absolute_url_no_query ~ "?" ~ url_units_auto ) | ( special_url_scheme ~ "://" ~ host ~ (":" ~ url_port)? ~ path_absolute_url_auto ) | ( special_url_scheme ~ "://" ~ host ~ ":" ~ url_port ~ &follows_auto_url ) | ( special_url_scheme ~ "://" ~ ( domain_host_auto | "["~(ASCII_HEX_DIGIT|"."|":")+~"]" ~ &follows_auto_url ) ) | ( ^"file://" ~ ( host ~ !("/:/"|"/|/") )? ~ path_absolute_url_auto ) | ( known_scheme ~ ":" ~ relative_url_auto ) } domain_host_auto = { ( !(":"|"/"|"?"|"#") ~ url_unit ~ url_units_auto ) | ( !(":"|"/"|"?"|"#") ~ url_unit ~ &">" ) | ( (ASCII_ALPHANUMERIC|"_"|"~"|"*"|"/"|"="|"+") ~ &follows_auto_url ) } path_absolute_url_auto = { "/" ~ path_relative_url_auto } path_relative_url_auto = { prua1 | prua2 | &follows_auto_url } prua1 = { ( url_path_segment_unit ~ prua1 ) | ( "/" ~ path_relative_url_auto ) } prua2 = { ( url_path_segment_unit ~ prua2 ) | ( (ASCII_ALPHANUMERIC|"_"|"~"|"*"|"="|"+") ~ &follows_auto_url ) } relative_url_auto = { ( "//" ~ host ~ (":" ~ url_port)? ~ path_absolute_url_auto ) | ( "//" ~ host ~ ":" ~ url_port ~ &follows_auto_url ) | ( "//" ~ ( domain_host_auto | "["~(ASCII_HEX_DIGIT|"."|":")+~"]" ~ &follows_auto_url ) ) | path_absolute_url_auto | // (prua1|prua2) is path_relative_url_auto minus the &follows_auto_url case (!(known_scheme ~ ":") ~ (prua1 | prua2)) } url_units_auto = { ( url_unit ~ url_units_auto ) | ( url_unit ~ &">" ~ &follows_auto_url ) | ( (ASCII_ALPHANUMERIC|"_"|"~"|"*"|"/"|"="|"+") ~ &follows_auto_url ) } follows_auto_url = { EOI|"\x00"|WHITE_SPACE|">"|"\u{201A}"|"\u{201E}"| (!(CONNECTOR_PUNCTUATION|OPEN_PUNCTUATION|"#"|"%"|"&"|"*"|"@") ~ PUNCTUATION) } /* Rules for emails as defined by the HTML standard */ email = { ( email_atext | "." )+ ~ "@" ~ email_label ~ ( "." ~ email_label )* } email_atext = { ASCII_ALPHANUMERIC|"!"|"#"|"$"|"%"|"&"|"'"|"/"|"="|"?"|"^"|"_"|"`"|"{"|"|"|"}"|"~" } email_label = { ASCII_ALPHANUMERIC ~ ( !("-"+ ~ !ASCII_ALPHANUMERIC) ~ (ASCII_ALPHANUMERIC|"-") ){0,62} } /* * character classes */ bullet_marker = _{ "+" | "*" | "-" } adornments = { // recommended "="+ | "-"+ | "`"+ | ":"+ | "."+ | "'"+ | "\""+ | "~"+ | "^"+ | "_"+ | "*"+ | "+"+ | "#"+ | // parentheses "("+ | ")"+ | "["+ | "]"+ | "{"+ | "}"+ | // punctuation ","+ | ";"+ | "!"+ | "?"+ | // operators "&"+ | "|"+ | "/"+ | "%"+ | "<"+ | ">"+ | // misc "$"+ | "@"+ | "\\"+ } nonspacechar = _{ !(" " | NEWLINE) ~ ANY } /* * lookaheads. do not use in another position */ marker = _{ (bullet_marker | "..") ~ " " } //################################################################################# // doctest_block = { (doctest_line+ ~ (!(">" | blank_line) ~ line)*)+ } // block_quote_raw = { ":" ~ blank_line ~ NEWLINE ~ nonblank_indented_line+ } // block_quote_chunk = { // !"::" ~ ":" ~ blank_line ~ // NEWLINE ~ // blank_line* ~ // nonblank_indented_line+ // } // block_quote = { block_quote_chunk+ } // horizontal_rule = { // ( "=" ~ sp ~ "=" ~ sp ~ "=" ~ (sp ~ "=")* // | "-" ~ sp ~ "-" ~ sp ~ "-" ~ (sp ~ "-")* // | "*" ~ sp ~ "*" ~ sp ~ "*" ~ (sp ~ "*")* // | "^" ~ sp ~ "^" ~ sp ~ "^" ~ (sp ~ "^")* // | "~" ~ sp ~ "~" ~ sp ~ "~" ~ (sp ~ "~")* // | "_" ~ sp ~ "_" ~ sp ~ "_" ~ (sp ~ "_")* // ) ~ // sp ~ NEWLINE ~ blank_line+ // } // table = { grid_table | header_less_grid_table | simple_table } // simple_table = { "NotImplemented" ~ "simple_table" } // grid_table = { grid_table_header ~ grid_table_header_sep ~ grid_table_body+ } // header_less_grid_table = { grid_table_sep ~ grid_table_body+ } // grid_table_header = { sp ~ "+" ~ ( "-"+ ~ "+" )+ ~ blank_line ~ grid_table_row+ } // grid_table_body = { ( grid_table_row ~ grid_table_sep )+ } // grid_table_row = { sp ~ "|" ~ sp ~ ( table_cell ~ sp ~ "|" )+ ~ blank_line } // table_cell = { ( ":" | ">" | "<" | "/" | "-" | spacechar | escaped_char | alphanumeric )+ } // grid_table_header_sep = { sp ~ "+" ~ ( "="+ ~ "+" )+ ~ blank_line } // grid_table_sep = { sp ~ "+" ~ ( "-"+ ~ "+" )+ ~ blank_line } // bullet = { !horizontal_rule ~ ("+" | "*" | "-") ~ spacechar+ } // bullet_list = { &bullet ~ (list_tight | list_loose) } // list_tight = { list_item_tight+ ~ blank_line* ~ !(bullet | enumerator | def_marker) } // list_loose = { ( list_item ~ blank_line* )+ } // list_item = { (bullet | enumerator | def_marker) ~ list_block ~ list_continuation_block* } // list_item_tight = { // (bullet | enumerator | def_marker) ~ // list_block ~ // (!blank_line ~ list_continuation_block)* ~ // !list_continuation_block // } // list_block = { !blank_line ~ line ~ list_block_line* } // list_continuation_block = { blank_line* ~ ( indent ~ list_block )+ } // enumerator = { (ASCII_DIGIT+ | "#"+) ~ "." ~ spacechar+ } // ordered_list = { &enumerator ~ (list_tight | list_loose) } // list_block_line = { // !blank_line ~ // !( (indent? ~ (bullet | enumerator)) | def_marker ) ~ // !horizontal_rule ~ // optionally_indented_line // } // space = _{ spacechar+ } // str = { normal_char+ ~ str_chunk* } // str_chunk = _{ (normal_char | "_"+ ~ &alphanumeric)+ } escaped_char = { "\\" ~ !NEWLINE ~ ("-" | "\\" | "`" | "|" | "*" | "_" | "{" | "}" | "[" | "]" | "(" | ")" | "#" | "+" | "." | "!" | ">" | "<") } // entity = { hex_entity | dec_entity | char_entity } // endline = _{ line_break | terminal_endline | normal_endline } // normal_endline = _{ sp ~ NEWLINE ~ !(blank_line | ">" | line ~ ("="+ | "-"+) ~ NEWLINE) } // terminal_endline = _{ sp ~ NEWLINE ~ EOI } // line_break = _{ " " ~ normal_endline } // symbol = { special_char } // application_depent = { !("`_" | "``_") ~ "`" ~ !"``" ~ target_name_qu ~ "`" ~ !("``" | "_") } // // This keeps the parser from getting bogged down on long strings of "*" or "_", // // or strings of "*" or "_" with space on each side: // ul_or_star_line = { ul_line | star_line } // star_line = { "****" ~ "*"* | spacechar ~ "*"+ ~ &spacechar } // ul_line = { "____" ~ "_"* | spacechar ~ "_"+ ~ &spacechar } // empty_title = { "" } // ticks_2 = { "``" ~ !"`" } // code = { ticks_2 ~ ( (!"`" ~ nonspacechar)+ | "_" | !ticks_2 ~ "`" | !(sp ~ ticks_2) ~ (spacechar | NEWLINE ~ !blank_line) )+ ~ ticks_2 } // quoted = { // "\"" ~ (!"\"" ~ ANY)* ~ "\"" | // "'" ~ (!"'" ~ ANY)* ~ "'" // } // spacechar = _{ " " | "\t" } // sp = _{ spacechar* } // spnl = _{ sp ~ (NEWLINE ~ sp)? } // special_char = _{ "~" | "*" | "_" | "`" | "&" | "[" | "]" | "(" | ")" | "<" | "!" | "#" | "\\" | "\"" | "'" | extended_special_char } // normal_char = _{ !( special_char | spacechar | NEWLINE ) ~ ANY } // alphanumeric = { // ASCII_ALPHANUMERIC | // "\u{200}" | "\u{201}" | "\u{202}" | "\u{203}" | "\u{204}" | "\u{205}" | "\u{206}" | "\u{207}" | // "\u{210}" | "\u{211}" | "\u{212}" | "\u{213}" | "\u{214}" | "\u{215}" | "\u{216}" | "\u{217}" | // "\u{220}" | "\u{221}" | "\u{222}" | "\u{223}" | "\u{224}" | "\u{225}" | "\u{226}" | "\u{227}" | // "\u{230}" | "\u{231}" | "\u{232}" | "\u{233}" | "\u{234}" | "\u{235}" | "\u{236}" | "\u{237}" | // "\u{240}" | "\u{241}" | "\u{242}" | "\u{243}" | "\u{244}" | "\u{245}" | "\u{246}" | "\u{247}" | // "\u{250}" | "\u{251}" | "\u{252}" | "\u{253}" | "\u{254}" | "\u{255}" | "\u{256}" | "\u{257}" | // "\u{260}" | "\u{261}" | "\u{262}" | "\u{263}" | "\u{264}" | "\u{265}" | "\u{266}" | "\u{267}" | // "\u{270}" | "\u{271}" | "\u{272}" | "\u{273}" | "\u{274}" | "\u{275}" | "\u{276}" | "\u{277}" | // "\u{300}" | "\u{301}" | "\u{302}" | "\u{303}" | "\u{304}" | "\u{305}" | "\u{306}" | "\u{307}" | // "\u{310}" | "\u{311}" | "\u{312}" | "\u{313}" | "\u{314}" | "\u{315}" | "\u{316}" | "\u{317}" | // "\u{320}" | "\u{321}" | "\u{322}" | "\u{323}" | "\u{324}" | "\u{325}" | "\u{326}" | "\u{327}" | // "\u{330}" | "\u{331}" | "\u{332}" | "\u{333}" | "\u{334}" | "\u{335}" | "\u{336}" | "\u{337}" | // "\u{340}" | "\u{341}" | "\u{342}" | "\u{343}" | "\u{344}" | "\u{345}" | "\u{346}" | "\u{347}" | // "\u{350}" | "\u{351}" | "\u{352}" | "\u{353}" | "\u{354}" | "\u{355}" | "\u{356}" | "\u{357}" | // "\u{360}" | "\u{361}" | "\u{362}" | "\u{363}" | "\u{364}" | "\u{365}" | "\u{366}" | "\u{367}" | // "\u{370}" | "\u{371}" | "\u{372}" | "\u{373}" | "\u{374}" | "\u{375}" | "\u{376}" | "\u{377}" // } // hex_entity = { "&#" ~ ("X"|"x") ~ ('0'..'9' | 'a'..'f' | 'A'..'F')+ ~ ";" } // dec_entity = { "&#" ~ ASCII_DIGIT+ ~ ";" } // char_entity = { "&" ~ ASCII_ALPHANUMERIC+ ~ ";" } // indent = _{ "\t" | " " } // indented_line = { indent ~ line } // optionally_indented_line = { indent? ~ line } // doctest_line = { ">>> " ~ raw_line } // line = _{ raw_line } // raw_line = _{ (!NEWLINE ~ ANY)* ~ NEWLINE | (!EOI ~ ANY)+ ~ EOI } // // Syntax extensions // extended_special_char = { // //&{ extension(EXT_SMART) } ~ // ("." | "-" | "\"" | "'") | // //&{ extension(EXT_NOTES) } ~ // "^" // } // smart = { // //&{ extension(EXT_SMART) } ~ // ( ellipsis | dash | single_quoted | double_quoted | apostrophe ) // } // apostrophe = { "'" } // ellipsis = { "..." | ". . ." } // dash = { em_dash | en_dash } // en_dash = { "-" ~ &ASCII_DIGIT } // em_dash = { "---" | "--" } // single_quote_start = { "'" ~ !(spacechar | NEWLINE) } // single_quote_end = { "'" ~ !alphanumeric } // single_quoted = { single_quote_start ~ ( !single_quote_end ~ inline )+ ~ single_quote_end } // double_quote_start = { "\"" } // double_quote_end = { "\"" } // double_quoted = { double_quote_start ~ ( !double_quote_end ~ inline )+ ~ double_quote_end } // definition = { // &( (!defmark ~ nonspacechar ~ raw_line) ~ blank_line? ~ defmark) ~ // d_list_title+ ~ // (def_tight | def_loose) // } // d_list_title = { !defmark ~ &nonspacechar ~ (!endline ~ inline)+ ~ sp ~ NEWLINE } // def_tight = { &defmark ~ list_tight } // def_loose = { blank_line ~ &defmark ~ list_loose } // defmark = { (":" | "~") ~ spacechar+ } // def_marker = { // //&{ extension(EXT_DLISTS) } ~ // defmark // } rst_parser-0.4.2/src/tests.rs000064400000000000000000000364711046102023000143250ustar 00000000000000use pest::consumes_to; use pest::parses_to; use crate::pest_rst::{RstParser, Rule}; #[test] fn plain() { parses_to! { parser: RstParser, input: "line\n", rule: Rule::paragraph, tokens: [ paragraph(0, 4, [ str(0, 4) ]) ] }; } #[test] fn emph_only() { parses_to! { parser: RstParser, input: "*emphasis*", rule: Rule::emph_outer, tokens: [ emph(1, 9, [str_nested(1, 9)]) ] }; } #[test] fn emph() { parses_to! { parser: RstParser, input: "line *with markup*\n", rule: Rule::paragraph, tokens: [ paragraph(0, 18, [ str(0, 5), emph(6, 17, [str_nested(6, 17)]), ]) ] }; } // TODO: escaped chars #[test] fn title() { parses_to! { parser: RstParser, input: "\ Title ===== ", rule: Rule::title, tokens: [ title(0, 12, [ title_single(0, 12, [ line(0, 6, [ str(0, 5) ]), adornments(6, 11), ]) ]) ] }; } #[test] fn title_overline() { parses_to! { parser: RstParser, input: "\ ----- Title ----- ", rule: Rule::title, tokens: [ title(0, 17, [ title_double(0, 17, [ adornments(0, 5), line(6, 12, [ str(6, 11) ]), ]) ]) ] }; } #[allow(clippy::cognitive_complexity)] #[test] fn two_targets() { parses_to! { parser: RstParser, input: "\ .. _a: http://example.com .. _`b_`: https://example.org ", rule: Rule::document, tokens: [ target(0, 26, [ target_name_uq(4, 5), link_target(7, 25), ]), target(26, 56, [ target_name_qu(31, 33), link_target(36, 55), ]), ] }; } #[test] fn footnote() { parses_to! { parser: RstParser, input: ".. [#] A\n", rule: Rule::footnote, tokens: [ footnote(0, 9, [ footnote_label(4, 5), line(7, 9, [ str(7, 8) ]), ]), ] }; } #[test] fn footnotes() { parses_to! { parser: RstParser, input: "\ .. [#] A .. [#b] B More .. [32] C ", rule: Rule::document, tokens: [ footnote(0, 9, [ footnote_label(4, 5), line(7, 9, [ str(7, 8) ]), ]), footnote(9, 28, [ footnote_label(13, 15), line(17, 19, [ str(17, 18) ]), paragraph(22, 26, [ str(22, 26) ]), ]), footnote(28, 38, [ footnote_label(32, 34), line(36, 38, [ str(36, 37) ]), ]), ] }; } #[test] fn footnote_refs() { parses_to! { parser: RstParser, input: "A-[#one]_ and [#]_ and [*]_.", rule: Rule::document, tokens: [ paragraph(0, 28, [ str(0, 2), footnote_reference(2, 9, [ footnote_label(3, 7) ]), str(9, 14), footnote_reference(14, 18, [ footnote_label(15, 16) ]), str(18, 23), footnote_reference(23, 27, [ footnote_label(24, 25) ]), str(27, 28), ]), ] }; } #[test] fn inline_code_literal_with_underscore() { parses_to! { parser: RstParser, input: "``NAME_WITH_UNDERSCORE``", rule: Rule::inline, tokens: [ literal(2, 22), ] }; } #[allow(clippy::cognitive_complexity)] #[test] fn admonitions() { parses_to! { parser: RstParser, input: "\ .. note:: Just next line .. admonition:: In line title Next line .. danger:: Just this line ", rule: Rule::document, tokens: [ admonition_gen(0, 28, [ admonition_type(3, 7), paragraph(13, 27, [ str(13, 27) ]), ]), admonition(28, 73, [ line(43, 58, [ str(43, 57) ]), paragraph(62, 71, [ str(62, 71) ]), ]), admonition_gen(73, 100, [ admonition_type(76, 82), line(84, 100, [ str(84, 99) ]), ]), ] }; } #[test] fn blockquote_no_attribution() { parses_to! { parser: RstParser, input: "\ Paragraph. Block quote. Paragraph. ", rule: Rule::document, tokens: [ paragraph(0, 10, [ str(0, 10) ]), block_quote(12, 29, [ paragraph(15, 27, [ str(15, 27) ]), ]), paragraph(29, 39, [ str(29, 39) ]), ] }; } #[test] fn blockquote_fake_attribution() { parses_to! { parser: RstParser, input: "\ Paragraph. -- Not an attribution Paragraph. ", rule: Rule::document, tokens: [ paragraph(0, 10, [ str(0, 10) ]), block_quote(12, 38, [ paragraph(15, 36, [ str(15, 36) ]), ]), paragraph(38, 48, [ str(38, 48) ]), ] }; } #[test] fn blockquote_simple() { parses_to! { parser: RstParser, input: "\ Paragraph. Block quote. -- Attribution Paragraph. ", rule: Rule::document, tokens: [ paragraph(0, 10, [ str(0, 10) ]), block_quote(12, 47, [ paragraph(15, 27, [ str(15, 27) ]), attribution(29, 47, [ line(35, 47, [ str(35, 46) ]) ]), ]), paragraph(48, 58, [ str(48, 58) ]), ] }; } #[test] fn blockquote_multiline_attribution() { parses_to! { parser: RstParser, input: "\ Paragraph. Block quote. --Attribution line one and line two Paragraph. ", rule: Rule::document, tokens: [ paragraph(0, 10, [ str(0, 10) ]), block_quote(12, 71, [ paragraph(15, 27, [ str(15, 27) ]), attribution(29, 71, [ line(34, 55, [ str(34, 54) ]), line(55, 71, [ str(55, 70) ]) ]), ]), paragraph(72, 82, [ str(72, 82) ]), ] }; } #[test] fn blockquote_multiline_attribution_2() { parses_to! { parser: RstParser, input: "\ Paragraph. -- Invalid attribution Block quote. -- Attribution line one and line two Paragraph. ", rule: Rule::document, tokens: [ paragraph(0, 10, [ str(0, 10) ]), block_quote(12, 102, [ paragraph(15, 37, [ str(15, 37) ]), paragraph(42, 54, [ str(42, 54) ]), attribution(56, 102, [ line(62, 83, [ str(62, 82) ]), line(83, 102, [ str(83, 101) ]) ]) ]), paragraph(103, 113, [ str(103, 113) ]), ] }; } #[test] fn blockquote_back_to_back() { parses_to! { parser: RstParser, input: "\ Paragraph. Block quote 1. -- Attribution 1 Block quote 2. --Attribution 2 Paragraph. ", rule: Rule::document, tokens: [ paragraph(0, 10, [ str(0, 10) ]), block_quote(12, 51, [ paragraph(15, 29, [ str(15, 29) ]), attribution(31, 51, [ line(37, 51, [ str(37, 50) ]) ]), ]), block_quote(52, 90, [ paragraph(55, 69, [ str(55, 69) ]), attribution(71, 90, [ line(76, 90, [ str(76, 89) ]) ]), ]), paragraph(91, 101, [ str(91, 101) ]), ] }; } #[test] fn block_quote_directive() { parses_to! { parser: RstParser, input: "\ .. epigraph:: Single line The end ", rule: Rule::document, tokens: [ block_quote_directive(0, 31, [ block_quote_type(3, 11), paragraph(18, 29, [ str(18, 29) ]), ]), paragraph(31, 38, [ str(31, 38) ]), ] }; } #[allow(clippy::cognitive_complexity)] #[test] fn literal_block() { parses_to! { parser: RstParser, input: "\ :: print('x') # second line The end ", rule: Rule::document, tokens: [ literal_block(0, 36, [ literal_lines(7, 36, [ literal_line(7, 18), literal_line_blank(18, 19), literal_line(22, 36), ]), ]), paragraph(37, 44, [ str(37, 44) ]), ] }; } #[allow(clippy::cognitive_complexity)] #[test] fn code_directive() { parses_to! { parser: RstParser, input: "\ .. code:: Single line .. code-block:: python print('x') # second line The end ", rule: Rule::document, tokens: [ code_directive(0, 26, [ literal_lines(14, 26, [ literal_line(14, 26) ]), ]), code_directive(27, 83, [ source(43, 49), literal_lines(54, 83, [ literal_line(54, 65), literal_line_blank(65, 66), literal_line(69, 83), ]), ]), paragraph(84, 91, [ str(84, 91) ]), ] }; } #[allow(clippy::cognitive_complexity)] #[test] fn raw() { parses_to! { parser: RstParser, input: "\ .. raw:: html hello world .. raw:: html hello
world

   parse
this The end ", rule: Rule::document, tokens: [ raw_directive(0, 43, [ raw_output_format(9, 13), raw_block(18, 43, [ raw_line(18, 43), ]), ]), raw_directive(44, 100, [ raw_output_format(53, 57), raw_block(62, 100, [ raw_line(62, 79), raw_line_blank(79, 80), raw_line(83, 100), ]), ]), paragraph(101, 108, [ str(101, 108) ]), ] }; } #[allow(clippy::cognitive_complexity)] #[test] fn comments() { parses_to! { parser: RstParser, input: "\ .. This is a comment .. This as well. .. _so: is this! .. [and] this! .. this:: too! .. |even| this:: ! .. With a title.. and a blank line... followed by a non-blank line and another one. .. .. Comments can also be run-in like this ", rule: Rule::document, tokens: [ block_comment(0, 22, [ comment_line(3, 21), ]), block_comment(22, 43, [ comment_line(28, 42), ]), block_comment(43, 63, [ comment_line(49, 63), ]), block_comment(63, 81, [ comment_line(69, 81), ]), block_comment(81, 99, [ comment_line(87, 99), ]), block_comment(99, 121, [ comment_line(105, 121), ]), block_comment(121, 216, [ comment_line(124, 139), comment_line_blank(139, 140), comment_line(143, 163), comment_line(166, 195), comment_line_blank(195, 196), comment_line(199, 216), ]), block_comment(216, 219), block_comment(219, 263, [ comment_line(222, 243), comment_line(246, 263), ]), ] }; } #[allow(clippy::cognitive_complexity)] #[test] fn substitutions() { parses_to! { parser: RstParser, input: "\ A |subst| in-line .. |subst| replace:: substitution .. |subst2| replace:: it can also be hanging ", rule: Rule::document, tokens: [ paragraph(0, 17, [ str(0, 2), substitution_name(3, 8), str(9, 17), ]), substitution_def(19, 52, [ substitution_name(23, 28), replace(30, 52, [ paragraph(40, 52, [str(40, 52)]) ]), ]), substitution_def(53, 101, [ substitution_name(57, 63), replace(65, 101, [ paragraph(75, 101, [ str(75, 86), ws_newline(86, 87), str(88, 100), ]) ]), ]), ] }; } #[test] fn substitution_in_literal() { parses_to! { parser: RstParser, input: "Just ``|code|``, really ``*code* |only|``", rule: Rule::document, tokens: [ paragraph(0, 41, [ str(0, 5), literal(7, 13), str(15, 24), literal(26, 39), ]), ] }; } #[allow(clippy::cognitive_complexity)] #[test] fn substitution_image() { parses_to! { parser: RstParser, input: "\ .. |subst| image:: thing.png :target: foo.html ", rule: Rule::document, tokens: [ substitution_def(0, 50, [ substitution_name(4, 9), image(11, 50, [ line(18, 29, [ str(18, 28) ]), image_option(32, 50, [ image_opt_name(33, 39), line(40, 50, [ str(40, 49) ]), ]), ]), ]), ] }; } #[allow(clippy::cognitive_complexity)] #[test] fn substitution_image_with_alt() { parses_to! { parser: RstParser, input: "\ .. |subst| image:: thing.png :alt: A thing :target: foo.html ", rule: Rule::document, tokens: [ substitution_def(0, 67, [ substitution_name(4, 9), image(11, 67, [ line(18, 29, [ str(18, 28) ]), image_option(32, 46, [ image_opt_name(33, 36), line(37, 46, [ str(37, 45) ]), ]), image_option(49, 67, [ image_opt_name(50, 56), line(57, 67, [ str(57, 66) ]), ]), ]), ]), ] }; } // TODO: test images #[allow(clippy::cognitive_complexity)] #[test] fn nested_lists() { parses_to! { parser: RstParser, input: "\ paragraph - item 1 - item 2 more text more text 2 more text 3 - nested item 1 - nested item 2 - nested item 3 ", rule: Rule::document, tokens: [ paragraph(0, 9, [ str(0, 9) ]), bullet_list(11, 131, [ bullet_item(11, 21, [ line(14, 21, [ str(14, 20) ]), ]), bullet_item(21, 131, [ line(24, 31, [ str(24, 30) ]), paragraph(34, 74, [ str(34, 43), ws_newline(43, 44), str(47, 58), ws_newline(58, 59), str(62, 73), ]), bullet_list(77, 131, [ bullet_item( 77, 93, [ line( 79, 93, [str( 79, 92)]) ]), bullet_item( 96, 112, [ line( 98, 112, [str( 98, 111)]) ]), bullet_item(115, 131, [ line(117, 131, [str(117, 130)]) ]), ]), ]), ]), ] } } rst_parser-0.4.2/src/token.rs000064400000000000000000000017651046102023000143010ustar 00000000000000//https://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#bullet-lists // *, +, -, •, ‣, ⁃ pub enum BulletListType { Ast, Plus, Minus, Bullet, TriBullet, HyphenBullet, } // 1, A, a, I, i pub enum EnumListChar { Arabic, AlphaUpper, AlphaLower, RomanUpper, RomanLower, Auto, } // 1., (1), 1) pub enum EnumListType { Period, ParenEnclosed, Paren, } // ! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~ pub enum AdornmentChar { Bang, DQuote, Hash, Dollar, Percent, Amp, SQuote, LParen, RParen, Ast, Plus, Comma, Minus, Period, Slash, Colon, Semicolon, Less, Eq, More, Question, At, LBrack, Backslash, RBrack, Caret, Underscore, Backtick, LBrace, Pipe, RBrace, Tilde, } // [1], [#], [*], [#foo] pub enum FootnoteType { Numbered(usize), AutoNumber, AutoSymbol, AutoNamed(String), } rst_parser-0.4.2/src/transforms/standard.rs000064400000000000000000000406421046102023000171540ustar 00000000000000/*! Perform standard transforms. * * Hyperlinks * ---------- * * See * * Links can have internal or external targets. * In the source, targets look like: * * ```restructuredtext * .. targetname1: * .. targetname2: * * some paragraph or list item or so * ``` * * or: * * ```restructuredtext * .. targetname1: * .. targetname2: https://link * ``` * * There’s also anonymous links and targets without names. * * TODO: continue documenting how it’s done via * * Footnotes * --------- * * See * * Footnotes can be numbered or symbolic. * In the source, they are split into two parts: footnote references and footnotes. * * Their order is defined by the order of the footnotes, not references. */ use std::{collections::HashMap, iter::once, num::NonZero, vec}; use document_tree::{ Document, HasChildren, LabelledFootnote as _, attribute_types::{AutoFootnoteType, ID, NameToken}, element_categories as c, elements::{self as e, Element}, extra_attributes::{ExtraAttributes, FootnoteType}, url::Url, }; use super::{Transform, Visit}; #[must_use] pub fn standard_transform(doc: Document) -> Document { let mut pass1 = Pass1::default(); let doc = pass1.transform(doc); let mut pass2 = Pass2::from(&pass1); pass2.visit(&doc); Pass3::from(&pass2).transform(doc) } #[derive(Debug)] #[allow(dead_code)] enum NamedTargetType { Citation, InternalLink, ExternalLink(Url), IndirectLink(NameToken), SectionTitle, } impl NamedTargetType { #[allow(dead_code)] /// See fn is_implicit_target(&self) -> bool { use NamedTargetType as T; matches!(self, T::SectionTitle | T::Citation) } } const ONE: NonZero = NonZero::::MIN; /// Pass 1: Number footnotes, and add IDs to footnote references and footnotes. /// /// Needs to be separate pass, since resolving `refid`s for footnote references requires already-assigned footnote numbers. /// Therefore, we do that here, then (in pass 2) resolve references, and finally (in pass 3) transform the footnotes. #[derive(Default, Debug)] struct Pass1 { /// Store numbers for symbolic footnotes. They can only be in order, so `_.values().sort() == 1..=_.len()` footnotes_symbol: HashMap>, /// Store numbers for numbered footnotes. They can have gaps due to explicitly numbered ones. footnotes_number: HashMap>, /// Numbers of anonymous footnotes in order of appearance. auto_numbered_anon_footnotes: Vec>, /// Numbers of named footnotes in order of appearance. auto_numbered_named_footnotes: Vec>, /// Number of encountered anonymous footnotes. Only used for ID generation. n_anon_footnotes: usize, /// Number of encountered footnote references. Only used for ID generation. n_footnote_refs: usize, } impl Pass1 { /// Get next footnote number for a type. /// /// See fn next_footnote(&mut self, typ: AutoFootnoteType) -> NonZero { match typ { AutoFootnoteType::Number => { let Some(n) = NonZero::new(self.footnotes_number.len()) else { return ONE; }; let mut ordered: Vec<_> = self.footnotes_number.values().copied().collect(); ordered.sort_unstable(); ordered .iter() .copied() .zip(1usize..) // https://github.com/rust-lang/rust/pull/127534 .enumerate() .find_map(|(i, (n1, n2))| (n1.get() != n2).then_some(ONE.saturating_add(i))) .unwrap_or(n) } AutoFootnoteType::Symbol => { if cfg!(debug_assertions) { let mut vals: Vec = self .footnotes_symbol .values() .copied() .map(Into::into) .collect(); vals.sort_unstable(); assert_eq!(vals, (1..=self.footnotes_symbol.len()).collect::>()); } ONE.saturating_add(self.footnotes_symbol.len()) } } } } impl Transform for Pass1 { /// Add (auto-)id and running count to “ids” of footnotes fn transform_footnote(&mut self, mut e: e::Footnote) -> impl Iterator { // Get next or stored footnote number let n = match e .extra() .auto .map(|t| self.next_footnote(t)) .ok_or(()) .or_else::(|()| Ok(e.get_label()?.parse()?)) { Ok(n) => n, Err(err) => { let t = e::Problematic::with_children(vec![err.to_string().into()]).into(); return once(e::Paragraph::with_children(vec![t]).into()); } }; // Get ID from name or create one from the running count let id = if let Some(name) = e.names().first() { name.0.as_str().into() } else { self.n_anon_footnotes += 1; ID(format!("footnote-{}", self.n_anon_footnotes)) }; e.ids_mut().push(id.clone()); // Add footnote to the correct mapping if e.is_symbol() { self.footnotes_symbol.insert(id.clone(), n); } else { self.footnotes_number.insert(id.clone(), n); } // Keep track of named vs anonymous footnotes for auto-numbering refs later if matches!(e.extra().auto, Some(AutoFootnoteType::Number)) { if e.names().is_empty() { self.auto_numbered_anon_footnotes.push(n); } else { self.auto_numbered_named_footnotes.push(n); } } // Standard transform self.transform_children(&mut e, Self::transform_sub_footnote); once(e.into()) } /// Give each reference an ID. We don’t need to do more. fn transform_footnote_reference( &mut self, mut e: e::FootnoteReference, ) -> impl Iterator { // Add running count ID self.n_footnote_refs += 1; e.ids_mut() .push(ID(format!("footnote-reference-{}", self.n_footnote_refs))); // Standard transform self.transform_children(&mut e, Self::transform_text_or_inline_element); once(e.into()) } } #[derive(Clone, Debug)] struct Substitution { content: Vec, /// If true and the sibling before the reference is a text node, /// the text node gets right-trimmed. ltrim: bool, /// Same as `ltrim` with the sibling after the reference. rtrim: bool, } #[derive(Debug)] struct Pass2<'p1> { pass1: &'p1 Pass1, named_targets: HashMap, substitutions: HashMap, normalized_substitutions: HashMap, /// Footnote references that reference symbol footnotes. symbol_footnote_refs: HashMap>, /// Footnote references that reference numbered footnotes. /// Multiple can point to the same number. numbered_footnote_refs: HashMap>, /// Number of symbol footnote references. n_symbol_footnote_refs: usize, /// Number of anonymous numbered footnote references. n_numbered_anon_footnote_refs: usize, /// Number of named numbered footnote references. n_numbered_named_footnote_refs: usize, } impl<'p1> From<&'p1 Pass1> for Pass2<'p1> { fn from(pass1: &'p1 Pass1) -> Self { Self { pass1, named_targets: HashMap::new(), substitutions: HashMap::new(), normalized_substitutions: HashMap::new(), symbol_footnote_refs: HashMap::new(), numbered_footnote_refs: HashMap::new(), n_symbol_footnote_refs: 0, n_numbered_anon_footnote_refs: 0, n_numbered_named_footnote_refs: 0, } } } /// Pass 2. /// /// - Populate substitution definitions. /// - Populate (link) targets. /// - Resolve which footnotes are referenced by footnote references. impl<'tree> Visit<'tree> for Pass2<'_> { fn visit_substitution_definition(&mut self, e: &'tree e::SubstitutionDefinition) { let subst = Substitution { content: e.children().clone(), ltrim: e.extra().ltrim, rtrim: e.extra().rtrim, }; for name in e.names() { if self.substitutions.contains_key(name) { // TODO: Duplicate substitution name (level 3 system message). } // Intentionally overriding any previous values. self.substitutions.insert(name.clone(), subst.clone()); self.normalized_substitutions .insert(name.0.to_lowercase(), subst.clone()); } } fn visit_target(&mut self, e: &'tree e::Target) { if let Some(uri) = &e.extra().refuri { for name in e.names() { self.named_targets .insert(name.clone(), NamedTargetType::ExternalLink(uri.clone())); } } // TODO: as is, people can only refer to the target directly containing the URL. // add refid and refnames to some HashMap and follow those later. } fn visit_footnote_reference(&mut self, e: &'tree e::FootnoteReference) { let id = e.ids().first().unwrap(); let name = e.names().first(); let n = match e.extra().auto { Some(AutoFootnoteType::Symbol) => { self.n_symbol_footnote_refs += 1; NonZero::new(self.n_symbol_footnote_refs).unwrap() } Some(AutoFootnoteType::Number) => { if name.is_some() { self.n_numbered_named_footnote_refs += 1; self.pass1.auto_numbered_named_footnotes [self.n_numbered_named_footnote_refs - 1] } else { self.n_numbered_anon_footnote_refs += 1; self.pass1.auto_numbered_anon_footnotes[self.n_numbered_anon_footnote_refs - 1] } } None => e.get_label().unwrap().parse().unwrap(), }; if e.is_symbol() { self.symbol_footnote_refs.insert(id.clone(), n); } else { self.numbered_footnote_refs.insert(id.clone(), n); } for c in e.children() { self.visit_text_or_inline_element(c); } } } #[derive(Debug)] struct Pass3<'p1, 'p2: 'p1>(&'p2 Pass2<'p1>); impl<'p2> Pass3<'_, 'p2> { fn target_url<'t>(self: &'t Pass3<'_, 'p2>, refname: &[NameToken]) -> Option<&'t Url> { // TODO: Check if the target would expand circularly assert!( refname.len() == 1, "Expected exactly one name in a reference." ); let name = refname[0].clone(); match self.0.named_targets.get(&name)? { NamedTargetType::ExternalLink(url) => Some(url), _ => unimplemented!(), } } fn substitution<'t>( self: &'t Pass3<'_, 'p2>, refname: &[NameToken], ) -> Option<&'t Substitution> { // TODO: Check if the substitution would expand circularly assert!( refname.len() == 1, "Expected exactly one name in a substitution reference." ); let name = refname[0].clone(); self.0 .substitutions .get(&name) .or_else(|| self.0.normalized_substitutions.get(&name.0.to_lowercase())) } } impl<'p1, 'p2: 'p1> From<&'p2 Pass2<'p1>> for Pass3<'p1, 'p2> { fn from(p: &'p2 Pass2<'p1>) -> Self { Pass3(p) } } /// 3rd pass. impl Transform for Pass3<'_, '_> { fn transform_substitution_definition( &mut self, _: e::SubstitutionDefinition, ) -> impl Iterator { None.into_iter() } fn transform_substitution_reference( &mut self, e: e::SubstitutionReference, ) -> impl Iterator { let r: Box> = if let Some(Substitution { content, ltrim, rtrim, }) = self.substitution(&e.extra().refname) { // (level 3 system message). // TODO: ltrim and rtrim. if *ltrim || *rtrim { dbg!(content, ltrim, rtrim); } Box::new(content.clone().into_iter()) } else { // Undefined substitution name (level 3 system message). // TODO: This replaces the reference by a Problematic node. // The corresponding SystemMessage node should go in a generated // section with class "system-messages" at the end of the document. let mut replacement: Box = Box::default(); replacement .children_mut() .push(c::TextOrInlineElement::String(Box::new(format!( "|{}|", e.extra().refname[0].0 )))); // TODO: Create an ID for replacement for the system_message to reference. // TODO: replacement.refid pointing to the system_message. Box::new(once(c::TextOrInlineElement::Problematic(replacement))) }; r } fn transform_reference( &mut self, mut e: e::Reference, ) -> impl Iterator { if e.extra().refuri.is_none() { if let Some(uri) = self.target_url(&e.extra().refname) { e.extra_mut().refuri = Some(uri.clone()); } } once(e.into()) } fn transform_footnote(&mut self, mut e: e::Footnote) -> impl Iterator { /* TODO: https://docutils.sourceforge.io/docs/ref/doctree.html#footnote-reference 1. see above 2. (in resolve_refs) set `footnote_reference[refid]`s, `footnote[backref]`s and `footnote>label` */ let id = e.ids().first().unwrap(); let id2num = if e.is_symbol() { &self.0.pass1.footnotes_symbol } else { &self.0.pass1.footnotes_number }; let num = id2num.get(id).unwrap(); if e.get_label().is_err() { e.children_mut().insert( 0, e::Label::with_children(vec![num.to_string().into()]).into(), ); } // backrefs let refid2num = if e.is_symbol() { &self.0.symbol_footnote_refs } else { &self.0.numbered_footnote_refs }; e.extra_mut().backrefs = refid2num .iter() .filter(|&(_, num2)| num == num2) .map(|(refid, _)| refid.clone()) .collect(); // standard transform self.transform_children(&mut e, Self::transform_sub_footnote); once(e.into()) } fn transform_footnote_reference( &mut self, mut e: e::FootnoteReference, ) -> impl Iterator { // TODO: dedupe // https://docutils.sourceforge.io/docs/ref/doctree.html#footnote-reference let refid = e.ids().first().unwrap(); let refid2num = if e.is_symbol() { &self.0.symbol_footnote_refs } else { &self.0.numbered_footnote_refs }; let n = refid2num.get(refid).unwrap(); // get referenced footnote ID let footnote2num = if e.is_symbol() { &self.0.pass1.footnotes_symbol } else { &self.0.pass1.footnotes_number }; let num2footnote: HashMap<_, _> = footnote2num.iter().map(|(k, v)| (*v, k.clone())).collect(); e.extra_mut().refid = num2footnote.get(n).cloned(); // add label if e.get_label().is_err() { e.children_mut().insert(0, n.to_string().into()); } // standard transform self.transform_children(&mut e, Self::transform_text_or_inline_element); once(e.into()) } } rst_parser-0.4.2/src/transforms/transform.rs000064400000000000000000001110321046102023000173570ustar 00000000000000use std::iter::once; use document_tree::HasChildren; use document_tree::element_categories as c; use document_tree::elements as e; /// Helper trait for [`Transform::transform_children`] while [Fn traits] are not stable yet. /// /// See . /// /// [Fn traits]: https://github.com/rust-lang/rust/issues/29625 pub trait IteratorMaker: FnMut(This, C) -> Self::Iter { type Iter: Iterator; } impl IteratorMaker for F where F: ?Sized + FnMut(This, C) -> Iter, Iter: Iterator, { type Iter = Iter; } #[inline] fn box_iter<'a, I>(i: impl Iterator + 'a) -> Box + 'a> { Box::new(i) } /// Transform a document tree. /// /// Override individual methods to modify the document tree. /// By default, every method transforms an element’s children (if applicable) and then returns the element. pub trait Transform { /// Transform children of an element. /// /// Can be used in other `transform_` methods to recurse, e.g.: /// /// ```rust /// use document_tree::elements as e; /// use document_tree::element_categories as c; /// use rst_parser::transforms::Transform; /// /// struct MyPass; /// impl Transform for MyPass { /// fn transform_header(&mut self, mut e: e::Header) -> impl Iterator { /// self.transform_children(&mut e, Self::transform_body_element); /// std::iter::once(e.into()) /// } /// } /// ``` fn transform_children( &mut self, e: &mut E, mut meth: impl for<'a> IteratorMaker<&'a mut Self, C>, ) where E: HasChildren, { let mut new = Vec::new(); for c in e.children_mut().drain(..) { new.extend(meth(self, c)); } e.children_mut().extend(new); } /// Transform a whole document tree. #[must_use] fn transform(&mut self, mut d: e::Document) -> e::Document { self.transform_children(&mut d, Self::transform_structural_sub_element); d } //////////////// // categories // //////////////// #[must_use] fn transform_structural_sub_element( &mut self, c: c::StructuralSubElement, ) -> impl Iterator { use c::StructuralSubElement as S; match c { S::Title(e) => box_iter(self.transform_title(*e).map(Into::into)), S::Subtitle(e) => box_iter(self.transform_subtitle(*e).map(S::from)), S::Decoration(e) => box_iter(self.transform_decoration(*e)), S::Docinfo(e) => box_iter(self.transform_docinfo(*e)), S::SubStructure(e) => box_iter(self.transform_substructure(*e).map(S::from)), } } #[must_use] fn transform_substructure( &mut self, c: c::SubStructure, ) -> impl Iterator { use c::SubStructure as S; match c { S::Topic(e) => box_iter(self.transform_topic(*e).map(Into::into)), S::Sidebar(e) => box_iter(self.transform_sidebar(*e)), S::Transition(e) => box_iter(self.transform_transition(*e)), S::Section(e) => box_iter(self.transform_section(*e)), S::BodyElement(e) => box_iter(self.transform_body_element(*e).map(Into::into)), } } #[must_use] fn transform_body_element( &mut self, c: c::BodyElement, ) -> impl Iterator { use c::BodyElement as B; match c { B::Paragraph(e) => box_iter(self.transform_paragraph(*e)), B::LiteralBlock(e) => box_iter(self.transform_literal_block(*e)), B::DoctestBlock(e) => box_iter(self.transform_doctest_block(*e)), B::MathBlock(e) => box_iter(self.transform_math_block(*e)), B::Rubric(e) => box_iter(self.transform_rubric(*e)), B::SubstitutionDefinition(e) => box_iter(self.transform_substitution_definition(*e)), B::Comment(e) => box_iter(self.transform_comment(*e)), B::Pending(e) => box_iter(self.transform_pending(*e)), B::Target(e) => box_iter(self.transform_target(*e)), B::Raw(e) => box_iter(self.transform_raw(*e)), B::Image(e) => box_iter(self.transform_image(*e)), B::Compound(e) => box_iter(self.transform_compound(*e)), B::Container(e) => box_iter(self.transform_container(*e)), B::BulletList(e) => box_iter(self.transform_bullet_list(*e)), B::EnumeratedList(e) => box_iter(self.transform_enumerated_list(*e)), B::DefinitionList(e) => box_iter(self.transform_definition_list(*e)), B::FieldList(e) => box_iter(self.transform_field_list(*e)), B::OptionList(e) => box_iter(self.transform_option_list(*e)), B::LineBlock(e) => box_iter(self.transform_line_block(*e).map(Into::into)), B::BlockQuote(e) => box_iter(self.transform_block_quote(*e)), B::Admonition(e) => box_iter(self.transform_admonition(*e)), B::Attention(e) => box_iter(self.transform_attention(*e)), B::Hint(e) => box_iter(self.transform_hint(*e)), B::Note(e) => box_iter(self.transform_note(*e)), B::Caution(e) => box_iter(self.transform_caution(*e)), B::Danger(e) => box_iter(self.transform_danger(*e)), B::Error(e) => box_iter(self.transform_error(*e)), B::Important(e) => box_iter(self.transform_important(*e)), B::Tip(e) => box_iter(self.transform_tip(*e)), B::Warning(e) => box_iter(self.transform_warning(*e)), B::Footnote(e) => box_iter(self.transform_footnote(*e)), B::Citation(e) => box_iter(self.transform_citation(*e)), B::SystemMessage(e) => box_iter(self.transform_system_message(*e)), B::Figure(e) => box_iter(self.transform_figure(*e)), B::Table(e) => box_iter(self.transform_table(*e)), } } #[must_use] fn transform_bibliographic_element( &mut self, c: c::BibliographicElement, ) -> impl Iterator { use c::BibliographicElement as B; match c { B::Authors(e) => box_iter(self.transform_authors(*e)), B::Author(e) => box_iter(self.transform_author(*e).map(Into::into)), B::Organization(e) => box_iter(self.transform_organization(*e).map(Into::into)), B::Address(e) => box_iter(self.transform_address(*e).map(Into::into)), B::Contact(e) => box_iter(self.transform_contact(*e).map(Into::into)), B::Version(e) => box_iter(self.transform_version(*e)), B::Revision(e) => box_iter(self.transform_revision(*e)), B::Status(e) => box_iter(self.transform_status(*e)), B::Date(e) => box_iter(self.transform_date(*e)), B::Copyright(e) => box_iter(self.transform_copyright(*e)), B::Field(e) => box_iter(self.transform_field(*e).map(Into::into)), } } #[must_use] fn transform_text_or_inline_element( &mut self, c: c::TextOrInlineElement, ) -> impl Iterator { use c::TextOrInlineElement as T; match c { T::String(e) => box_iter(self.transform_string(*e).map(Into::into)), T::Emphasis(e) => box_iter(self.transform_emphasis(*e)), T::Strong(e) => box_iter(self.transform_strong(*e)), T::Literal(e) => box_iter(self.transform_literal(*e)), T::Reference(e) => box_iter(self.transform_reference(*e)), T::FootnoteReference(e) => box_iter(self.transform_footnote_reference(*e)), T::CitationReference(e) => box_iter(self.transform_citation_reference(*e)), T::SubstitutionReference(e) => box_iter(self.transform_substitution_reference(*e)), T::TitleReference(e) => box_iter(self.transform_title_reference(*e)), T::Abbreviation(e) => box_iter(self.transform_abbreviation(*e)), T::Acronym(e) => box_iter(self.transform_acronym(*e)), T::Superscript(e) => box_iter(self.transform_superscript(*e)), T::Subscript(e) => box_iter(self.transform_subscript(*e)), T::Inline(e) => box_iter(self.transform_inline(*e)), T::Problematic(e) => box_iter(self.transform_problematic(*e)), T::Generated(e) => box_iter(self.transform_generated(*e)), T::Math(e) => box_iter(self.transform_math(*e)), T::TargetInline(e) => box_iter(self.transform_target_inline(*e)), T::RawInline(e) => box_iter(self.transform_raw_inline(*e)), T::ImageInline(e) => box_iter(self.transform_image_inline(*e)), } } #[must_use] fn transform_author_info(&mut self, c: c::AuthorInfo) -> impl Iterator { use c::AuthorInfo as A; match c { A::Author(e) => box_iter(self.transform_author(*e)), A::Organization(e) => box_iter(self.transform_organization(*e)), A::Address(e) => box_iter(self.transform_address(*e)), A::Contact(e) => box_iter(self.transform_contact(*e)), } } #[must_use] fn transform_decoration_element( &mut self, c: c::DecorationElement, ) -> impl Iterator { use c::DecorationElement as D; match c { D::Header(e) => box_iter(self.transform_header(*e)), D::Footer(e) => box_iter(self.transform_footer(*e)), } } #[must_use] fn transform_sub_topic(&mut self, c: c::SubTopic) -> impl Iterator { use c::SubTopic as S; match c { S::Title(e) => box_iter(self.transform_title(*e).map(Into::into)), S::BodyElement(e) => box_iter(self.transform_body_element(*e).map(Into::into)), } } #[must_use] fn transform_sub_sidebar(&mut self, c: c::SubSidebar) -> impl Iterator { use c::SubSidebar as S; match c { S::Topic(e) => box_iter(self.transform_topic(*e).map(Into::into)), S::Title(e) => box_iter(self.transform_title(*e).map(Into::into)), S::Subtitle(e) => box_iter(self.transform_subtitle(*e)), S::BodyElement(e) => box_iter(self.transform_body_element(*e).map(Into::into)), } } #[must_use] fn transform_sub_dl_item(&mut self, c: c::SubDLItem) -> impl Iterator { use c::SubDLItem as S; match c { S::Term(e) => box_iter(self.transform_term(*e)), S::Classifier(e) => box_iter(self.transform_classifier(*e)), S::Definition(e) => box_iter(self.transform_definition(*e)), } } #[must_use] fn transform_sub_field(&mut self, c: c::SubField) -> impl Iterator { use c::SubField as S; match c { S::FieldName(e) => box_iter(self.transform_field_name(*e)), S::FieldBody(e) => box_iter(self.transform_field_body(*e)), } } #[must_use] fn transform_sub_option_list_item( &mut self, c: c::SubOptionListItem, ) -> impl Iterator { use c::SubOptionListItem as S; match c { S::OptionGroup(e) => box_iter(self.transform_option_group(*e)), S::Description(e) => box_iter(self.transform_description(*e)), } } #[must_use] fn transform_sub_option(&mut self, c: c::SubOption) -> impl Iterator { use c::SubOption as S; match c { S::OptionString(e) => box_iter(self.transform_option_string(*e)), S::OptionArgument(e) => box_iter(self.transform_option_argument(*e)), } } #[must_use] fn transform_sub_line_block( &mut self, c: c::SubLineBlock, ) -> impl Iterator { use c::SubLineBlock as S; match c { S::LineBlock(e) => box_iter(self.transform_line_block(*e).map(Into::into)), S::Line(e) => box_iter(self.transform_line(*e)), } } #[must_use] fn transform_sub_block_quote( &mut self, c: c::SubBlockQuote, ) -> impl Iterator { use c::SubBlockQuote as S; match c { S::Attribution(e) => box_iter(self.transform_attribution(*e)), S::BodyElement(e) => box_iter(self.transform_body_element(*e).map(Into::into)), } } #[must_use] fn transform_sub_footnote( &mut self, c: c::SubFootnote, ) -> impl Iterator { use c::SubFootnote as S; match c { S::Label(e) => box_iter(self.transform_label(*e)), S::BodyElement(e) => box_iter(self.transform_body_element(*e).map(Into::into)), } } #[must_use] fn transform_sub_figure(&mut self, c: c::SubFigure) -> impl Iterator { use c::SubFigure as S; match c { S::Caption(e) => box_iter(self.transform_caption(*e)), S::Legend(e) => box_iter(self.transform_legend(*e)), S::BodyElement(e) => box_iter(self.transform_body_element(*e).map(Into::into)), } } #[must_use] fn transform_sub_table(&mut self, c: c::SubTable) -> impl Iterator { use c::SubTable as S; match c { S::Title(e) => box_iter(self.transform_title(*e).map(Into::into)), S::TableGroup(e) => box_iter(self.transform_table_group(*e)), } } #[must_use] fn transform_sub_table_group( &mut self, c: c::SubTableGroup, ) -> impl Iterator { use c::SubTableGroup as S; match c { S::TableColspec(e) => box_iter(self.transform_table_colspec(*e)), S::TableHead(e) => box_iter(self.transform_table_head(*e)), S::TableBody(e) => box_iter(self.transform_table_body(*e)), } } ////////////// // elements // ////////////// //structual elements #[must_use] fn transform_section(&mut self, mut e: e::Section) -> impl Iterator { self.transform_children(&mut e, Self::transform_structural_sub_element); once(e.into()) } #[must_use] // TODO: introduce and return category for topic|bodyelement fn transform_topic(&mut self, mut e: e::Topic) -> impl Iterator { self.transform_children(&mut e, Self::transform_sub_topic); once(e) } #[must_use] fn transform_sidebar(&mut self, mut e: e::Sidebar) -> impl Iterator { self.transform_children(&mut e, Self::transform_sub_sidebar); once(e.into()) } //structural subelements #[must_use] fn transform_title(&mut self, mut e: e::Title) -> impl Iterator { self.transform_children(&mut e, Self::transform_text_or_inline_element); once(e) } #[must_use] fn transform_subtitle(&mut self, mut e: e::Subtitle) -> impl Iterator { self.transform_children(&mut e, Self::transform_text_or_inline_element); once(e.into()) } #[must_use] fn transform_decoration( &mut self, mut e: e::Decoration, ) -> impl Iterator { self.transform_children(&mut e, Self::transform_decoration_element); once(e.into()) } #[must_use] fn transform_docinfo( &mut self, mut e: e::Docinfo, ) -> impl Iterator { self.transform_children(&mut e, Self::transform_bibliographic_element); once(e.into()) } #[must_use] fn transform_transition(&mut self, e: e::Transition) -> impl Iterator { once(e.into()) } //bibliographic elements #[must_use] fn transform_authors( &mut self, mut e: e::Authors, ) -> impl Iterator { self.transform_children(&mut e, Self::transform_author_info); once(e.into()) } #[must_use] fn transform_author(&mut self, mut e: e::Author) -> impl Iterator { self.transform_children(&mut e, Self::transform_text_or_inline_element); once(e.into()) } #[must_use] fn transform_organization( &mut self, mut e: e::Organization, ) -> impl Iterator { self.transform_children(&mut e, Self::transform_text_or_inline_element); once(e.into()) } #[must_use] fn transform_address(&mut self, mut e: e::Address) -> impl Iterator { self.transform_children(&mut e, Self::transform_text_or_inline_element); once(e.into()) } #[must_use] fn transform_contact(&mut self, mut e: e::Contact) -> impl Iterator { self.transform_children(&mut e, Self::transform_text_or_inline_element); once(e.into()) } #[must_use] fn transform_version( &mut self, mut e: e::Version, ) -> impl Iterator { self.transform_children(&mut e, Self::transform_text_or_inline_element); once(e.into()) } #[must_use] fn transform_revision( &mut self, mut e: e::Revision, ) -> impl Iterator { self.transform_children(&mut e, Self::transform_text_or_inline_element); once(e.into()) } #[must_use] fn transform_status( &mut self, mut e: e::Status, ) -> impl Iterator { self.transform_children(&mut e, Self::transform_text_or_inline_element); once(e.into()) } #[must_use] fn transform_date(&mut self, mut e: e::Date) -> impl Iterator { self.transform_children(&mut e, Self::transform_text_or_inline_element); once(e.into()) } #[must_use] fn transform_copyright( &mut self, mut e: e::Copyright, ) -> impl Iterator { self.transform_children(&mut e, Self::transform_text_or_inline_element); once(e.into()) } #[must_use] fn transform_field(&mut self, mut e: e::Field) -> impl Iterator { self.transform_children(&mut e, Self::transform_sub_field); once(e) } //decoration elements #[must_use] fn transform_header(&mut self, mut e: e::Header) -> impl Iterator { self.transform_children(&mut e, Self::transform_body_element); once(e.into()) } #[must_use] fn transform_footer(&mut self, mut e: e::Footer) -> impl Iterator { self.transform_children(&mut e, Self::transform_body_element); once(e.into()) } //simple body elements #[must_use] fn transform_paragraph(&mut self, mut e: e::Paragraph) -> impl Iterator { self.transform_children(&mut e, Self::transform_text_or_inline_element); once(e.into()) } #[must_use] fn transform_literal_block( &mut self, mut e: e::LiteralBlock, ) -> impl Iterator { self.transform_children(&mut e, Self::transform_text_or_inline_element); once(e.into()) } #[must_use] fn transform_doctest_block( &mut self, mut e: e::DoctestBlock, ) -> impl Iterator { self.transform_children(&mut e, Self::transform_text_or_inline_element); once(e.into()) } #[must_use] fn transform_math_block( &mut self, mut e: e::MathBlock, ) -> impl Iterator { self.transform_children(&mut e, Self::transform_string); once(e.into()) } #[must_use] fn transform_rubric(&mut self, mut e: e::Rubric) -> impl Iterator { self.transform_children(&mut e, Self::transform_text_or_inline_element); once(e.into()) } #[must_use] fn transform_substitution_definition( &mut self, mut e: e::SubstitutionDefinition, ) -> impl Iterator { self.transform_children(&mut e, Self::transform_text_or_inline_element); once(e.into()) } #[must_use] fn transform_comment(&mut self, mut e: e::Comment) -> impl Iterator { self.transform_children(&mut e, Self::transform_text_or_inline_element); once(e.into()) } #[must_use] fn transform_pending(&mut self, e: e::Pending) -> impl Iterator { once(e.into()) } #[must_use] fn transform_target(&mut self, e: e::Target) -> impl Iterator { once(e.into()) } #[must_use] fn transform_raw(&mut self, mut e: e::Raw) -> impl Iterator { self.transform_children(&mut e, Self::transform_string); once(e.into()) } #[must_use] fn transform_image(&mut self, e: e::Image) -> impl Iterator { once(e.into()) } //compound body elements #[must_use] fn transform_compound(&mut self, mut e: e::Compound) -> impl Iterator { self.transform_children(&mut e, Self::transform_body_element); once(e.into()) } #[must_use] fn transform_container(&mut self, mut e: e::Container) -> impl Iterator { self.transform_children(&mut e, Self::transform_body_element); once(e.into()) } #[must_use] fn transform_bullet_list( &mut self, mut e: e::BulletList, ) -> impl Iterator { self.transform_children(&mut e, Self::transform_list_item); once(e.into()) } #[must_use] fn transform_enumerated_list( &mut self, mut e: e::EnumeratedList, ) -> impl Iterator { self.transform_children(&mut e, Self::transform_list_item); once(e.into()) } #[must_use] fn transform_definition_list( &mut self, mut e: e::DefinitionList, ) -> impl Iterator { self.transform_children(&mut e, Self::transform_definition_list_item); once(e.into()) } #[must_use] fn transform_field_list( &mut self, mut e: e::FieldList, ) -> impl Iterator { self.transform_children(&mut e, Self::transform_field); once(e.into()) } #[must_use] fn transform_option_list( &mut self, mut e: e::OptionList, ) -> impl Iterator { self.transform_children(&mut e, Self::transform_option_list_item); once(e.into()) } #[must_use] fn transform_line_block(&mut self, mut e: e::LineBlock) -> impl Iterator { self.transform_children(&mut e, Self::transform_sub_line_block); once(e) } #[must_use] fn transform_block_quote( &mut self, mut e: e::BlockQuote, ) -> impl Iterator { self.transform_children(&mut e, Self::transform_sub_block_quote); once(e.into()) } #[must_use] fn transform_admonition( &mut self, mut e: e::Admonition, ) -> impl Iterator { self.transform_children(&mut e, Self::transform_sub_topic); once(e.into()) } #[must_use] fn transform_attention(&mut self, mut e: e::Attention) -> impl Iterator { self.transform_children(&mut e, Self::transform_body_element); once(e.into()) } #[must_use] fn transform_hint(&mut self, mut e: e::Hint) -> impl Iterator { self.transform_children(&mut e, Self::transform_body_element); once(e.into()) } #[must_use] fn transform_note(&mut self, mut e: e::Note) -> impl Iterator { self.transform_children(&mut e, Self::transform_body_element); once(e.into()) } #[must_use] fn transform_caution(&mut self, mut e: e::Caution) -> impl Iterator { self.transform_children(&mut e, Self::transform_body_element); once(e.into()) } #[must_use] fn transform_danger(&mut self, mut e: e::Danger) -> impl Iterator { self.transform_children(&mut e, Self::transform_body_element); once(e.into()) } #[must_use] fn transform_error(&mut self, mut e: e::Error) -> impl Iterator { self.transform_children(&mut e, Self::transform_body_element); once(e.into()) } #[must_use] fn transform_important(&mut self, mut e: e::Important) -> impl Iterator { self.transform_children(&mut e, Self::transform_body_element); once(e.into()) } #[must_use] fn transform_tip(&mut self, mut e: e::Tip) -> impl Iterator { self.transform_children(&mut e, Self::transform_body_element); once(e.into()) } #[must_use] fn transform_warning(&mut self, mut e: e::Warning) -> impl Iterator { self.transform_children(&mut e, Self::transform_body_element); once(e.into()) } #[must_use] fn transform_footnote(&mut self, mut e: e::Footnote) -> impl Iterator { self.transform_children(&mut e, Self::transform_sub_footnote); once(e.into()) } #[must_use] fn transform_citation(&mut self, mut e: e::Citation) -> impl Iterator { self.transform_children(&mut e, Self::transform_sub_footnote); once(e.into()) } #[must_use] fn transform_system_message( &mut self, mut e: e::SystemMessage, ) -> impl Iterator { self.transform_children(&mut e, Self::transform_body_element); once(e.into()) } #[must_use] fn transform_figure(&mut self, mut e: e::Figure) -> impl Iterator { self.transform_children(&mut e, Self::transform_sub_figure); once(e.into()) } #[must_use] fn transform_table(&mut self, mut e: e::Table) -> impl Iterator { self.transform_children(&mut e, Self::transform_sub_table); once(e.into()) } //table elements #[must_use] fn transform_table_group(&mut self, mut e: e::TableGroup) -> impl Iterator { self.transform_children(&mut e, Self::transform_sub_table_group); once(e.into()) } #[must_use] fn transform_table_head( &mut self, mut e: e::TableHead, ) -> impl Iterator { self.transform_children(&mut e, Self::transform_table_row); once(e.into()) } #[must_use] fn transform_table_body( &mut self, mut e: e::TableBody, ) -> impl Iterator { self.transform_children(&mut e, Self::transform_table_row); once(e.into()) } #[must_use] fn transform_table_row(&mut self, mut e: e::TableRow) -> impl Iterator { self.transform_children(&mut e, Self::transform_table_entry); once(e) } #[must_use] fn transform_table_entry( &mut self, mut e: e::TableEntry, ) -> impl Iterator { self.transform_children(&mut e, Self::transform_body_element); once(e) } #[must_use] fn transform_table_colspec( &mut self, e: e::TableColspec, ) -> impl Iterator { once(e.into()) } //body sub elements #[must_use] fn transform_list_item(&mut self, mut e: e::ListItem) -> impl Iterator { self.transform_children(&mut e, Self::transform_body_element); once(e) } #[must_use] fn transform_definition_list_item( &mut self, mut e: e::DefinitionListItem, ) -> impl Iterator { self.transform_children(&mut e, Self::transform_sub_dl_item); once(e) } #[must_use] fn transform_term(&mut self, mut e: e::Term) -> impl Iterator { self.transform_children(&mut e, Self::transform_text_or_inline_element); once(e.into()) } #[must_use] fn transform_classifier(&mut self, mut e: e::Classifier) -> impl Iterator { self.transform_children(&mut e, Self::transform_text_or_inline_element); once(e.into()) } #[must_use] fn transform_definition(&mut self, mut e: e::Definition) -> impl Iterator { self.transform_children(&mut e, Self::transform_body_element); once(e.into()) } #[must_use] fn transform_field_name(&mut self, mut e: e::FieldName) -> impl Iterator { self.transform_children(&mut e, Self::transform_text_or_inline_element); once(e.into()) } #[must_use] fn transform_field_body(&mut self, mut e: e::FieldBody) -> impl Iterator { self.transform_children(&mut e, Self::transform_body_element); once(e.into()) } #[must_use] fn transform_option_list_item( &mut self, mut e: e::OptionListItem, ) -> impl Iterator { self.transform_children(&mut e, Self::transform_sub_option_list_item); once(e) } #[must_use] fn transform_option_group( &mut self, mut e: e::OptionGroup, ) -> impl Iterator { self.transform_children(&mut e, Self::transform_option); once(e.into()) } #[must_use] fn transform_description( &mut self, mut e: e::Description, ) -> impl Iterator { self.transform_children(&mut e, Self::transform_body_element); once(e.into()) } #[must_use] fn transform_option(&mut self, mut e: e::Option_) -> impl Iterator { self.transform_children(&mut e, Self::transform_sub_option); once(e) } #[must_use] fn transform_option_string( &mut self, mut e: e::OptionString, ) -> impl Iterator { self.transform_children(&mut e, Self::transform_string); once(e.into()) } #[must_use] fn transform_option_argument( &mut self, mut e: e::OptionArgument, ) -> impl Iterator { self.transform_children(&mut e, Self::transform_string); once(e.into()) } #[must_use] fn transform_line(&mut self, mut e: e::Line) -> impl Iterator { self.transform_children(&mut e, Self::transform_text_or_inline_element); once(e.into()) } #[must_use] fn transform_attribution( &mut self, mut e: e::Attribution, ) -> impl Iterator { self.transform_children(&mut e, Self::transform_text_or_inline_element); once(e.into()) } #[must_use] fn transform_label(&mut self, mut e: e::Label) -> impl Iterator { self.transform_children(&mut e, Self::transform_text_or_inline_element); once(e.into()) } #[must_use] fn transform_caption(&mut self, mut e: e::Caption) -> impl Iterator { self.transform_children(&mut e, Self::transform_text_or_inline_element); once(e.into()) } #[must_use] fn transform_legend(&mut self, mut e: e::Legend) -> impl Iterator { self.transform_children(&mut e, Self::transform_body_element); once(e.into()) } //inline elements #[must_use] fn transform_string(&mut self, e: String) -> impl Iterator { once(e) } #[must_use] fn transform_emphasis( &mut self, mut e: e::Emphasis, ) -> impl Iterator { self.transform_children(&mut e, Self::transform_text_or_inline_element); once(e.into()) } #[must_use] fn transform_literal( &mut self, mut e: e::Literal, ) -> impl Iterator { self.transform_children(&mut e, Self::transform_string); once(e.into()) } #[must_use] fn transform_reference( &mut self, mut e: e::Reference, ) -> impl Iterator { self.transform_children(&mut e, Self::transform_text_or_inline_element); once(e.into()) } #[must_use] fn transform_strong( &mut self, mut e: e::Strong, ) -> impl Iterator { self.transform_children(&mut e, Self::transform_text_or_inline_element); once(e.into()) } #[must_use] fn transform_footnote_reference( &mut self, mut e: e::FootnoteReference, ) -> impl Iterator { self.transform_children(&mut e, Self::transform_text_or_inline_element); once(e.into()) } #[must_use] fn transform_citation_reference( &mut self, mut e: e::CitationReference, ) -> impl Iterator { self.transform_children(&mut e, Self::transform_text_or_inline_element); once(e.into()) } #[must_use] fn transform_substitution_reference( &mut self, mut e: e::SubstitutionReference, ) -> impl Iterator { self.transform_children(&mut e, Self::transform_text_or_inline_element); once(e.into()) } #[must_use] fn transform_title_reference( &mut self, mut e: e::TitleReference, ) -> impl Iterator { self.transform_children(&mut e, Self::transform_text_or_inline_element); once(e.into()) } #[must_use] fn transform_abbreviation( &mut self, mut e: e::Abbreviation, ) -> impl Iterator { self.transform_children(&mut e, Self::transform_text_or_inline_element); once(e.into()) } #[must_use] fn transform_acronym( &mut self, mut e: e::Acronym, ) -> impl Iterator { self.transform_children(&mut e, Self::transform_text_or_inline_element); once(e.into()) } #[must_use] fn transform_superscript( &mut self, mut e: e::Superscript, ) -> impl Iterator { self.transform_children(&mut e, Self::transform_text_or_inline_element); once(e.into()) } #[must_use] fn transform_subscript( &mut self, mut e: e::Subscript, ) -> impl Iterator { self.transform_children(&mut e, Self::transform_text_or_inline_element); once(e.into()) } #[must_use] fn transform_inline( &mut self, mut e: e::Inline, ) -> impl Iterator { self.transform_children(&mut e, Self::transform_text_or_inline_element); once(e.into()) } #[must_use] fn transform_problematic( &mut self, mut e: e::Problematic, ) -> impl Iterator { self.transform_children(&mut e, Self::transform_text_or_inline_element); once(e.into()) } #[must_use] fn transform_generated( &mut self, mut e: e::Generated, ) -> impl Iterator { self.transform_children(&mut e, Self::transform_text_or_inline_element); once(e.into()) } #[must_use] fn transform_math(&mut self, mut e: e::Math) -> impl Iterator { self.transform_children(&mut e, Self::transform_string); once(e.into()) } #[must_use] fn transform_target_inline( &mut self, mut e: e::TargetInline, ) -> impl Iterator { self.transform_children(&mut e, Self::transform_string); once(e.into()) } #[must_use] fn transform_raw_inline( &mut self, mut e: e::RawInline, ) -> impl Iterator { self.transform_children(&mut e, Self::transform_string); once(e.into()) } #[must_use] fn transform_image_inline( &mut self, e: e::ImageInline, ) -> impl Iterator { once(e.into()) } } rst_parser-0.4.2/src/transforms/visit.rs000064400000000000000000000567201046102023000165160ustar 00000000000000use document_tree::HasChildren as _; use document_tree::element_categories as c; use document_tree::elements as e; pub trait Visit<'tree> { /// Visit a document tree. fn visit(&mut self, e: &'tree e::Document) { for c in e.children() { self.visit_structural_sub_element(c); } } //////////////// // categories // //////////////// fn visit_structural_sub_element(&mut self, c: &'tree c::StructuralSubElement) { use c::StructuralSubElement as S; match c { S::Title(e) => self.visit_title(e), S::Subtitle(e) => self.visit_subtitle(e), S::Decoration(e) => self.visit_decoration(e), S::Docinfo(e) => self.visit_docinfo(e), S::SubStructure(e) => self.visit_substructure(e), } } fn visit_substructure(&mut self, c: &'tree c::SubStructure) { use c::SubStructure as S; match c { S::Topic(e) => self.visit_topic(e), S::Sidebar(e) => self.visit_sidebar(e), S::Transition(e) => self.visit_transition(e), S::Section(e) => self.visit_section(e), S::BodyElement(e) => self.visit_body_element(e), } } fn visit_body_element(&mut self, c: &'tree c::BodyElement) { use c::BodyElement as B; match c { B::Paragraph(e) => self.visit_paragraph(e), B::LiteralBlock(e) => self.visit_literal_block(e), B::DoctestBlock(e) => self.visit_doctest_block(e), B::MathBlock(e) => self.visit_math_block(e), B::Rubric(e) => self.visit_rubric(e), B::SubstitutionDefinition(e) => self.visit_substitution_definition(e), B::Comment(e) => self.visit_comment(e), B::Pending(e) => self.visit_pending(e), B::Target(e) => self.visit_target(e), B::Raw(e) => self.visit_raw(e), B::Image(e) => self.visit_image(e), B::Compound(e) => self.visit_compound(e), B::Container(e) => self.visit_container(e), B::BulletList(e) => self.visit_bullet_list(e), B::EnumeratedList(e) => self.visit_enumerated_list(e), B::DefinitionList(e) => self.visit_definition_list(e), B::FieldList(e) => self.visit_field_list(e), B::OptionList(e) => self.visit_option_list(e), B::LineBlock(e) => self.visit_line_block(e), B::BlockQuote(e) => self.visit_block_quote(e), B::Admonition(e) => self.visit_admonition(e), B::Attention(e) => self.visit_attention(e), B::Hint(e) => self.visit_hint(e), B::Note(e) => self.visit_note(e), B::Caution(e) => self.visit_caution(e), B::Danger(e) => self.visit_danger(e), B::Error(e) => self.visit_error(e), B::Important(e) => self.visit_important(e), B::Tip(e) => self.visit_tip(e), B::Warning(e) => self.visit_warning(e), B::Footnote(e) => self.visit_footnote(e), B::Citation(e) => self.visit_citation(e), B::SystemMessage(e) => self.visit_system_message(e), B::Figure(e) => self.visit_figure(e), B::Table(e) => self.visit_table(e), } } fn visit_bibliographic_element(&mut self, c: &'tree c::BibliographicElement) { use c::BibliographicElement as B; match c { B::Authors(e) => self.visit_authors(e), B::Author(e) => self.visit_author(e), B::Organization(e) => self.visit_organization(e), B::Address(e) => self.visit_address(e), B::Contact(e) => self.visit_contact(e), B::Version(e) => self.visit_version(e), B::Revision(e) => self.visit_revision(e), B::Status(e) => self.visit_status(e), B::Date(e) => self.visit_date(e), B::Copyright(e) => self.visit_copyright(e), B::Field(e) => self.visit_field(e), } } fn visit_text_or_inline_element(&mut self, c: &'tree c::TextOrInlineElement) { use c::TextOrInlineElement as T; match c { T::String(e) => self.visit_string(e), T::Emphasis(e) => self.visit_emphasis(e), T::Strong(e) => self.visit_strong(e), T::Literal(e) => self.visit_literal(e), T::Reference(e) => self.visit_reference(e), T::FootnoteReference(e) => self.visit_footnote_reference(e), T::CitationReference(e) => self.visit_citation_reference(e), T::SubstitutionReference(e) => self.visit_substitution_reference(e), T::TitleReference(e) => self.visit_title_reference(e), T::Abbreviation(e) => self.visit_abbreviation(e), T::Acronym(e) => self.visit_acronym(e), T::Superscript(e) => self.visit_superscript(e), T::Subscript(e) => self.visit_subscript(e), T::Inline(e) => self.visit_inline(e), T::Problematic(e) => self.visit_problematic(e), T::Generated(e) => self.visit_generated(e), T::Math(e) => self.visit_math(e), T::TargetInline(e) => self.visit_target_inline(e), T::RawInline(e) => self.visit_raw_inline(e), T::ImageInline(e) => self.visit_image_inline(e), } } fn visit_author_info(&mut self, c: &'tree c::AuthorInfo) { use c::AuthorInfo as A; match c { A::Author(e) => self.visit_author(e), A::Organization(e) => self.visit_organization(e), A::Address(e) => self.visit_address(e), A::Contact(e) => self.visit_contact(e), } } fn visit_decoration_element(&mut self, c: &'tree c::DecorationElement) { use c::DecorationElement as D; match c { D::Header(e) => self.visit_header(e), D::Footer(e) => self.visit_footer(e), } } fn visit_sub_topic(&mut self, c: &'tree c::SubTopic) { use c::SubTopic as S; match c { S::Title(e) => self.visit_title(e), S::BodyElement(e) => self.visit_body_element(e), } } fn visit_sub_sidebar(&mut self, c: &'tree c::SubSidebar) { use c::SubSidebar as S; match c { S::Topic(e) => self.visit_topic(e), S::Title(e) => self.visit_title(e), S::Subtitle(e) => self.visit_subtitle(e), S::BodyElement(e) => self.visit_body_element(e), } } fn visit_sub_dl_item(&mut self, c: &'tree c::SubDLItem) { use c::SubDLItem as S; match c { S::Term(e) => self.visit_term(e), S::Classifier(e) => self.visit_classifier(e), S::Definition(e) => self.visit_definition(e), } } fn visit_sub_field(&mut self, c: &'tree c::SubField) { use c::SubField as S; match c { S::FieldName(e) => self.visit_field_name(e), S::FieldBody(e) => self.visit_field_body(e), } } fn visit_sub_option_list_item(&mut self, c: &'tree c::SubOptionListItem) { use c::SubOptionListItem as S; match c { S::OptionGroup(e) => self.visit_option_group(e), S::Description(e) => self.visit_description(e), } } fn visit_sub_option(&mut self, c: &'tree c::SubOption) { use c::SubOption as S; match c { S::OptionString(e) => self.visit_option_string(e), S::OptionArgument(e) => self.visit_option_argument(e), } } fn visit_sub_line_block(&mut self, c: &'tree c::SubLineBlock) { use c::SubLineBlock as S; match c { S::LineBlock(e) => self.visit_line_block(e), S::Line(e) => self.visit_line(e), } } fn visit_sub_block_quote(&mut self, c: &'tree c::SubBlockQuote) { use c::SubBlockQuote as S; match c { S::Attribution(e) => self.visit_attribution(e), S::BodyElement(e) => self.visit_body_element(e), } } fn visit_sub_footnote(&mut self, c: &'tree c::SubFootnote) { use c::SubFootnote as S; match c { S::Label(e) => self.visit_label(e), S::BodyElement(e) => self.visit_body_element(e), } } fn visit_sub_figure(&mut self, c: &'tree c::SubFigure) { use c::SubFigure as S; match c { S::Caption(e) => self.visit_caption(e), S::Legend(e) => self.visit_legend(e), S::BodyElement(e) => self.visit_body_element(e), } } fn visit_sub_table(&mut self, c: &'tree c::SubTable) { use c::SubTable as S; match c { S::Title(e) => self.visit_title(e), S::TableGroup(e) => self.visit_table_group(e), } } fn visit_sub_table_group(&mut self, c: &'tree c::SubTableGroup) { use c::SubTableGroup as S; match c { S::TableColspec(e) => self.visit_table_colspec(e), S::TableHead(e) => self.visit_table_head(e), S::TableBody(e) => self.visit_table_body(e), } } ////////////// // elements // ////////////// //structual elements fn visit_section(&mut self, e: &'tree e::Section) { for c in e.children() { self.visit_structural_sub_element(c); } } fn visit_topic(&mut self, e: &'tree e::Topic) { for c in e.children() { self.visit_sub_topic(c); } } fn visit_sidebar(&mut self, e: &'tree e::Sidebar) { for c in e.children() { self.visit_sub_sidebar(c); } } //structural subelements fn visit_title(&mut self, e: &'tree e::Title) { for c in e.children() { self.visit_text_or_inline_element(c); } } fn visit_subtitle(&mut self, e: &'tree e::Subtitle) { for c in e.children() { self.visit_text_or_inline_element(c); } } fn visit_decoration(&mut self, e: &'tree e::Decoration) { for c in e.children() { self.visit_decoration_element(c); } } fn visit_docinfo(&mut self, e: &'tree e::Docinfo) { for c in e.children() { self.visit_bibliographic_element(c); } } fn visit_transition(&mut self, _: &'tree e::Transition) {} //bibliographic elements fn visit_author(&mut self, e: &'tree e::Author) { for c in e.children() { self.visit_text_or_inline_element(c); } } fn visit_authors(&mut self, e: &'tree e::Authors) { for c in e.children() { self.visit_author_info(c); } } fn visit_organization(&mut self, e: &'tree e::Organization) { for c in e.children() { self.visit_text_or_inline_element(c); } } fn visit_address(&mut self, e: &'tree e::Address) { for c in e.children() { self.visit_text_or_inline_element(c); } } fn visit_contact(&mut self, e: &'tree e::Contact) { for c in e.children() { self.visit_text_or_inline_element(c); } } fn visit_version(&mut self, e: &'tree e::Version) { for c in e.children() { self.visit_text_or_inline_element(c); } } fn visit_revision(&mut self, e: &'tree e::Revision) { for c in e.children() { self.visit_text_or_inline_element(c); } } fn visit_status(&mut self, e: &'tree e::Status) { for c in e.children() { self.visit_text_or_inline_element(c); } } fn visit_date(&mut self, e: &'tree e::Date) { for c in e.children() { self.visit_text_or_inline_element(c); } } fn visit_copyright(&mut self, e: &'tree e::Copyright) { for c in e.children() { self.visit_text_or_inline_element(c); } } fn visit_field(&mut self, e: &'tree e::Field) { for c in e.children() { self.visit_sub_field(c); } } //decoration elements fn visit_header(&mut self, e: &'tree e::Header) { for c in e.children() { self.visit_body_element(c); } } fn visit_footer(&mut self, e: &'tree e::Footer) { for c in e.children() { self.visit_body_element(c); } } //simple body elements fn visit_paragraph(&mut self, e: &'tree e::Paragraph) { for c in e.children() { self.visit_text_or_inline_element(c); } } fn visit_literal_block(&mut self, e: &'tree e::LiteralBlock) { for c in e.children() { self.visit_text_or_inline_element(c); } } fn visit_doctest_block(&mut self, e: &'tree e::DoctestBlock) { for c in e.children() { self.visit_text_or_inline_element(c); } } fn visit_math_block(&mut self, e: &'tree e::MathBlock) { for c in e.children() { self.visit_string(c); } } fn visit_rubric(&mut self, e: &'tree e::Rubric) { for c in e.children() { self.visit_text_or_inline_element(c); } } fn visit_substitution_definition(&mut self, e: &'tree e::SubstitutionDefinition) { for c in e.children() { self.visit_text_or_inline_element(c); } } fn visit_comment(&mut self, e: &'tree e::Comment) { for c in e.children() { self.visit_text_or_inline_element(c); } } fn visit_pending(&mut self, _: &'tree e::Pending) {} fn visit_target(&mut self, _: &'tree e::Target) {} fn visit_raw(&mut self, e: &'tree e::Raw) { for c in e.children() { self.visit_string(c); } } fn visit_image(&mut self, _: &'tree e::Image) {} //compound body elements fn visit_compound(&mut self, e: &'tree e::Compound) { for c in e.children() { self.visit_body_element(c); } } fn visit_container(&mut self, e: &'tree e::Container) { for c in e.children() { self.visit_body_element(c); } } fn visit_bullet_list(&mut self, e: &'tree e::BulletList) { for c in e.children() { self.visit_list_item(c); } } fn visit_enumerated_list(&mut self, e: &'tree e::EnumeratedList) { for c in e.children() { self.visit_list_item(c); } } fn visit_definition_list(&mut self, e: &'tree e::DefinitionList) { for c in e.children() { self.visit_definition_list_item(c); } } fn visit_field_list(&mut self, e: &'tree e::FieldList) { for c in e.children() { self.visit_field(c); } } fn visit_option_list(&mut self, e: &'tree e::OptionList) { for c in e.children() { self.visit_option_list_item(c); } } fn visit_line_block(&mut self, e: &'tree e::LineBlock) { for c in e.children() { self.visit_sub_line_block(c); } } fn visit_block_quote(&mut self, e: &'tree e::BlockQuote) { for c in e.children() { self.visit_sub_block_quote(c); } } fn visit_admonition(&mut self, e: &'tree e::Admonition) { for c in e.children() { self.visit_sub_topic(c); } } fn visit_attention(&mut self, e: &'tree e::Attention) { for c in e.children() { self.visit_body_element(c); } } fn visit_hint(&mut self, e: &'tree e::Hint) { for c in e.children() { self.visit_body_element(c); } } fn visit_note(&mut self, e: &'tree e::Note) { for c in e.children() { self.visit_body_element(c); } } fn visit_caution(&mut self, e: &'tree e::Caution) { for c in e.children() { self.visit_body_element(c); } } fn visit_danger(&mut self, e: &'tree e::Danger) { for c in e.children() { self.visit_body_element(c); } } fn visit_error(&mut self, e: &'tree e::Error) { for c in e.children() { self.visit_body_element(c); } } fn visit_important(&mut self, e: &'tree e::Important) { for c in e.children() { self.visit_body_element(c); } } fn visit_tip(&mut self, e: &'tree e::Tip) { for c in e.children() { self.visit_body_element(c); } } fn visit_warning(&mut self, e: &'tree e::Warning) { for c in e.children() { self.visit_body_element(c); } } fn visit_footnote(&mut self, e: &'tree e::Footnote) { for c in e.children() { self.visit_sub_footnote(c); } } fn visit_citation(&mut self, e: &'tree e::Citation) { for c in e.children() { self.visit_sub_footnote(c); } } fn visit_system_message(&mut self, e: &'tree e::SystemMessage) { for c in e.children() { self.visit_body_element(c); } } fn visit_figure(&mut self, e: &'tree e::Figure) { for c in e.children() { self.visit_sub_figure(c); } } fn visit_table(&mut self, e: &'tree e::Table) { for c in e.children() { self.visit_sub_table(c); } } //table elements fn visit_table_group(&mut self, e: &'tree e::TableGroup) { for c in e.children() { self.visit_sub_table_group(c); } } fn visit_table_head(&mut self, e: &'tree e::TableHead) { for c in e.children() { self.visit_table_row(c); } } fn visit_table_body(&mut self, e: &'tree e::TableBody) { for c in e.children() { self.visit_table_row(c); } } fn visit_table_row(&mut self, e: &'tree e::TableRow) { for c in e.children() { self.visit_table_entry(c); } } fn visit_table_entry(&mut self, e: &'tree e::TableEntry) { for c in e.children() { self.visit_body_element(c); } } fn visit_table_colspec(&mut self, _: &'tree e::TableColspec) {} //body sub elements fn visit_list_item(&mut self, e: &'tree e::ListItem) { for c in e.children() { self.visit_body_element(c); } } fn visit_definition_list_item(&mut self, e: &'tree e::DefinitionListItem) { for c in e.children() { self.visit_sub_dl_item(c); } } fn visit_term(&mut self, e: &'tree e::Term) { for c in e.children() { self.visit_text_or_inline_element(c); } } fn visit_classifier(&mut self, e: &'tree e::Classifier) { for c in e.children() { self.visit_text_or_inline_element(c); } } fn visit_definition(&mut self, e: &'tree e::Definition) { for c in e.children() { self.visit_body_element(c); } } fn visit_field_name(&mut self, e: &'tree e::FieldName) { for c in e.children() { self.visit_text_or_inline_element(c); } } fn visit_field_body(&mut self, e: &'tree e::FieldBody) { for c in e.children() { self.visit_body_element(c); } } fn visit_option_list_item(&mut self, e: &'tree e::OptionListItem) { for c in e.children() { self.visit_sub_option_list_item(c); } } fn visit_option_group(&mut self, e: &'tree e::OptionGroup) { for c in e.children() { self.visit_option(c); } } fn visit_description(&mut self, e: &'tree e::Description) { for c in e.children() { self.visit_body_element(c); } } fn visit_option(&mut self, e: &'tree e::Option_) { for c in e.children() { self.visit_sub_option(c); } } fn visit_option_string(&mut self, e: &'tree e::OptionString) { for c in e.children() { self.visit_string(c); } } fn visit_option_argument(&mut self, e: &'tree e::OptionArgument) { for c in e.children() { self.visit_string(c); } } fn visit_line(&mut self, e: &'tree e::Line) { for c in e.children() { self.visit_text_or_inline_element(c); } } fn visit_attribution(&mut self, e: &'tree e::Attribution) { for c in e.children() { self.visit_text_or_inline_element(c); } } fn visit_label(&mut self, e: &'tree e::Label) { for c in e.children() { self.visit_text_or_inline_element(c); } } fn visit_caption(&mut self, e: &'tree e::Caption) { for c in e.children() { self.visit_text_or_inline_element(c); } } fn visit_legend(&mut self, e: &'tree e::Legend) { for c in e.children() { self.visit_body_element(c); } } //inline elements fn visit_string(&mut self, _: &'tree str) {} fn visit_emphasis(&mut self, e: &'tree e::Emphasis) { for c in e.children() { self.visit_text_or_inline_element(c); } } fn visit_literal(&mut self, e: &'tree e::Literal) { for c in e.children() { self.visit_string(c); } } fn visit_reference(&mut self, e: &'tree e::Reference) { for c in e.children() { self.visit_text_or_inline_element(c); } } fn visit_strong(&mut self, e: &'tree e::Strong) { for c in e.children() { self.visit_text_or_inline_element(c); } } fn visit_footnote_reference(&mut self, e: &'tree e::FootnoteReference) { for c in e.children() { self.visit_text_or_inline_element(c); } } fn visit_citation_reference(&mut self, e: &'tree e::CitationReference) { for c in e.children() { self.visit_text_or_inline_element(c); } } fn visit_substitution_reference(&mut self, e: &'tree e::SubstitutionReference) { for c in e.children() { self.visit_text_or_inline_element(c); } } fn visit_title_reference(&mut self, e: &'tree e::TitleReference) { for c in e.children() { self.visit_text_or_inline_element(c); } } fn visit_abbreviation(&mut self, e: &'tree e::Abbreviation) { for c in e.children() { self.visit_text_or_inline_element(c); } } fn visit_acronym(&mut self, e: &'tree e::Acronym) { for c in e.children() { self.visit_text_or_inline_element(c); } } fn visit_superscript(&mut self, e: &'tree e::Superscript) { for c in e.children() { self.visit_text_or_inline_element(c); } } fn visit_subscript(&mut self, e: &'tree e::Subscript) { for c in e.children() { self.visit_text_or_inline_element(c); } } fn visit_inline(&mut self, e: &'tree e::Inline) { for c in e.children() { self.visit_text_or_inline_element(c); } } fn visit_problematic(&mut self, e: &'tree e::Problematic) { for c in e.children() { self.visit_text_or_inline_element(c); } } fn visit_generated(&mut self, e: &'tree e::Generated) { for c in e.children() { self.visit_text_or_inline_element(c); } } fn visit_math(&mut self, e: &'tree e::Math) { for c in e.children() { self.visit_string(c); } } //non-inline versions of inline elements fn visit_target_inline(&mut self, e: &'tree e::TargetInline) { for c in e.children() { self.visit_string(c); } } fn visit_raw_inline(&mut self, e: &'tree e::RawInline) { for c in e.children() { self.visit_string(c); } } fn visit_image_inline(&mut self, _: &'tree e::ImageInline) {} } rst_parser-0.4.2/src/transforms.rs000064400000000000000000000002461046102023000153500ustar 00000000000000mod standard; mod transform; mod visit; pub use self::standard::standard_transform; pub use self::transform::{IteratorMaker, Transform}; pub use self::visit::Visit;