aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPhilipp A2023-12-28 15:43:24 +0100
committerGitHub2023-12-28 15:43:24 +0100
commit0f4f1a420cbcf263a9118ec9b288c95f1b59ade8 (patch)
tree030abd05e8894e4e9c6775794f890180cf6c5e91
parent38b1c488601cfc2479f02df555b135ef01aa5618 (diff)
parent774dd4798aedc40b38c6480e9c47f34c482f12d0 (diff)
downloadrust-rst-0f4f1a420cbcf263a9118ec9b288c95f1b59ade8.tar.bz2
Merge branch 'main' into allow-rst-to-read-from-stdin
-rw-r--r--.editorconfig8
-rw-r--r--.github/workflows/release.yml20
-rw-r--r--.github/workflows/rust.yml55
-rw-r--r--.pre-commit-config.yaml20
-rw-r--r--.travis.yml7
-rw-r--r--.vscode/launch.json2
-rw-r--r--.vscode/settings.json6
-rw-r--r--Cargo.toml8
-rw-r--r--README.rst9
-rw-r--r--document_tree/Cargo.toml2
-rw-r--r--document_tree/src/attribute_types.rs238
-rw-r--r--document_tree/src/element_categories.rs260
-rw-r--r--document_tree/src/element_types.rs186
-rw-r--r--document_tree/src/elements.rs546
-rw-r--r--document_tree/src/extra_attributes.rs126
-rw-r--r--document_tree/src/lib.rs74
-rw-r--r--document_tree/src/macro_util.rs63
-rw-r--r--document_tree/src/url.rs104
-rw-r--r--justfile27
-rw-r--r--parser/Cargo.toml4
-rw-r--r--parser/src/conversion.rs147
-rw-r--r--parser/src/conversion/block.rs414
-rw-r--r--parser/src/conversion/inline.rs278
-rw-r--r--parser/src/conversion/tests.rs101
-rw-r--r--parser/src/lib.rs17
-rw-r--r--parser/src/pair_ext_parse.rs44
-rw-r--r--parser/src/rst.pest10
-rw-r--r--parser/src/simplify.rs1159
-rw-r--r--parser/src/tests.rs517
-rw-r--r--parser/src/token.rs66
-rw-r--r--renderer/Cargo.toml4
-rw-r--r--renderer/src/html.rs753
-rw-r--r--renderer/src/html/tests.rs138
-rw-r--r--renderer/src/lib.rs20
-rw-r--r--rst/Cargo.toml6
-rw-r--r--rst/src/main.rs82
-rw-r--r--rust-rst.svg104
37 files changed, 3158 insertions, 2467 deletions
diff --git a/.editorconfig b/.editorconfig
index c730fb1..73554fa 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -2,12 +2,12 @@
root = true
[*]
-indent_style = tab
+indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
-trim_trailing_whitespace = false
+trim_trailing_whitespace = true
insert_final_newline = true
-[*.pest]
-indent_style = space
+[*.{yml,yaml}]
+indent_size = 2
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..25b0ac9
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,20 @@
+name: Create release PR or publish GitHub release and Rust crate
+
+permissions:
+ pull-requests: write
+ contents: write
+
+on:
+ push:
+ branches: [main]
+
+jobs:
+ release:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - uses: dtolnay/rust-toolchain@stable
+ - uses: MarcoIeni/release-plz-action@v0.5
+ env:
+ GITHUB_TOKEN: ${{ secrets.PAT_GITHUB }}
+ CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml
new file mode 100644
index 0000000..6e35283
--- /dev/null
+++ b/.github/workflows/rust.yml
@@ -0,0 +1,55 @@
+name: CI
+
+on:
+ push:
+ branches: [main]
+ pull_request:
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+env:
+ CARGO_TERM_COLOR: always
+
+jobs:
+ build_and_test:
+ name: Build, check, and test
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - uses: dtolnay/rust-toolchain@stable
+ with:
+ components: clippy
+ - uses: taiki-e/install-action@v2
+ with:
+ tool: cargo-hack,just
+ - uses: Swatinem/rust-cache@v2
+ - run: just build
+ - run: just check
+ - run: just fmt --check
+ - run: just test
+ doc:
+ name: Build and publish docs
+ permissions:
+ pages: write
+ id-token: write
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/configure-pages@v3
+ - uses: dtolnay/rust-toolchain@nightly
+ - uses: taiki-e/install-action@v2
+ with:
+ tool: just
+ - uses: Swatinem/rust-cache@v2
+ - run: just doc
+ - uses: actions/upload-pages-artifact@v3
+ with:
+ path: target/doc
+ - uses: actions/deploy-pages@v4
+ if: github.ref_name == 'main'
+ id: deployment
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 0000000..1c4f592
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,20 @@
+ci:
+ skip:
+ - cargo-fmt
+ - cargo-clippy
+
+repos:
+ - repo: https://github.com/pre-commit/pre-commit-hooks
+ rev: v4.5.0
+ hooks:
+ - id: trailing-whitespace
+ - id: end-of-file-fixer
+ - id: check-yaml
+ - id: check-added-large-files
+ - id: detect-private-key
+ - id: check-merge-conflict
+ - repo: https://github.com/kykosic/pre-commit-rust
+ rev: '0.4.0'
+ hooks:
+ - id: cargo-fmt
+ - id: cargo-clippy
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 0f8af64..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-dist: bionic
-language: rust
-cache: cargo
-rust:
- - stable
-env:
- - RUST_BACKTRACE=short
diff --git a/.vscode/launch.json b/.vscode/launch.json
index acc54de..3629e14 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -38,4 +38,4 @@
"cwd": "${workspaceFolder}"
}
]
-} \ No newline at end of file
+}
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 7a5dde2..4538515 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,4 +1,6 @@
{
"explorer.sortOrder": "mixed",
- "restructuredtext.confPath": ""
-} \ No newline at end of file
+ "restructuredtext.confPath": "",
+ "rust-analyzer.check.features": "all",
+ "rust-analyzer.check.command": "clippy",
+}
diff --git a/Cargo.toml b/Cargo.toml
index e06882d..4c7ed6e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,7 +1,7 @@
[workspace]
members = [
- 'document_tree',
- 'parser',
- 'renderer',
- 'rst',
+ 'document_tree',
+ 'parser',
+ 'renderer',
+ 'rst',
]
diff --git a/README.rst b/README.rst
index 3409190..f9f9c38 100644
--- a/README.rst
+++ b/README.rst
@@ -1,9 +1,6 @@
-=========================
-RuSTructuredText |travis|
-=========================
-
-.. |travis| image:: https://travis-ci.com/flying-sheep/rust-rst.svg?branch=master
- :target: https://travis-ci.com/flying-sheep/rust-rst
+================
+RuSTructuredText
+================
.. image:: rust-rst.svg
diff --git a/document_tree/Cargo.toml b/document_tree/Cargo.toml
index 9e1c481..9a3ccc8 100644
--- a/document_tree/Cargo.toml
+++ b/document_tree/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = 'document_tree'
-version = '0.3.0'
+version = '0.4.0'
authors = ['Philipp A. <flying-sheep@web.de>']
edition = '2018'
description = 'reStructuredText’s DocumentTree representation'
diff --git a/document_tree/src/attribute_types.rs b/document_tree/src/attribute_types.rs
index 411b24d..5303c5b 100644
--- a/document_tree/src/attribute_types.rs
+++ b/document_tree/src/attribute_types.rs
@@ -1,155 +1,201 @@
use std::str::FromStr;
-use failure::{Error,bail,format_err};
-use serde_derive::Serialize;
+use failure::{bail, format_err, Error};
use regex::Regex;
+use serde_derive::Serialize;
use crate::url::Url;
-#[derive(Debug,PartialEq,Eq,Hash,Serialize,Clone)]
+#[derive(Debug, PartialEq, Eq, Hash, Serialize, Clone)]
pub enum EnumeratedListType {
- Arabic,
- LowerAlpha,
- UpperAlpha,
- LowerRoman,
- UpperRoman,
+ Arabic,
+ LowerAlpha,
+ UpperAlpha,
+ LowerRoman,
+ UpperRoman,
}
-#[derive(Debug,PartialEq,Eq,Hash,Serialize,Clone)]
-pub enum FixedSpace { Default, Preserve } // yes, default really is not “Default”
-impl Default for FixedSpace { fn default() -> FixedSpace { FixedSpace::Preserve } }
+#[derive(Default, Debug, PartialEq, Eq, Hash, Serialize, Clone)]
+pub enum FixedSpace {
+ Default,
+ // yes, default really is not “Default”
+ #[default]
+ Preserve,
+}
-#[derive(Debug,PartialEq,Eq,Hash,Serialize,Clone)] pub enum AlignH { Left, Center, Right}
-#[derive(Debug,PartialEq,Eq,Hash,Serialize,Clone)] pub enum AlignHV { Top, Middle, Bottom, Left, Center, Right }
-#[derive(Debug,PartialEq,Eq,Hash,Serialize,Clone)] pub enum AlignV { Top, Middle, Bottom }
+#[derive(Debug, PartialEq, Eq, Hash, Serialize, Clone)]
+pub enum AlignH {
+ Left,
+ Center,
+ Right,
+}
+#[derive(Debug, PartialEq, Eq, Hash, Serialize, Clone)]
+pub enum AlignHV {
+ Top,
+ Middle,
+ Bottom,
+ Left,
+ Center,
+ Right,
+}
+#[derive(Debug, PartialEq, Eq, Hash, Serialize, Clone)]
+pub enum AlignV {
+ Top,
+ Middle,
+ Bottom,
+}
-#[derive(Debug,PartialEq,Eq,Hash,Serialize,Clone)] pub enum TableAlignH { Left, Right, Center, Justify, Char }
-#[derive(Debug,PartialEq,Eq,Hash,Serialize,Clone)] pub enum TableBorder { Top, Bottom, TopBottom, All, Sides, None }
+#[derive(Debug, PartialEq, Eq, Hash, Serialize, Clone)]
+pub enum TableAlignH {
+ Left,
+ Right,
+ Center,
+ Justify,
+ Char,
+}
+#[derive(Debug, PartialEq, Eq, Hash, Serialize, Clone)]
+pub enum TableBorder {
+ Top,
+ Bottom,
+ TopBottom,
+ All,
+ Sides,
+ None,
+}
-#[derive(Debug,PartialEq,Eq,Hash,Serialize,Clone)] pub struct ID(pub String);
-#[derive(Debug,PartialEq,Eq,Hash,Serialize,Clone)] pub struct NameToken(pub String);
+#[derive(Debug, PartialEq, Eq, Hash, Serialize, Clone)]
+pub struct ID(pub String);
+#[derive(Debug, PartialEq, Eq, Hash, Serialize, Clone)]
+pub struct NameToken(pub String);
// The table DTD has the cols attribute of tgroup as required, but having
// TableGroupCols not implement Default would leave no possible implementation
// for TableGroup::with_children.
-#[derive(Debug,PartialEq,Eq,Hash,Serialize,Clone)] pub struct TableGroupCols(pub usize);
-impl Default for TableGroupCols {
- fn default() -> Self {
- TableGroupCols(0)
- }
-}
+#[derive(Default, Debug, PartialEq, Eq, Hash, Serialize, Clone)]
+pub struct TableGroupCols(pub usize);
// no eq for f64
-#[derive(Debug,PartialEq,Serialize,Clone)]
-pub enum Measure { // http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#length-units
- Em(f64),
- Ex(f64),
- Mm(f64),
- Cm(f64),
- In(f64),
- Px(f64),
- Pt(f64),
- Pc(f64),
+#[derive(Debug, PartialEq, Serialize, Clone)]
+pub enum Measure {
+ // http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#length-units
+ Em(f64),
+ Ex(f64),
+ Mm(f64),
+ Cm(f64),
+ In(f64),
+ Px(f64),
+ Pt(f64),
+ Pc(f64),
}
impl FromStr for AlignHV {
- type Err = Error;
- fn from_str(s: &str) -> Result<Self, Self::Err> {
- use self::AlignHV::*;
- Ok(match s {
- "top" => Top,
- "middle" => Middle,
- "bottom" => Bottom,
- "left" => Left,
- "center" => Center,
- "right" => Right,
- s => bail!("Invalid Alignment {}", s),
- })
- }
+ type Err = Error;
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ use self::AlignHV::*;
+ Ok(match s {
+ "top" => Top,
+ "middle" => Middle,
+ "bottom" => Bottom,
+ "left" => Left,
+ "center" => Center,
+ "right" => Right,
+ s => bail!("Invalid Alignment {}", s),
+ })
+ }
}
impl From<&str> for ID {
- fn from(s: &str) -> Self {
- ID(s.to_owned().replace(' ', "-"))
- }
+ fn from(s: &str) -> Self {
+ ID(s.to_owned().replace(' ', "-"))
+ }
}
impl From<&str> for NameToken {
- fn from(s: &str) -> Self {
- NameToken(s.to_owned())
- }
+ fn from(s: &str) -> Self {
+ NameToken(s.to_owned())
+ }
}
impl FromStr for Measure {
- type Err = Error;
- fn from_str(s: &str) -> Result<Self, Self::Err> {
- use self::Measure::*;
- let re = Regex::new(r"(?P<float>\d+\.\d*|\.?\d+)\s*(?P<unit>em|ex|mm|cm|in|px|pt|pc)").unwrap();
- let caps: regex::Captures = re.captures(s).ok_or_else(|| format_err!("Invalid measure"))?;
- let value: f64 = caps["float"].parse()?;
- Ok(match &caps["unit"] {
- "em" => Em(value),
- "ex" => Ex(value),
- "mm" => Mm(value),
- "cm" => Cm(value),
- "in" => In(value),
- "px" => Px(value),
- "pt" => Pt(value),
- "pc" => Pc(value),
- _ => unreachable!(),
- })
- }
+ type Err = Error;
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ use self::Measure::*;
+ let re =
+ Regex::new(r"(?P<float>\d+\.\d*|\.?\d+)\s*(?P<unit>em|ex|mm|cm|in|px|pt|pc)").unwrap();
+ let caps: regex::Captures = re
+ .captures(s)
+ .ok_or_else(|| format_err!("Invalid measure"))?;
+ let value: f64 = caps["float"].parse()?;
+ Ok(match &caps["unit"] {
+ "em" => Em(value),
+ "ex" => Ex(value),
+ "mm" => Mm(value),
+ "cm" => Cm(value),
+ "in" => In(value),
+ "px" => Px(value),
+ "pt" => Pt(value),
+ "pc" => Pc(value),
+ _ => unreachable!(),
+ })
+ }
}
#[cfg(test)]
mod parse_tests {
- use super::*;
-
- #[test]
- fn measure() {
- let _a: Measure = "1.5em".parse().unwrap();
- let _b: Measure = "20 mm".parse().unwrap();
- let _c: Measure = ".5in".parse().unwrap();
- let _d: Measure = "1.pc".parse().unwrap();
- }
+ use super::*;
+
+ #[test]
+ fn measure() {
+ let _a: Measure = "1.5em".parse().unwrap();
+ let _b: Measure = "20 mm".parse().unwrap();
+ let _c: Measure = ".5in".parse().unwrap();
+ let _d: Measure = "1.pc".parse().unwrap();
+ }
}
pub(crate) trait CanBeEmpty {
- fn is_empty(&self) -> bool;
+ fn is_empty(&self) -> bool;
}
/* Specialization necessary
impl<T> CanBeEmpty for T {
- fn is_empty(&self) -> bool { false }
+ fn is_empty(&self) -> bool { false }
}
*/
macro_rules! impl_cannot_be_empty {
- ($t:ty) => {
- impl CanBeEmpty for $t {
- fn is_empty(&self) -> bool { false }
- }
- };
- ($t:ty, $($ts:ty),*) => {
- impl_cannot_be_empty!($t);
- impl_cannot_be_empty!($($ts),*);
- };
+ ($t:ty) => {
+ impl CanBeEmpty for $t {
+ fn is_empty(&self) -> bool { false }
+ }
+ };
+ ($t:ty, $($ts:ty),*) => {
+ impl_cannot_be_empty!($t);
+ impl_cannot_be_empty!($($ts),*);
+ };
}
impl_cannot_be_empty!(Url);
impl_cannot_be_empty!(TableGroupCols);
impl<T> CanBeEmpty for Option<T> {
- fn is_empty(&self) -> bool { self.is_none() }
+ fn is_empty(&self) -> bool {
+ self.is_none()
+ }
}
impl<T> CanBeEmpty for Vec<T> {
- fn is_empty(&self) -> bool { self.is_empty() }
+ fn is_empty(&self) -> bool {
+ self.is_empty()
+ }
}
impl CanBeEmpty for bool {
- fn is_empty(&self) -> bool { !self }
+ fn is_empty(&self) -> bool {
+ !self
+ }
}
impl CanBeEmpty for FixedSpace {
- fn is_empty(&self) -> bool { self == &FixedSpace::default() }
+ fn is_empty(&self) -> bool {
+ self == &FixedSpace::default()
+ }
}
-
diff --git a/document_tree/src/element_categories.rs b/document_tree/src/element_categories.rs
index 24a0798..e0a1886 100644
--- a/document_tree/src/element_categories.rs
+++ b/document_tree/src/element_categories.rs
@@ -1,130 +1,200 @@
-use std::fmt::{self,Debug,Formatter};
+use std::fmt::{self, Debug, Formatter};
use serde_derive::Serialize;
use crate::elements::*;
pub trait HasChildren<C> {
- fn with_children(children: Vec<C>) -> Self;
- fn children(&self) -> &Vec<C>;
- fn children_mut(&mut self) -> &mut Vec<C>;
- fn append_child<R: Into<C>>(&mut self, child: R) {
- self.children_mut().push(child.into());
- }
- fn append_children<R: Into<C> + Clone>(&mut self, more: &[R]) {
- let children = self.children_mut();
- children.reserve(more.len());
- for child in more {
- children.push(child.clone().into());
- }
- }
+ fn with_children(children: Vec<C>) -> Self;
+ fn children(&self) -> &Vec<C>;
+ fn children_mut(&mut self) -> &mut Vec<C>;
+ fn append_child<R: Into<C>>(&mut self, child: R) {
+ self.children_mut().push(child.into());
+ }
+ fn append_children<R: Into<C> + Clone>(&mut self, more: &[R]) {
+ let children = self.children_mut();
+ children.reserve(more.len());
+ for child in more {
+ children.push(child.clone().into());
+ }
+ }
}
macro_rules! impl_into {
- ([ $( (($subcat:ident :: $entry:ident), $supcat:ident), )+ ]) => {
- $( impl_into!($subcat::$entry => $supcat); )+
- };
- ($subcat:ident :: $entry:ident => $supcat:ident ) => {
- impl Into<$supcat> for $entry {
- fn into(self) -> $supcat {
- $supcat::$subcat(Box::new(self.into()))
- }
- }
- };
+ ([ $( (($subcat:ident :: $entry:ident), $supcat:ident), )+ ]) => {
+ $( impl_into!($subcat::$entry => $supcat); )+
+ };
+ ($subcat:ident :: $entry:ident => $supcat:ident ) => {
+ impl From<$entry> for $supcat {
+ fn from(inner: $entry) -> Self {
+ $supcat::$subcat(Box::new(inner.into()))
+ }
+ }
+ };
}
macro_rules! synonymous_enum {
- ( $subcat:ident : $($supcat:ident),+ ; $midcat:ident : $supsupcat:ident { $($entry:ident),+ $(,)* } ) => {
- synonymous_enum!($subcat : $( $supcat ),+ , $midcat { $($entry,)* });
- $( impl_into!($midcat::$entry => $supsupcat); )+
- };
- ( $subcat:ident : $($supcat:ident),+ { $($entry:ident),+ $(,)* } ) => {
- synonymous_enum!($subcat { $( $entry, )* });
- cartesian!(impl_into, [ $( ($subcat::$entry) ),+ ], [ $($supcat),+ ]);
- };
- ( $name:ident { $( $entry:ident ),+ $(,)* } ) => {
- #[derive(PartialEq,Serialize,Clone)]
- pub enum $name { $(
- $entry(Box<$entry>),
- )* }
-
- impl Debug for $name {
- fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
- match *self {
- $( $name::$entry(ref inner) => inner.fmt(fmt), )*
- }
- }
- }
-
- $( impl Into<$name> for $entry {
- fn into(self) -> $name {
- $name::$entry(Box::new(self))
- }
- } )*
- };
+ ( $subcat:ident : $($supcat:ident),+ ; $midcat:ident : $supsupcat:ident { $($entry:ident),+ $(,)* } ) => {
+ synonymous_enum!($subcat : $( $supcat ),+ , $midcat { $($entry,)* });
+ $( impl_into!($midcat::$entry => $supsupcat); )+
+ };
+ ( $subcat:ident : $($supcat:ident),+ { $($entry:ident),+ $(,)* } ) => {
+ synonymous_enum!($subcat { $( $entry, )* });
+ cartesian!(impl_into, [ $( ($subcat::$entry) ),+ ], [ $($supcat),+ ]);
+ };
+ ( $name:ident { $( $entry:ident ),+ $(,)* } ) => {
+ #[derive(PartialEq,Serialize,Clone)]
+ pub enum $name { $(
+ $entry(Box<$entry>),
+ )* }
+
+ impl Debug for $name {
+ fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
+ match *self {
+ $( $name::$entry(ref inner) => inner.fmt(fmt), )*
+ }
+ }
+ }
+
+ $( impl From<$entry> for $name {
+ fn from(inner: $entry) -> Self {
+ $name::$entry(Box::new(inner))
+ }
+ } )*
+ };
}
-synonymous_enum!(StructuralSubElement { Title, Subtitle, Decoration, Docinfo, SubStructure });
+synonymous_enum!(StructuralSubElement {
+ Title,
+ Subtitle,
+ Decoration,
+ Docinfo,
+ SubStructure
+});
synonymous_enum!(SubStructure: StructuralSubElement { Topic, Sidebar, Transition, Section, BodyElement });
synonymous_enum!(BodyElement: SubTopic, SubSidebar, SubBlockQuote, SubFootnote, SubFigure; SubStructure: StructuralSubElement {
- //Simple
- Paragraph, LiteralBlock, DoctestBlock, MathBlock, Rubric, SubstitutionDefinition, Comment, Pending, Target, Raw, Image,
- //Compound
- Compound, Container,
- BulletList, EnumeratedList, DefinitionList, FieldList, OptionList,
- LineBlock, BlockQuote, Admonition, Attention, Hint, Note, Caution, Danger, Error, Important, Tip, Warning, Footnote, Citation, SystemMessage, Figure, Table
+ //Simple
+ Paragraph, LiteralBlock, DoctestBlock, MathBlock, Rubric, SubstitutionDefinition, Comment, Pending, Target, Raw, Image,
+ //Compound
+ Compound, Container,
+ BulletList, EnumeratedList, DefinitionList, FieldList, OptionList,
+ LineBlock, BlockQuote, Admonition, Attention, Hint, Note, Caution, Danger, Error, Important, Tip, Warning, Footnote, Citation, SystemMessage, Figure, Table
});
-synonymous_enum!(BibliographicElement { Author, Authors, Organization, Address, Contact, Version, Revision, Status, Date, Copyright, Field });
+synonymous_enum!(BibliographicElement {
+ Author,
+ Authors,
+ Organization,
+ Address,
+ Contact,
+ Version,
+ Revision,
+ Status,
+ Date,
+ Copyright,
+ Field
+});
synonymous_enum!(TextOrInlineElement {
- String, Emphasis, Strong, Literal, Reference, FootnoteReference, CitationReference, SubstitutionReference, TitleReference, Abbreviation, Acronym, Superscript, Subscript, Inline, Problematic, Generated, Math,
- //also have non-inline versions. Inline image is no figure child, inline target has content
- TargetInline, RawInline, ImageInline
+ String,
+ Emphasis,
+ Strong,
+ Literal,
+ Reference,
+ FootnoteReference,
+ CitationReference,
+ SubstitutionReference,
+ TitleReference,
+ Abbreviation,
+ Acronym,
+ Superscript,
+ Subscript,
+ Inline,
+ Problematic,
+ Generated,
+ Math,
+ //also have non-inline versions. Inline image is no figure child, inline target has content
+ TargetInline,
+ RawInline,
+ ImageInline
});
//--------------\\
//Content Models\\
//--------------\\
-synonymous_enum!(AuthorInfo { Author, Organization, Address, Contact });
+synonymous_enum!(AuthorInfo {
+ Author,
+ Organization,
+ Address,
+ Contact
+});
synonymous_enum!(DecorationElement { Header, Footer });
synonymous_enum!(SubTopic { Title, BodyElement });
-synonymous_enum!(SubSidebar { Topic, Title, Subtitle, BodyElement });
-synonymous_enum!(SubDLItem { Term, Classifier, Definition });
-synonymous_enum!(SubField { FieldName, FieldBody });
-synonymous_enum!(SubOptionListItem { OptionGroup, Description });
-synonymous_enum!(SubOption { OptionString, OptionArgument });
+synonymous_enum!(SubSidebar {
+ Topic,
+ Title,
+ Subtitle,
+ BodyElement
+});
+synonymous_enum!(SubDLItem {
+ Term,
+ Classifier,
+ Definition
+});
+synonymous_enum!(SubField {
+ FieldName,
+ FieldBody
+});
+synonymous_enum!(SubOptionListItem {
+ OptionGroup,
+ Description
+});
+synonymous_enum!(SubOption {
+ OptionString,
+ OptionArgument
+});
synonymous_enum!(SubLineBlock { LineBlock, Line });
-synonymous_enum!(SubBlockQuote { Attribution, BodyElement });
+synonymous_enum!(SubBlockQuote {
+ Attribution,
+ BodyElement
+});
synonymous_enum!(SubFootnote { Label, BodyElement });
-synonymous_enum!(SubFigure { Caption, Legend, BodyElement });
+synonymous_enum!(SubFigure {
+ Caption,
+ Legend,
+ BodyElement
+});
synonymous_enum!(SubTable { Title, TableGroup });
-synonymous_enum!(SubTableGroup { TableColspec, TableHead, TableBody });
+synonymous_enum!(SubTableGroup {
+ TableColspec,
+ TableHead,
+ TableBody
+});
#[cfg(test)]
mod conversion_tests {
- use std::default::Default;
- use super::*;
-
- #[test]
- fn basic() {
- let _: BodyElement = Paragraph::default().into();
- }
-
- #[test]
- fn more() {
- let _: SubStructure = Paragraph::default().into();
- }
-
- #[test]
- fn even_more() {
- let _: StructuralSubElement = Paragraph::default().into();
- }
-
- #[test]
- fn super_() {
- let be: BodyElement = Paragraph::default().into();
- let _: StructuralSubElement = be.into();
- }
+ use super::*;
+ use std::default::Default;
+
+ #[test]
+ fn basic() {
+ let _: BodyElement = Paragraph::default().into();
+ }
+
+ #[test]
+ fn more() {
+ let _: SubStructure = Paragraph::default().into();
+ }
+
+ #[test]
+ fn even_more() {
+ let _: StructuralSubElement = Paragraph::default().into();
+ }
+
+ #[test]
+ fn super_() {
+ let be: BodyElement = Paragraph::default().into();
+ let _: StructuralSubElement = be.into();
+ }
}
diff --git a/document_tree/src/element_types.rs b/document_tree/src/element_types.rs
index 429573e..42571d1 100644
--- a/document_tree/src/element_types.rs
+++ b/document_tree/src/element_types.rs
@@ -1,96 +1,96 @@
// enum ElementType {
-// //structual elements
-// Section, Topic, Sidebar,
-//
-// //structural subelements
-// Title, Subtitle, Decoration, Docinfo, Transition,
-//
-// //bibliographic elements
-// Author, Authors, Organization,
-// Address { space: FixedSpace },
-// Contact, Version, Revision, Status,
-// Date, Copyright, Field,
-//
-// //decoration elements
-// Header, Footer,
-//
-// //simple body elements
-// Paragraph,
-// LiteralBlock { space: FixedSpace },
-// DoctestBlock { space: FixedSpace },
-// MathBlock, Rubric,
-// SubstitutionDefinition { ltrim: bool, rtrim: bool },
-// Comment { space: FixedSpace },
-// Pending,
-// Target { refuri: Url, refid: ID, refname: Vec<NameToken>, anonymous: bool },
-// Raw { space: FixedSpace, format: Vec<NameToken> },
-// Image {
-// align: AlignHV,
-// uri: Url,
-// alt: String,
-// height: Measure,
-// width: Measure,
-// scale: f64,
-// },
-//
-// //compound body elements
-// Compound, Container,
-//
-// BulletList { bullet: String },
-// EnumeratedList { enumtype: EnumeratedListType, prefix: String, suffix: String },
-// DefinitionList, FieldList, OptionList,
-//
-// LineBlock, BlockQuote,
-// Admonition, Attention, Hint, Note,
-// Caution, Danger, Error, Important,
-// Tip, Warning,
-// Footnote { backrefs: Vec<ID>, auto: bool },
-// Citation { backrefs: Vec<ID> },
-// SystemMessage { backrefs: Vec<ID>, level: usize, line: usize, type_: NameToken },
-// Figure { align: AlignH, width: usize },
-// Table, //TODO: Table
-//
-// //body sub elements
-// ListItem,
-//
-// DefinitionListItem, Term,
-// Classifier, Definition,
-//
-// FieldName, FieldBody,
-//
-// OptionListItem, OptionGroup, Description, Option_, OptionString,
-// OptionArgument { delimiter: String },
-//
-// Line, Attribution, Label,
-//
-// Caption, Legend,
-//
-// //inline elements
-// Emphasis, Strong, Literal,
-// Reference { name: String, refuri: Url, refid: ID, refname: Vec<NameToken> },
-// FootnoteReference { refid: ID, refname: Vec<NameToken>, auto: bool },
-// CitationReference { refid: ID, refname: Vec<NameToken> },
-// SubstitutionReference { refname: Vec<NameToken> },
-// TitleReference,
-// Abbreviation, Acronym,
-// Superscript, Subscript,
-// Inline,
-// Problematic { refid: ID },
-// Generated, Math,
-//
-// //also have non-inline versions. Inline image is no figure child, inline target has content
-// TargetInline { refuri: Url, refid: ID, refname: Vec<NameToken>, anonymous: bool },
-// RawInline { space: FixedSpace, format: Vec<NameToken> },
-// ImageInline {
-// align: AlignHV,
-// uri: Url,
-// alt: String,
-// height: Measure,
-// width: Measure,
-// scale: f64,
-// },
-//
-// //text element
-// TextElement,
+// //structual elements
+// Section, Topic, Sidebar,
+//
+// //structural subelements
+// Title, Subtitle, Decoration, Docinfo, Transition,
+//
+// //bibliographic elements
+// Author, Authors, Organization,
+// Address { space: FixedSpace },
+// Contact, Version, Revision, Status,
+// Date, Copyright, Field,
+//
+// //decoration elements
+// Header, Footer,
+//
+// //simple body elements
+// Paragraph,
+// LiteralBlock { space: FixedSpace },
+// DoctestBlock { space: FixedSpace },
+// MathBlock, Rubric,
+// SubstitutionDefinition { ltrim: bool, rtrim: bool },
+// Comment { space: FixedSpace },
+// Pending,
+// Target { refuri: Url, refid: ID, refname: Vec<NameToken>, anonymous: bool },
+// Raw { space: FixedSpace, format: Vec<NameToken> },
+// Image {
+// align: AlignHV,
+// uri: Url,
+// alt: String,
+// height: Measure,
+// width: Measure,
+// scale: f64,
+// },
+//
+// //compound body elements
+// Compound, Container,
+//
+// BulletList { bullet: String },
+// EnumeratedList { enumtype: EnumeratedListType, prefix: String, suffix: String },
+// DefinitionList, FieldList, OptionList,
+//
+// LineBlock, BlockQuote,
+// Admonition, Attention, Hint, Note,
+// Caution, Danger, Error, Important,
+// Tip, Warning,
+// Footnote { backrefs: Vec<ID>, auto: bool },
+// Citation { backrefs: Vec<ID> },
+// SystemMessage { backrefs: Vec<ID>, level: usize, line: usize, type_: NameToken },
+// Figure { align: AlignH, width: usize },
+// Table, //TODO: Table
+//
+// //body sub elements
+// ListItem,
+//
+// DefinitionListItem, Term,
+// Classifier, Definition,
+//
+// FieldName, FieldBody,
+//
+// OptionListItem, OptionGroup, Description, Option_, OptionString,
+// OptionArgument { delimiter: String },
+//
+// Line, Attribution, Label,
+//
+// Caption, Legend,
+//
+// //inline elements
+// Emphasis, Strong, Literal,
+// Reference { name: String, refuri: Url, refid: ID, refname: Vec<NameToken> },
+// FootnoteReference { refid: ID, refname: Vec<NameToken>, auto: bool },
+// CitationReference { refid: ID, refname: Vec<NameToken> },
+// SubstitutionReference { refname: Vec<NameToken> },
+// TitleReference,
+// Abbreviation, Acronym,
+// Superscript, Subscript,
+// Inline,
+// Problematic { refid: ID },
+// Generated, Math,
+//
+// //also have non-inline versions. Inline image is no figure child, inline target has content
+// TargetInline { refuri: Url, refid: ID, refname: Vec<NameToken>, anonymous: bool },
+// RawInline { space: FixedSpace, format: Vec<NameToken> },
+// ImageInline {
+// align: AlignHV,
+// uri: Url,
+// alt: String,
+// height: Measure,
+// width: Measure,
+// scale: f64,
+// },
+//
+// //text element
+// TextElement,
// }
diff --git a/document_tree/src/elements.rs b/document_tree/src/elements.rs
index 1db0a24..4921e1a 100644
--- a/document_tree/src/elements.rs
+++ b/document_tree/src/elements.rs
@@ -1,288 +1,348 @@
-use std::path::PathBuf;
use serde_derive::Serialize;
+use std::path::PathBuf;
-use crate::attribute_types::{CanBeEmpty,ID,NameToken};
-use crate::extra_attributes::{self,ExtraAttributes};
+use crate::attribute_types::{CanBeEmpty, NameToken, ID};
use crate::element_categories::*;
-
+use crate::extra_attributes::{self, ExtraAttributes};
//-----------------\\
//Element hierarchy\\
//-----------------\\
pub trait Element {
- /// A list containing one or more unique identifier keys
- fn ids (& self) -> & Vec<ID>;
- fn ids_mut(&mut self) -> &mut Vec<ID>;
- /// a list containing the names of an element, typically originating from the element's title or content.
- /// Each name in names must be unique; if there are name conflicts (two or more elements want to the same name),
- /// the contents will be transferred to the dupnames attribute on the duplicate elements.
- /// An element may have at most one of the names or dupnames attributes, but not both.
- fn names (& self) -> & Vec<NameToken>;
- fn names_mut(&mut self) -> &mut Vec<NameToken>;
- fn source (& self) -> & Option<PathBuf>;
- fn source_mut(&mut self) -> &mut Option<PathBuf>;
- fn classes (& self) -> & Vec<String>;
- fn classes_mut(&mut self) -> &mut Vec<String>;
+ /// A list containing one or more unique identifier keys
+ fn ids(&self) -> &Vec<ID>;
+ fn ids_mut(&mut self) -> &mut Vec<ID>;
+ /// a list containing the names of an element, typically originating from the element's title or content.
+ /// Each name in names must be unique; if there are name conflicts (two or more elements want to the same name),
+ /// the contents will be transferred to the dupnames attribute on the duplicate elements.
+ /// An element may have at most one of the names or dupnames attributes, but not both.
+ fn names(&self) -> &Vec<NameToken>;
+ fn names_mut(&mut self) -> &mut Vec<NameToken>;
+ fn source(&self) -> &Option<PathBuf>;
+ fn source_mut(&mut self) -> &mut Option<PathBuf>;
+ fn classes(&self) -> &Vec<String>;
+ fn classes_mut(&mut self) -> &mut Vec<String>;
}
-#[derive(Debug,Default,PartialEq,Serialize,Clone)]
+#[derive(Debug, Default, PartialEq, Serialize, Clone)]
pub struct CommonAttributes {
- #[serde(skip_serializing_if = "CanBeEmpty::is_empty")]
- ids: Vec<ID>,
- #[serde(skip_serializing_if = "CanBeEmpty::is_empty")]
- names: Vec<NameToken>,
- #[serde(skip_serializing_if = "CanBeEmpty::is_empty")]
- source: Option<PathBuf>,
- #[serde(skip_serializing_if = "CanBeEmpty::is_empty")]
- classes: Vec<String>,
- //TODO: dupnames
+ #[serde(skip_serializing_if = "CanBeEmpty::is_empty")]
+ ids: Vec<ID>,
+ #[serde(skip_serializing_if = "CanBeEmpty::is_empty")]
+ names: Vec<NameToken>,
+ #[serde(skip_serializing_if = "CanBeEmpty::is_empty")]
+ source: Option<PathBuf>,
+ #[serde(skip_serializing_if = "CanBeEmpty::is_empty")]
+ classes: Vec<String>,
+ //TODO: dupnames
}
//----\\
//impl\\
//----\\
-macro_rules! impl_element { ($name:ident) => (
- impl Element for $name {
- fn ids (& self) -> & Vec<ID> { & self.common.ids }
- fn ids_mut(&mut self) -> &mut Vec<ID> { &mut self.common.ids }
- fn names (& self) -> & Vec<NameToken> { & self.common.names }
- fn names_mut(&mut self) -> &mut Vec<NameToken> { &mut self.common.names }
- fn source (& self) -> & Option<PathBuf> { & self.common.source }
- fn source_mut(&mut self) -> &mut Option<PathBuf> { &mut self.common.source }
- fn classes (& self) -> & Vec<String> { & self.common.classes }
- fn classes_mut(&mut self) -> &mut Vec<String> { &mut self.common.classes }
- }
-)}
+macro_rules! impl_element {
+ ($name:ident) => {
+ impl Element for $name {
+ fn ids(&self) -> &Vec<ID> {
+ &self.common.ids
+ }
+ fn ids_mut(&mut self) -> &mut Vec<ID> {
+ &mut self.common.ids
+ }
+ fn names(&self) -> &Vec<NameToken> {
+ &self.common.names
+ }
+ fn names_mut(&mut self) -> &mut Vec<NameToken> {
+ &mut self.common.names
+ }
+ fn source(&self) -> &Option<PathBuf> {
+ &self.common.source
+ }
+ fn source_mut(&mut self) -> &mut Option<PathBuf> {
+ &mut self.common.source
+ }
+ fn classes(&self) -> &Vec<String> {
+ &self.common.classes
+ }
+ fn classes_mut(&mut self) -> &mut Vec<String> {
+ &mut self.common.classes
+ }
+ }
+ };
+}
-macro_rules! impl_children { ($name:ident, $childtype:ident) => (
- impl HasChildren<$childtype> for $name {
- #[allow(clippy::needless_update)]
- fn with_children(children: Vec<$childtype>) -> $name { $name { children: children, ..Default::default() } }
- fn children (& self) -> & Vec<$childtype> { & self.children }
- fn children_mut(&mut self) -> &mut Vec<$childtype> { &mut self.children }
- }
-)}
+macro_rules! impl_children {
+ ($name:ident, $childtype:ident) => {
+ impl HasChildren<$childtype> for $name {
+ #[allow(clippy::needless_update)]
+ fn with_children(children: Vec<$childtype>) -> $name {
+ $name {
+ children,
+ ..Default::default()
+ }
+ }
+ fn children(&self) -> &Vec<$childtype> {
+ &self.children
+ }
+ fn children_mut(&mut self) -> &mut Vec<$childtype> {
+ &mut self.children
+ }
+ }
+ };
+}
macro_rules! impl_extra { ($name:ident $($more:tt)*) => (
- impl ExtraAttributes<extra_attributes::$name> for $name {
- #[allow(clippy::needless_update)]
- fn with_extra(extra: extra_attributes::$name) -> $name { $name { common: Default::default(), extra: extra $($more)* } }
- fn extra (& self) -> & extra_attributes::$name { & self.extra }
- fn extra_mut(&mut self) -> &mut extra_attributes::$name { &mut self.extra }
- }
+ impl ExtraAttributes<extra_attributes::$name> for $name {
+ #[allow(clippy::needless_update)]
+ fn with_extra(extra: extra_attributes::$name) -> $name { $name { common: Default::default(), extra $($more)* } }
+ fn extra (& self) -> & extra_attributes::$name { & self.extra }
+ fn extra_mut(&mut self) -> &mut extra_attributes::$name { &mut self.extra }
+ }
)}
trait HasExtraAndChildren<C, A> {
- fn with_extra_and_children(extra: A, children: Vec<C>) -> Self;
+ fn with_extra_and_children(extra: A, children: Vec<C>) -> Self;
}
-impl<T, C, A> HasExtraAndChildren<C, A> for T where T: HasChildren<C> + ExtraAttributes<A> {
- #[allow(clippy::needless_update)]
- fn with_extra_and_children(extra: A, mut children: Vec<C>) -> Self {
- let mut r = Self::with_extra(extra);
- r.children_mut().extend(children.drain(..));
- r
- }
+impl<T, C, A> HasExtraAndChildren<C, A> for T
+where
+ T: HasChildren<C> + ExtraAttributes<A>,
+{
+ #[allow(clippy::needless_update)]
+ fn with_extra_and_children(extra: A, mut children: Vec<C>) -> Self {
+ let mut r = Self::with_extra(extra);
+ r.children_mut().append(&mut children);
+ r
+ }
}
macro_rules! impl_new {(
- $(#[$attr:meta])*
- pub struct $name:ident { $(
- $(#[$fattr:meta])*
- $field:ident : $typ:path
- ),* $(,)* }
+ $(#[$attr:meta])*
+ pub struct $name:ident { $(
+ $(#[$fattr:meta])*
+ $field:ident : $typ:path
+ ),* $(,)* }
) => (
- $(#[$attr])*
- #[derive(Debug,PartialEq,Serialize,Clone)]
- pub struct $name { $(
- $(#[$fattr])* $field: $typ,
- )* }
- impl $name {
- pub fn new( $( $field: $typ, )* ) -> $name { $name { $( $field: $field, )* } }
- }
+ $(#[$attr])*
+ #[derive(Debug,PartialEq,Serialize,Clone)]
+ pub struct $name { $(
+ $(#[$fattr])* $field: $typ,
+ )* }
+ impl $name {
+ pub fn new( $( $field: $typ, )* ) -> $name { $name { $( $field, )* } }
+ }
)}
macro_rules! impl_elem {
- ($name:ident) => {
- impl_new!(#[derive(Default)] pub struct $name {
- #[serde(flatten)] common: CommonAttributes,
- });
- impl_element!($name);
- };
- ($name:ident; +) => {
- impl_new!(#[derive(Default)] pub struct $name {
- #[serde(flatten)] common: CommonAttributes,
- #[serde(flatten)] extra: extra_attributes::$name,
- });
- impl_element!($name); impl_extra!($name, ..Default::default());
- };
- ($name:ident; *) => { //same as above with no default
- impl_new!(pub struct $name {
- #[serde(flatten)] common: CommonAttributes,
- #[serde(flatten)] extra: extra_attributes::$name
- });
- impl_element!($name); impl_extra!($name);
- };
- ($name:ident, $childtype:ident) => {
- impl_new!(#[derive(Default)] pub struct $name {
- #[serde(flatten)] common: CommonAttributes,
- children: Vec<$childtype>,
- });
- impl_element!($name); impl_children!($name, $childtype);
- };
- ($name:ident, $childtype:ident; +) => {
- impl_new!(#[derive(Default)] pub struct $name {
- #[serde(flatten)] common: CommonAttributes,
- #[serde(flatten)] extra: extra_attributes::$name,
- children: Vec<$childtype>,
- });
- impl_element!($name); impl_extra!($name, ..Default::default()); impl_children!($name, $childtype);
- };
+ ($name:ident) => {
+ impl_new!(
+ #[derive(Default)]
+ pub struct $name {
+ #[serde(flatten)]
+ common: CommonAttributes,
+ }
+ );
+ impl_element!($name);
+ };
+ ($name:ident; +) => {
+ impl_new!(
+ #[derive(Default)]
+ pub struct $name {
+ #[serde(flatten)]
+ common: CommonAttributes,
+ #[serde(flatten)]
+ extra: extra_attributes::$name,
+ }
+ );
+ impl_element!($name);
+ impl_extra!($name, ..Default::default());
+ };
+ ($name:ident; *) => {
+ //same as above with no default
+ impl_new!(
+ pub struct $name {
+ #[serde(flatten)]
+ common: CommonAttributes,
+ #[serde(flatten)]
+ extra: extra_attributes::$name,
+ }
+ );
+ impl_element!($name);
+ impl_extra!($name);
+ };
+ ($name:ident, $childtype:ident) => {
+ impl_new!(
+ #[derive(Default)]
+ pub struct $name {
+ #[serde(flatten)]
+ common: CommonAttributes,
+ children: Vec<$childtype>,
+ }
+ );
+ impl_element!($name);
+ impl_children!($name, $childtype);
+ };
+ ($name:ident, $childtype:ident; +) => {
+ impl_new!(
+ #[derive(Default)]
+ pub struct $name {
+ #[serde(flatten)]
+ common: CommonAttributes,
+ #[serde(flatten)]
+ extra: extra_attributes::$name,
+ children: Vec<$childtype>,
+ }
+ );
+ impl_element!($name);
+ impl_extra!($name, ..Default::default());
+ impl_children!($name, $childtype);
+ };
}
macro_rules! impl_elems { ( $( ($($args:tt)*) )* ) => (
- $( impl_elem!($($args)*); )*
+ $( impl_elem!($($args)*); )*
)}
-
-#[derive(Default,Debug,Serialize)]
-pub struct Document { children: Vec<StructuralSubElement> }
+#[derive(Default, Debug, Serialize)]
+pub struct Document {
+ children: Vec<StructuralSubElement>,
+}
impl_children!(Document, StructuralSubElement);
impl_elems!(
- //structual elements
- (Section, StructuralSubElement)
- (Topic, SubTopic)
- (Sidebar, SubSidebar)
-
- //structural subelements
- (Title, TextOrInlineElement)
- (Subtitle, TextOrInlineElement)
- (Decoration, DecorationElement)
- (Docinfo, BibliographicElement)
- (Transition)
-
- //bibliographic elements
- (Author, TextOrInlineElement)
- (Authors, AuthorInfo)
- (Organization, TextOrInlineElement)
- (Address, TextOrInlineElement; +)
- (Contact, TextOrInlineElement)
- (Version, TextOrInlineElement)
- (Revision, TextOrInlineElement)
- (Status, TextOrInlineElement)
- (Date, TextOrInlineElement)
- (Copyright, TextOrInlineElement)
- (Field, SubField)
-
- //decoration elements
- (Header, BodyElement)
- (Footer, BodyElement)
-
- //simple body elements
- (Paragraph, TextOrInlineElement)
- (LiteralBlock, TextOrInlineElement; +)
- (DoctestBlock, TextOrInlineElement; +)
- (MathBlock, String)
- (Rubric, TextOrInlineElement)
- (SubstitutionDefinition, TextOrInlineElement; +)
- (Comment, TextOrInlineElement; +)
- (Pending)
- (Target; +)
- (Raw, String; +)
- (Image; *)
-
- //compound body elements
- (Compound, BodyElement)
- (Container, BodyElement)
-
- (BulletList, ListItem; +)
- (EnumeratedList, ListItem; +)
- (DefinitionList, DefinitionListItem)
- (FieldList, Field)
- (OptionList, OptionListItem)
-
- (LineBlock, SubLineBlock)
- (BlockQuote, SubBlockQuote)
- (Admonition, SubTopic)
- (Attention, BodyElement)
- (Hint, BodyElement)
- (Note, BodyElement)
- (Caution, BodyElement)
- (Danger, BodyElement)
- (Error, BodyElement)
- (Important, BodyElement)
- (Tip, BodyElement)
- (Warning, BodyElement)
- (Footnote, SubFootnote; +)
- (Citation, SubFootnote; +)
- (SystemMessage, BodyElement; +)
- (Figure, SubFigure; +)
- (Table, SubTable; +)
-
- //table elements
- (TableGroup, SubTableGroup; +)
- (TableHead, TableRow; +)
- (TableBody, TableRow; +)
- (TableRow, TableEntry; +)
- (TableEntry, BodyElement; +)
- (TableColspec; +)
-
- //body sub elements
- (ListItem, BodyElement)
-
- (DefinitionListItem, SubDLItem)
- (Term, TextOrInlineElement)
- (Classifier, TextOrInlineElement)
- (Definition, BodyElement)
-
- (FieldName, TextOrInlineElement)
- (FieldBody, BodyElement)
-
- (OptionListItem, SubOptionListItem)
- (OptionGroup, Option_)
- (Description, BodyElement)
- (Option_, SubOption)
- (OptionString, String)
- (OptionArgument, String; +)
-
- (Line, TextOrInlineElement)
- (Attribution, TextOrInlineElement)
- (Label, TextOrInlineElement)
-
- (Caption, TextOrInlineElement)
- (Legend, BodyElement)
-
- //inline elements
- (Emphasis, TextOrInlineElement)
- (Literal, String)
- (Reference, TextOrInlineElement; +)
- (Strong, TextOrInlineElement)
- (FootnoteReference, TextOrInlineElement; +)
- (CitationReference, TextOrInlineElement; +)
- (SubstitutionReference, TextOrInlineElement; +)
- (TitleReference, TextOrInlineElement)
- (Abbreviation, TextOrInlineElement)
- (Acronym, TextOrInlineElement)
- (Superscript, TextOrInlineElement)
- (Subscript, TextOrInlineElement)
- (Inline, TextOrInlineElement)
- (Problematic, TextOrInlineElement; +)
- (Generated, TextOrInlineElement)
- (Math, String)
-
- //also have non-inline versions. Inline image is no figure child, inline target has content
- (TargetInline, String; +)
- (RawInline, String; +)
- (ImageInline; *)
-
- //text element = String
+ //structual elements
+ (Section, StructuralSubElement)
+ (Topic, SubTopic)
+ (Sidebar, SubSidebar)
+
+ //structural subelements
+ (Title, TextOrInlineElement)
+ (Subtitle, TextOrInlineElement)
+ (Decoration, DecorationElement)
+ (Docinfo, BibliographicElement)
+ (Transition)
+
+ //bibliographic elements
+ (Author, TextOrInlineElement)
+ (Authors, AuthorInfo)
+ (Organization, TextOrInlineElement)
+ (Address, TextOrInlineElement; +)
+ (Contact, TextOrInlineElement)
+ (Version, TextOrInlineElement)
+ (Revision, TextOrInlineElement)
+ (Status, TextOrInlineElement)
+ (Date, TextOrInlineElement)
+ (Copyright, TextOrInlineElement)
+ (Field, SubField)
+
+ //decoration elements
+ (Header, BodyElement)
+ (Footer, BodyElement)
+
+ //simple body elements
+ (Paragraph, TextOrInlineElement)
+ (LiteralBlock, TextOrInlineElement; +)
+ (DoctestBlock, TextOrInlineElement; +)
+ (MathBlock, String)
+ (Rubric, TextOrInlineElement)
+ (SubstitutionDefinition, TextOrInlineElement; +)
+ (Comment, TextOrInlineElement; +)
+ (Pending)
+ (Target; +)
+ (Raw, String; +)
+ (Image; *)
+
+ //compound body elements
+ (Compound, BodyElement)
+ (Container, BodyElement)
+
+ (BulletList, ListItem; +)
+ (EnumeratedList, ListItem; +)
+ (DefinitionList, DefinitionListItem)
+ (FieldList, Field)
+ (OptionList, OptionListItem)
+
+ (LineBlock, SubLineBlock)
+ (BlockQuote, SubBlockQuote)
+ (Admonition, SubTopic)
+ (Attention, BodyElement)
+ (Hint, BodyElement)
+ (Note, BodyElement)
+ (Caution, BodyElement)
+ (Danger, BodyElement)
+ (Error, BodyElement)
+ (Important, BodyElement)
+ (Tip, BodyElement)
+ (Warning, BodyElement)
+ (Footnote, SubFootnote; +)
+ (Citation, SubFootnote; +)
+ (SystemMessage, BodyElement; +)
+ (Figure, SubFigure; +)
+ (Table, SubTable; +)
+
+ //table elements
+ (TableGroup, SubTableGroup; +)
+ (TableHead, TableRow; +)
+ (TableBody, TableRow; +)
+ (TableRow, TableEntry; +)
+ (TableEntry, BodyElement; +)
+ (TableColspec; +)
+
+ //body sub elements
+ (ListItem, BodyElement)
+
+ (DefinitionListItem, SubDLItem)
+ (Term, TextOrInlineElement)
+ (Classifier, TextOrInlineElement)
+ (Definition, BodyElement)
+
+ (FieldName, TextOrInlineElement)
+ (FieldBody, BodyElement)
+
+ (OptionListItem, SubOptionListItem)
+ (OptionGroup, Option_)
+ (Description, BodyElement)
+ (Option_, SubOption)
+ (OptionString, String)
+ (OptionArgument, String; +)
+
+ (Line, TextOrInlineElement)
+ (Attribution, TextOrInlineElement)
+ (Label, TextOrInlineElement)
+
+ (Caption, TextOrInlineElement)
+ (Legend, BodyElement)
+
+ //inline elements
+ (Emphasis, TextOrInlineElement)
+ (Literal, String)
+ (Reference, TextOrInlineElement; +)
+ (Strong, TextOrInlineElement)
+ (FootnoteReference, TextOrInlineElement; +)
+ (CitationReference, TextOrInlineElement; +)
+ (SubstitutionReference, TextOrInlineElement; +)
+ (TitleReference, TextOrInlineElement)
+ (Abbreviation, TextOrInlineElement)
+ (Acronym, TextOrInlineElement)
+ (Superscript, TextOrInlineElement)
+ (Subscript, TextOrInlineElement)
+ (Inline, TextOrInlineElement)
+ (Problematic, TextOrInlineElement; +)
+ (Generated, TextOrInlineElement)
+ (Math, String)
+
+ //also have non-inline versions. Inline image is no figure child, inline target has content
+ (TargetInline, String; +)
+ (RawInline, String; +)
+ (ImageInline; *)
+
+ //text element = String
);
impl<'a> From<&'a str> for TextOrInlineElement {
- fn from(s: &'a str) -> Self {
- s.to_owned().into()
- }
+ fn from(s: &'a str) -> Self {
+ s.to_owned().into()
+ }
}
diff --git a/document_tree/src/extra_attributes.rs b/document_tree/src/extra_attributes.rs
index 45fcf32..9dcabd7 100644
--- a/document_tree/src/extra_attributes.rs
+++ b/document_tree/src/extra_attributes.rs
@@ -1,62 +1,60 @@
use serde_derive::Serialize;
-use crate::url::Url;
use crate::attribute_types::{
- CanBeEmpty,
- FixedSpace,
- ID,NameToken,
- AlignHV,AlignH,AlignV,
- TableAlignH,TableBorder,TableGroupCols,
- Measure,
- EnumeratedListType,
+ AlignH, AlignHV, AlignV, CanBeEmpty, EnumeratedListType, FixedSpace, Measure, NameToken,
+ TableAlignH, TableBorder, TableGroupCols, ID,
};
+use crate::url::Url;
pub trait ExtraAttributes<A> {
- fn with_extra(extra: A) -> Self;
- fn extra (& self) -> & A;
- fn extra_mut(&mut self) -> &mut A;
+ fn with_extra(extra: A) -> Self;
+ fn extra(&self) -> &A;
+ fn extra_mut(&mut self) -> &mut A;
}
macro_rules! impl_extra {
- ( $name:ident { $( $(#[$pattr:meta])* $param:ident : $type:ty ),* $(,)* } ) => (
- impl_extra!(
- #[derive(Default,Debug,PartialEq,Serialize,Clone)]
- $name { $( $(#[$pattr])* $param : $type, )* }
- );
- );
- ( $(#[$attr:meta])+ $name:ident { $( $(#[$pattr:meta])* $param:ident : $type:ty ),* $(,)* } ) => (
- $(#[$attr])+
- pub struct $name { $(
- $(#[$pattr])*
- #[serde(skip_serializing_if = "CanBeEmpty::is_empty")]
- pub $param : $type,
- )* }
- );
+ ( $name:ident { $( $(#[$pattr:meta])* $param:ident : $type:ty ),* $(,)* } ) => (
+ impl_extra!(
+ #[derive(Default,Debug,PartialEq,Serialize,Clone)]
+ $name { $( $(#[$pattr])* $param : $type, )* }
+ );
+ );
+ ( $(#[$attr:meta])+ $name:ident { $( $(#[$pattr:meta])* $param:ident : $type:ty ),* $(,)* } ) => (
+ $(#[$attr])+
+ pub struct $name { $(
+ $(#[$pattr])*
+ #[serde(skip_serializing_if = "CanBeEmpty::is_empty")]
+ pub $param : $type,
+ )* }
+ );
}
impl_extra!(Address { space: FixedSpace });
impl_extra!(LiteralBlock { space: FixedSpace });
impl_extra!(DoctestBlock { space: FixedSpace });
-impl_extra!(SubstitutionDefinition { ltrim: bool, rtrim: bool });
+impl_extra!(SubstitutionDefinition {
+ ltrim: bool,
+ rtrim: bool
+});
impl_extra!(Comment { space: FixedSpace });
impl_extra!(Target {
- /// External reference to a URI/URL
- refuri: Option<Url>,
- /// References to ids attributes in other elements
- refid: Option<ID>,
- /// Internal reference to the names attribute of another element. May resolve to either an internal or external reference.
- refname: Vec<NameToken>,
- anonymous: bool,
+ /// External reference to a URI/URL
+ refuri: Option<Url>,
+ /// References to ids attributes in other elements
+ refid: Option<ID>,
+ /// Internal reference to the names attribute of another element. May resolve to either an internal or external reference.
+ refname: Vec<NameToken>,
+ anonymous: bool,
});
impl_extra!(Raw { space: FixedSpace, format: Vec<NameToken> });
impl_extra!(#[derive(Debug,PartialEq,Serialize,Clone)] Image {
- uri: Url,
- align: Option<AlignHV>,
- alt: Option<String>,
- height: Option<Measure>,
- width: Option<Measure>,
- scale: Option<u8>,
- target: Option<Url>, // Not part of the DTD but a valid argument
+ uri: Url,
+ align: Option<AlignHV>,
+ alt: Option<String>,
+ height: Option<Measure>,
+ width: Option<Measure>,
+ scale: Option<u8>,
+ target: Option<Url>, // Not part of the DTD but a valid argument
});
//bools usually are XML yesorno. “auto” however either exists and is set to something random like “1” or doesn’t exist
@@ -81,13 +79,13 @@ impl_extra!(TableColspec { colnum: Option<usize>, colname: Option<NameToken>, co
impl_extra!(OptionArgument { delimiter: Option<String> });
impl_extra!(Reference {
- name: Option<NameToken>, //TODO: is CDATA in the DTD, so maybe no nametoken?
- /// External reference to a URI/URL
- refuri: Option<Url>,
- /// References to ids attributes in other elements
- refid: Option<ID>,
- /// Internal reference to the names attribute of another element
- refname: Vec<NameToken>,
+ name: Option<NameToken>, //TODO: is CDATA in the DTD, so maybe no nametoken?
+ /// External reference to a URI/URL
+ refuri: Option<Url>,
+ /// References to ids attributes in other elements
+ refid: Option<ID>,
+ /// Internal reference to the names attribute of another element
+ refname: Vec<NameToken>,
});
impl_extra!(FootnoteReference { refid: Option<ID>, refname: Vec<NameToken>, auto: bool });
impl_extra!(CitationReference { refid: Option<ID>, refname: Vec<NameToken> });
@@ -96,25 +94,27 @@ impl_extra!(Problematic { refid: Option<ID> });
//also have non-inline versions. Inline image is no figure child, inline target has content
impl_extra!(TargetInline {
- /// External reference to a URI/URL
- refuri: Option<Url>,
- /// References to ids attributes in other elements
- refid: Option<ID>,
- /// Internal reference to the names attribute of another element. May resolve to either an internal or external reference.
- refname: Vec<NameToken>,
- anonymous: bool,
+ /// External reference to a URI/URL
+ refuri: Option<Url>,
+ /// References to ids attributes in other elements
+ refid: Option<ID>,
+ /// Internal reference to the names attribute of another element. May resolve to either an internal or external reference.
+ refname: Vec<NameToken>,
+ anonymous: bool,
});
impl_extra!(RawInline { space: FixedSpace, format: Vec<NameToken> });
pub type ImageInline = Image;
impl Image {
- pub fn new(uri: Url) -> Image { Image {
- uri,
- align: None,
- alt: None,
- height: None,
- width: None,
- scale: None,
- target: None,
- } }
+ pub fn new(uri: Url) -> Image {
+ Image {
+ uri,
+ align: None,
+ alt: None,
+ height: None,
+ width: None,
+ scale: None,
+ target: None,
+ }
+ }
}
diff --git a/document_tree/src/lib.rs b/document_tree/src/lib.rs
index 9154725..4b34ea9 100644
--- a/document_tree/src/lib.rs
+++ b/document_tree/src/lib.rs
@@ -1,50 +1,52 @@
-#![recursion_limit="256"]
+#![recursion_limit = "256"]
-///http://docutils.sourceforge.net/docs/ref/doctree.html
-///serves as AST
+/// See [doctree][] reference.
+/// Serves as AST.
+///
+/// [doctree]: http://docutils.sourceforge.net/docs/ref/doctree.html
#[macro_use]
mod macro_util;
-pub mod url;
-pub mod elements;
+pub mod attribute_types;
pub mod element_categories;
+pub mod elements;
pub mod extra_attributes;
-pub mod attribute_types;
+pub mod url;
+pub use self::element_categories::HasChildren;
pub use self::elements::*; //Element,CommonAttributes,HasExtraAndChildren
pub use self::extra_attributes::ExtraAttributes;
-pub use self::element_categories::HasChildren;
#[cfg(test)]
mod tests {
- use super::*;
- use std::default::Default;
-
- #[test]
- fn imperative() {
- let mut doc = Document::default();
- let mut title = Title::default();
- let url = "https://example.com/image.jpg".parse().unwrap();
- let image = ImageInline::with_extra(extra_attributes::ImageInline::new(url));
- title.append_child("Hi");
- title.append_child(image);
- doc.append_child(title);
-
- println!("{:?}", doc);
- }
-
- #[test]
- fn descriptive() {
- let doc = Document::with_children(vec![
- Title::with_children(vec![
- "Hi".into(),
- ImageInline::with_extra(extra_attributes::ImageInline::new(
- "https://example.com/image.jpg".parse().unwrap()
- )).into(),
- ]).into()
- ]);
-
- println!("{:?}", doc);
- }
+ use super::*;
+ use std::default::Default;
+
+ #[test]
+ fn imperative() {
+ let mut doc = Document::default();
+ let mut title = Title::default();
+ let url = "https://example.com/image.jpg".parse().unwrap();
+ let image = ImageInline::with_extra(extra_attributes::ImageInline::new(url));
+ title.append_child("Hi");
+ title.append_child(image);
+ doc.append_child(title);
+
+ println!("{:?}", doc);
+ }
+
+ #[test]
+ fn descriptive() {
+ let doc = Document::with_children(vec![Title::with_children(vec![
+ "Hi".into(),
+ ImageInline::with_extra(extra_attributes::ImageInline::new(
+ "https://example.com/image.jpg".parse().unwrap(),
+ ))
+ .into(),
+ ])
+ .into()]);
+
+ println!("{:?}", doc);
+ }
}
diff --git a/document_tree/src/macro_util.rs b/document_tree/src/macro_util.rs
index dcf3725..e370011 100644
--- a/document_tree/src/macro_util.rs
+++ b/document_tree/src/macro_util.rs
@@ -1,42 +1,41 @@
macro_rules! cartesian_impl {
- ($out:tt [] $b:tt $init_b:tt $submacro:tt) => {
- $submacro!{$out}
- };
- ($out:tt [$a:tt, $($at:tt)*] [] $init_b:tt $submacro:tt) => {
- cartesian_impl!{$out [$($at)*] $init_b $init_b $submacro}
- };
- ([$($out:tt)*] [$a:tt, $($at:tt)*] [$b:tt, $($bt:tt)*] $init_b:tt $submacro:tt) => {
- cartesian_impl!{[$($out)* ($a, $b),] [$a, $($at)*] [$($bt)*] $init_b $submacro}
- };
+ ($out:tt [] $b:tt $init_b:tt $submacro:tt) => {
+ $submacro!{$out}
+ };
+ ($out:tt [$a:tt, $($at:tt)*] [] $init_b:tt $submacro:tt) => {
+ cartesian_impl!{$out [$($at)*] $init_b $init_b $submacro}
+ };
+ ([$($out:tt)*] [$a:tt, $($at:tt)*] [$b:tt, $($bt:tt)*] $init_b:tt $submacro:tt) => {
+ cartesian_impl!{[$($out)* ($a, $b),] [$a, $($at)*] [$($bt)*] $init_b $submacro}
+ };
}
macro_rules! cartesian {
- ( $submacro:tt, [$($a:tt)*], [$($b:tt)*]) => {
- cartesian_impl!{[] [$($a)*,] [$($b)*,] [$($b)*,] $submacro}
- };
+ ( $submacro:tt, [$($a:tt)*], [$($b:tt)*]) => {
+ cartesian_impl!{[] [$($a)*,] [$($b)*,] [$($b)*,] $submacro}
+ };
}
-
#[cfg(test)]
mod tests {
- macro_rules! print_cartesian {
- ( [ $(($a1:tt, $a2:tt)),* , ] ) => {
- fn test_f(x:i64, y:i64) -> Result<(i64, i64), ()> {
- match (x, y) {
- $(
- ($a1, $a2) => { Ok(($a1, $a2)) }
- )*
- _ => { Err(()) }
- }
- }
- };
- }
+ macro_rules! print_cartesian {
+ ( [ $(($a1:tt, $a2:tt)),* , ] ) => {
+ fn test_f(x:i64, y:i64) -> Result<(i64, i64), ()> {
+ match (x, y) {
+ $(
+ ($a1, $a2) => { Ok(($a1, $a2)) }
+ )*
+ _ => { Err(()) }
+ }
+ }
+ };
+ }
- #[test]
- fn print_cartesian() {
- cartesian!(print_cartesian, [1, 2, 3], [4, 5, 6]);
- assert_eq!(test_f(1, 4), Ok((1, 4)));
- assert_eq!(test_f(1, 3), Err(()));
- assert_eq!(test_f(3, 5), Ok((3, 5)));
- }
+ #[test]
+ fn print_cartesian() {
+ cartesian!(print_cartesian, [1, 2, 3], [4, 5, 6]);
+ assert_eq!(test_f(1, 4), Ok((1, 4)));
+ assert_eq!(test_f(1, 3), Err(()));
+ assert_eq!(test_f(3, 5), Ok((3, 5)));
+ }
}
diff --git a/document_tree/src/url.rs b/document_tree/src/url.rs
index 31a0536..be320e9 100644
--- a/document_tree/src/url.rs
+++ b/document_tree/src/url.rs
@@ -1,78 +1,74 @@
use std::fmt;
use std::str::FromStr;
-use url::{self,ParseError};
use serde_derive::Serialize;
-
+use url::{self, ParseError};
fn starts_with_scheme(input: &str) -> bool {
- let scheme = input.split(':').next().unwrap();
- if scheme == input || scheme.is_empty() {
- return false;
- }
- let mut chars = input.chars();
- // First character.
- if !chars.next().unwrap().is_ascii_alphabetic() {
- return false;
- }
- for ch in chars {
- if !ch.is_ascii_alphanumeric() && ch != '+' && ch != '-' && ch != '.' {
- return false;
- }
- }
- true
+ let scheme = input.split(':').next().unwrap();
+ if scheme == input || scheme.is_empty() {
+ return false;
+ }
+ let mut chars = input.chars();
+ // First character.
+ if !chars.next().unwrap().is_ascii_alphabetic() {
+ return false;
+ }
+ for ch in chars {
+ if !ch.is_ascii_alphanumeric() && ch != '+' && ch != '-' && ch != '.' {
+ return false;
+ }
+ }
+ true
}
/// The string representation of a URL, either absolute or relative, that has
/// been verified as a valid URL on construction.
-#[derive(Debug,PartialEq,Serialize,Clone)]
+#[derive(Debug, PartialEq, Serialize, Clone)]
#[serde(transparent)]
pub struct Url(String);
impl Url {
- pub fn parse_absolute(input: &str) -> Result<Self, ParseError> {
- Ok(url::Url::parse(input)?.into())
- }
- pub fn parse_relative(input: &str) -> Result<Self, ParseError> {
- // We're assuming that any scheme through which RsT documents are being
- // accessed is a hierarchical scheme, and so we can parse relative to a
- // random hierarchical URL.
- if input.starts_with('/') || !starts_with_scheme(input) {
- // Continue only if the parse succeeded, disregarding its result.
- let random_base_url = url::Url::parse("https://a/b").unwrap();
- url::Url::options()
- .base_url(Some(&random_base_url))
- .parse(input)?;
- Ok(Url(input.into()))
- } else {
- // If this is a URL at all, it's an absolute one.
- // There's no appropriate variant of url::ParseError really.
- Err(ParseError::SetHostOnCannotBeABaseUrl)
- }
- }
- pub fn as_str(&self) -> &str {
- self.0.as_str()
- }
+ pub fn parse_absolute(input: &str) -> Result<Self, ParseError> {
+ Ok(url::Url::parse(input)?.into())
+ }
+ pub fn parse_relative(input: &str) -> Result<Self, ParseError> {
+ // We're assuming that any scheme through which RsT documents are being
+ // accessed is a hierarchical scheme, and so we can parse relative to a
+ // random hierarchical URL.
+ if input.starts_with('/') || !starts_with_scheme(input) {
+ // Continue only if the parse succeeded, disregarding its result.
+ let random_base_url = url::Url::parse("https://a/b").unwrap();
+ url::Url::options()
+ .base_url(Some(&random_base_url))
+ .parse(input)?;
+ Ok(Url(input.into()))
+ } else {
+ // If this is a URL at all, it's an absolute one.
+ // There's no appropriate variant of url::ParseError really.
+ Err(ParseError::SetHostOnCannotBeABaseUrl)
+ }
+ }
+ pub fn as_str(&self) -> &str {
+ self.0.as_str()
+ }
}
impl From<url::Url> for Url {
- fn from(url: url::Url) -> Self {
- Url(url.into_string())
- }
+ fn from(url: url::Url) -> Self {
+ Url(url.into())
+ }
}
-
impl fmt::Display for Url {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "{}", self.as_str())
- }
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{}", self.as_str())
+ }
}
-
impl FromStr for Url {
- type Err = ParseError;
- fn from_str(input: &str) -> Result<Self, Self::Err> {
- Url::parse_absolute(input)
- .or_else(|_| Url::parse_relative(input))
- }
+ type Err = ParseError;
+ fn from_str(input: &str) -> Result<Self, Self::Err> {
+ Url::parse_absolute(input).or_else(|_| Url::parse_relative(input))
+ }
}
diff --git a/justfile b/justfile
new file mode 100644
index 0000000..43bf768
--- /dev/null
+++ b/justfile
@@ -0,0 +1,27 @@
+# just manual: https://github.com/casey/just/#readme
+
+_default:
+ @just --list
+
+watch:
+ cargo watch -s 'just doc' -s 'just fmt'
+
+# Build package
+build:
+ cargo hack --feature-powerset build --verbose
+
+# Runs clippy on the sources
+check:
+ cargo hack --feature-powerset clippy --locked -- -D warnings
+
+# Runs unit tests
+test:
+ cargo hack --feature-powerset --skip=extension-module test --locked
+
+# Build documentation
+doc:
+ RUSTDOCFLAGS="-Dwarnings -Z unstable-options --enable-index-page" cargo +nightly doc --all-features
+
+# Format code
+fmt *args:
+ cargo fmt {{args}}
diff --git a/parser/Cargo.toml b/parser/Cargo.toml
index 18e38ae..721d6da 100644
--- a/parser/Cargo.toml
+++ b/parser/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = 'rst_parser'
-version = '0.3.2'
+version = '0.4.0'
authors = ['Philipp A. <flying-sheep@web.de>']
edition = '2018'
description = 'a reStructuredText parser'
@@ -12,7 +12,7 @@ homepage = 'https://github.com/flying-sheep/rust-rst'
repository = 'https://github.com/flying-sheep/rust-rst'
[dependencies]
-document_tree = { path = '../document_tree', version = '0.3.0' }
+document_tree = { path = '../document_tree', version = '0.4.0' }
pest = '2.1.2'
pest_derive = '2.1.0'
diff --git a/parser/src/conversion.rs b/parser/src/conversion.rs
index de5f091..4bd5a8c 100644
--- a/parser/src/conversion.rs
+++ b/parser/src/conversion.rs
@@ -7,90 +7,95 @@ use failure::Error;
use pest::iterators::Pairs;
use document_tree::{
- Element,HasChildren,
- elements as e,
- element_categories as c,
- attribute_types as at,
+ attribute_types as at, element_categories as c, elements as e, Element, HasChildren,
};
use crate::pest_rst::Rule;
-
fn ssubel_to_section_unchecked_mut(ssubel: &mut c::StructuralSubElement) -> &mut e::Section {
- match ssubel {
- c::StructuralSubElement::SubStructure(ref mut b) => match **b {
- c::SubStructure::Section(ref mut s) => s,
- _ => unreachable!(),
- },
- _ => unreachable!(),
- }
+ match ssubel {
+ c::StructuralSubElement::SubStructure(ref mut b) => match **b {
+ c::SubStructure::Section(ref mut s) => s,
+ _ => unreachable!(),
+ },
+ _ => unreachable!(),
+ }
}
-
-fn get_level<'tl>(toplevel: &'tl mut Vec<c::StructuralSubElement>, section_idxs: &[Option<usize>]) -> &'tl mut Vec<c::StructuralSubElement> {
- let mut level = toplevel;
- for maybe_i in section_idxs {
- if let Some(i) = *maybe_i {
- level = ssubel_to_section_unchecked_mut(&mut level[i]).children_mut();
- }
- }
- level
+fn get_level<'tl>(
+ toplevel: &'tl mut Vec<c::StructuralSubElement>,
+ section_idxs: &[Option<usize>],
+) -> &'tl mut Vec<c::StructuralSubElement> {
+ let mut level = toplevel;
+ for maybe_i in section_idxs {
+ if let Some(i) = *maybe_i {
+ level = ssubel_to_section_unchecked_mut(&mut level[i]).children_mut();
+ }
+ }
+ level
}
-
pub fn convert_document(pairs: Pairs<Rule>) -> Result<e::Document, Error> {
- use self::block::TitleOrSsubel::*;
-
- let mut toplevel: Vec<c::StructuralSubElement> = vec![];
- // The kinds of section titles encountered.
- // `section_idx[x]` has the kind `kinds[x]`, but `kinds` can be longer
- let mut kinds: Vec<block::TitleKind> = 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<Option<usize>> = 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, &section_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, &section_idxs).push(elem),
- }}
- }
- Ok(e::Document::with_children(toplevel))
+ use self::block::TitleOrSsubel::*;
+
+ let mut toplevel: Vec<c::StructuralSubElement> = vec![];
+ // The kinds of section titles encountered.
+ // `section_idx[x]` has the kind `kinds[x]`, but `kinds` can be longer
+ let mut kinds: Vec<block::TitleKind> = 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<Option<usize>> = 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, &section_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, &section_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() || (ch >= '\x1C' && ch <= '\x1F')
- ).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
+ // 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
}
diff --git a/parser/src/conversion/block.rs b/parser/src/conversion/block.rs
index 626bc20..97f0e23 100644
--- a/parser/src/conversion/block.rs
+++ b/parser/src/conversion/block.rs
@@ -1,256 +1,286 @@
-use failure::{Error,bail};
+use failure::{bail, Error};
use pest::iterators::Pair;
use document_tree::{
- Element,HasChildren,ExtraAttributes,
- elements as e,
- element_categories as c,
- extra_attributes as a,
- attribute_types as at
+ attribute_types as at, element_categories as c, elements as e, extra_attributes as a, Element,
+ ExtraAttributes, HasChildren,
};
-use crate::{
- pest_rst::Rule,
- pair_ext_parse::PairExt,
-};
-use super::{whitespace_normalize_name, inline::convert_inlines};
-
+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 TitleKind {
+ Double(char),
+ Single(char),
+}
pub(super) enum TitleOrSsubel {
- Title(e::Title, TitleKind),
- Ssubel(c::StructuralSubElement),
+ Title(e::Title, TitleKind),
+ Ssubel(c::StructuralSubElement),
}
-
pub(super) fn convert_ssubel(pair: Pair<Rule>) -> Result<Option<TitleOrSsubel>, Error> {
- use self::TitleOrSsubel::*;
- 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()),
- }))
+ use self::TitleOrSsubel::*;
+ 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<Rule>) -> Result<c::SubStructure, Error> {
- Ok(match pair.as_rule() {
- // todo: Topic, Sidebar, Transition
- // no section here, as it’s constructed from titles
- _ => convert_body_elem(pair)?.into(),
- })
+ #[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<Rule>) -> Result<c::BodyElement, Error> {
- Ok(match pair.as_rule() {
- Rule::paragraph => convert_paragraph(pair)?.into(),
- Rule::target => convert_target(pair)?.into(),
- Rule::substitution_def => convert_substitution_def(pair)?.into(),
- Rule::admonition_gen => convert_admonition_gen(pair)?.into(),
- Rule::image => convert_image::<e::Image>(pair)?.into(),
- Rule::bullet_list => convert_bullet_list(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),
- })
+ Ok(match pair.as_rule() {
+ Rule::paragraph => convert_paragraph(pair)?.into(),
+ Rule::target => convert_target(pair)?.into(),
+ Rule::substitution_def => convert_substitution_def(pair)?.into(),
+ Rule::admonition_gen => convert_admonition_gen(pair)?,
+ Rule::image => convert_image::<e::Image>(pair)?.into(),
+ Rule::bullet_list => convert_bullet_list(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<Rule>) -> Result<(e::Title, TitleKind), Error> {
- let mut title: Option<String> = None;
- let mut title_inlines: Option<Vec<c::TextOrInlineElement>> = None;
- let mut adornment_char: Option<char> = 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))
+ let mut title: Option<String> = None;
+ let mut title_inlines: Option<Vec<c::TextOrInlineElement>> = None;
+ let mut adornment_char: Option<char> = 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<Rule>) -> Result<e::Paragraph, Error> {
- Ok(e::Paragraph::with_children(convert_inlines(pair)?))
+ Ok(e::Paragraph::with_children(convert_inlines(pair)?))
}
-
fn convert_target(pair: Pair<Rule>) -> Result<e::Target, Error> {
- let mut elem: e::Target = Default::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)
+ let mut elem: e::Target = Default::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)
}
fn convert_substitution_def(pair: Pair<Rule>) -> Result<e::SubstitutionDefinition, Error> {
- 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<c::TextOrInlineElement> = match inner_pair.as_rule() {
- Rule::replace => convert_replace(inner_pair)?,
- Rule::image => vec![convert_image::<e::ImageInline>(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)
+ 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<c::TextOrInlineElement> = match inner_pair.as_rule() {
+ Rule::replace => convert_replace(inner_pair)?,
+ Rule::image => vec![convert_image::<e::ImageInline>(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<Rule>) -> Result<Vec<c::TextOrInlineElement>, Error> {
- let mut pairs = pair.into_inner();
- let paragraph = pairs.next().unwrap();
- convert_inlines(paragraph)
+ let mut pairs = pair.into_inner();
+ let paragraph = pairs.next().unwrap();
+ convert_inlines(paragraph)
}
-fn convert_image<I>(pair: Pair<Rule>) -> Result<I, Error> where I: Element + ExtraAttributes<a::Image> {
- 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 convert_image<I>(pair: Pair<Rule>) -> Result<I, Error>
+where
+ I: Element + ExtraAttributes<a::Image>,
+{
+ 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<Rule>) -> Result<u8, Error> {
- let input = if pair.as_str().chars().rev().next() == Some('%') { &pair.as_str()[..pair.as_str().len()-1] } else { pair.as_str() };
- use pest::error::{Error,ErrorVariant};
- Ok(input.parse().map_err(|e: std::num::ParseIntError| {
- let var: ErrorVariant<Rule> = ErrorVariant::CustomError { message: e.to_string() };
- Error::new_from_span(var, pair.as_span())
- })?)
+ let input = if pair.as_str().ends_with('%') {
+ &pair.as_str()[..pair.as_str().len() - 1]
+ } else {
+ pair.as_str()
+ };
+ use pest::error::{Error, ErrorVariant};
+ Ok(input.parse().map_err(|e: std::num::ParseIntError| {
+ let var: ErrorVariant<Rule> = ErrorVariant::CustomError {
+ message: e.to_string(),
+ };
+ Error::new_from_span(var, pair.as_span())
+ })?)
}
fn convert_admonition_gen(pair: Pair<Rule>) -> Result<c::BodyElement, Error> {
- let mut iter = pair.into_inner();
- let typ = iter.next().unwrap().as_str();
- // TODO: in reality it contains body elements.
- let children: Vec<c::BodyElement> = iter.map(|p| e::Paragraph::with_children(vec![p.as_str().into()]).into()).collect();
- Ok(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),
- })
+ let mut iter = pair.into_inner();
+ let typ = iter.next().unwrap().as_str();
+ // TODO: in reality it contains body elements.
+ let children: Vec<c::BodyElement> = iter
+ .map(|p| e::Paragraph::with_children(vec![p.as_str().into()]).into())
+ .collect();
+ Ok(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<Rule>) -> Result<e::BulletList, Error> {
- Ok(e::BulletList::with_children(pair.into_inner().map(convert_bullet_item).collect::<Result<_, _>>()?))
+ Ok(e::BulletList::with_children(
+ pair.into_inner()
+ .map(convert_bullet_item)
+ .collect::<Result<_, _>>()?,
+ ))
}
fn convert_bullet_item(pair: Pair<Rule>) -> Result<e::ListItem, Error> {
- let mut iter = pair.into_inner();
- let mut children: Vec<c::BodyElement> = vec![
- convert_paragraph(iter.next().unwrap())?.into()
- ];
- for p in iter {
- children.push(convert_body_elem(p)?);
- }
- Ok(e::ListItem::with_children(children))
+ let mut iter = pair.into_inner();
+ let mut children: Vec<c::BodyElement> = 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_literal_block(pair: Pair<Rule>) -> e::LiteralBlock {
- convert_literal_lines(pair.into_inner().next().unwrap())
+ convert_literal_lines(pair.into_inner().next().unwrap())
}
fn convert_literal_lines(pair: Pair<Rule>) -> 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();
- return e::LiteralBlock::with_children(children);
+ 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<Rule>) -> 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
+ 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<Rule>) -> e::Raw {
- let mut iter = pair.into_inner();
- let format = iter.next().unwrap();
+ 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
+ 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<Rule>) -> 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)
+ 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)
}
diff --git a/parser/src/conversion/inline.rs b/parser/src/conversion/inline.rs
index 82a74e7..a0dcb88 100644
--- a/parser/src/conversion/inline.rs
+++ b/parser/src/conversion/inline.rs
@@ -2,159 +2,155 @@ use failure::Error;
use pest::iterators::Pair;
use document_tree::{
- HasChildren,
- elements as e,
- url::Url,
- element_categories as c,
- extra_attributes as a,
- attribute_types as at,
+ attribute_types as at, element_categories as c, elements as e, extra_attributes as a, url::Url,
+ HasChildren,
};
-use crate::{
- pest_rst::Rule,
-// pair_ext_parse::PairExt,
-};
use super::whitespace_normalize_name;
-
+use crate::pest_rst::Rule;
pub fn convert_inline(pair: Pair<Rule>) -> Result<c::TextOrInlineElement, Error> {
- Ok(match pair.as_rule() {
- Rule::str | Rule::str_nested => pair.as_str().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 => unimplemented!("unknown rule {:?}", rule),
- })
+ Ok(match pair.as_rule() {
+ Rule::str | Rule::str_nested => pair.as_str().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 => unimplemented!("unknown rule {:?}", rule),
+ })
}
pub fn convert_inlines(pair: Pair<Rule>) -> Result<Vec<c::TextOrInlineElement>, Error> {
- pair.into_inner().map(convert_inline).collect()
+ pair.into_inner().map(convert_inline).collect()
}
fn convert_reference(pair: Pair<Rule>) -> Result<c::TextOrInlineElement, Error> {
- let name;
- let refuri;
- let refid;
- let mut refname = vec![];
- let mut children: Vec<c::TextOrInlineElement> = vec![];
- let concrete = pair.into_inner().next().unwrap();
- match concrete.as_rule() {
- Rule::reference_target => {
- let rt_inner = concrete.into_inner().next().unwrap(); // reference_target_uq or target_name_qu
- match rt_inner.as_rule() {
- Rule::reference_target_uq => {
- refid = None;
- name = Some(rt_inner.as_str().into());
- refuri = None;
- refname.push(rt_inner.as_str().into());
- children.push(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!()
- };
- refid = None;
- name = Some(trimmed_text.into());
- refuri = 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)
- } else if inner.as_str().ends_with('_') {
- // like target_name_qu (minus the final underscore)
- let full_str = inner.as_str();
- refname.push(full_str[0..full_str.len() - 1].into());
- None
- } else {
- // like relative_reference
- Some(Url::parse_relative(inner.as_str())?)
- },
- Rule::target_name_qu => {
- refname.push(inner.as_str().into());
- None
- },
- Rule::relative_reference => {
- Some(Url::parse_relative(inner.as_str())?)
- },
- _ => unreachable!()
- }
- } else {
- refname.push(trimmed_text.into());
- None
- };
- children.push(trimmed_text.into());
- },
- _ => unreachable!()
- }
- },
- Rule::reference_explicit => unimplemented!("explicit reference"),
- Rule::reference_auto => {
- let rt_inner = concrete.into_inner().next().unwrap();
- match rt_inner.as_rule() {
- Rule::url_auto => match Url::parse_absolute(rt_inner.as_str()) {
- Ok(target) => {
- refuri = Some(target);
- name = None;
- refid = None;
- children.push(rt_inner.as_str().into());
- },
- // if our parser got a URL wrong, return it as a string
- Err(_) => return Ok(rt_inner.as_str().into())
- },
- Rule::email => {
- let mailto_url = String::from("mailto:") + rt_inner.as_str();
- match Url::parse_absolute(&mailto_url) {
- Ok(target) => {
- refuri = Some(target);
- name = None;
- refid = None;
- children.push(rt_inner.as_str().into());
- },
- // if our parser got a URL wrong, return it as a string
- Err(_) => return Ok(rt_inner.as_str().into())
- }
- },
- _ => unreachable!()
- }
- },
- _ => unreachable!(),
- };
- Ok(e::Reference::new(
- Default::default(),
- a::Reference { name, refuri, refid, refname },
- children
- ).into())
+ let name;
+ let refuri;
+ let refid;
+ let mut refname = vec![];
+ let mut children: Vec<c::TextOrInlineElement> = vec![];
+ let concrete = pair.into_inner().next().unwrap();
+ match concrete.as_rule() {
+ Rule::reference_target => {
+ let rt_inner = concrete.into_inner().next().unwrap(); // reference_target_uq or target_name_qu
+ match rt_inner.as_rule() {
+ Rule::reference_target_uq => {
+ refid = None;
+ name = Some(rt_inner.as_str().into());
+ refuri = None;
+ refname.push(rt_inner.as_str().into());
+ children.push(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!(),
+ };
+ refid = None;
+ name = Some(trimmed_text.into());
+ refuri = 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)
+ } else if inner.as_str().ends_with('_') {
+ // like target_name_qu (minus the final underscore)
+ let full_str = inner.as_str();
+ refname.push(full_str[0..full_str.len() - 1].into());
+ None
+ } else {
+ // like relative_reference
+ Some(Url::parse_relative(inner.as_str())?)
+ }
+ }
+ Rule::target_name_qu => {
+ refname.push(inner.as_str().into());
+ None
+ }
+ Rule::relative_reference => Some(Url::parse_relative(inner.as_str())?),
+ _ => unreachable!(),
+ }
+ } else {
+ refname.push(trimmed_text.into());
+ None
+ };
+ children.push(trimmed_text.into());
+ }
+ _ => unreachable!(),
+ }
+ }
+ Rule::reference_explicit => unimplemented!("explicit reference"),
+ Rule::reference_auto => {
+ let rt_inner = concrete.into_inner().next().unwrap();
+ match rt_inner.as_rule() {
+ Rule::url_auto => match Url::parse_absolute(rt_inner.as_str()) {
+ Ok(target) => {
+ refuri = Some(target);
+ name = None;
+ refid = None;
+ children.push(rt_inner.as_str().into());
+ }
+ // if our parser got a URL wrong, return it as a string
+ Err(_) => return Ok(rt_inner.as_str().into()),
+ },
+ Rule::email => {
+ let mailto_url = String::from("mailto:") + rt_inner.as_str();
+ match Url::parse_absolute(&mailto_url) {
+ Ok(target) => {
+ refuri = Some(target);
+ name = None;
+ refid = None;
+ children.push(rt_inner.as_str().into());
+ }
+ // if our parser got a URL wrong, return it as a string
+ Err(_) => return Ok(rt_inner.as_str().into()),
+ }
+ }
+ _ => unreachable!(),
+ }
+ }
+ _ => unreachable!(),
+ };
+ Ok(e::Reference::new(
+ Default::default(),
+ a::Reference {
+ name,
+ refuri,
+ refid,
+ refname,
+ },
+ children,
+ )
+ .into())
}
fn convert_substitution_ref(pair: Pair<Rule>) -> Result<e::SubstitutionReference, Error> {
- let name = whitespace_normalize_name(pair.as_str());
- Ok(a::ExtraAttributes::with_extra(
- a::SubstitutionReference {
- refname: vec![at::NameToken(name)]
- }
- ))
+ let name = whitespace_normalize_name(pair.as_str());
+ Ok(a::ExtraAttributes::with_extra(a::SubstitutionReference {
+ refname: vec![at::NameToken(name)],
+ }))
}
diff --git a/parser/src/conversion/tests.rs b/parser/src/conversion/tests.rs
index 89b0a1c..8fcb408 100644
--- a/parser/src/conversion/tests.rs
+++ b/parser/src/conversion/tests.rs
@@ -1,19 +1,15 @@
-use document_tree::{
- elements as e,
- element_categories as c,
- HasChildren,
-};
+use document_tree::{element_categories as c, elements as e, HasChildren};
use crate::parse;
fn ssubel_to_section(ssubel: &c::StructuralSubElement) -> &e::Section {
- match ssubel {
- c::StructuralSubElement::SubStructure(ref b) => match **b {
- c::SubStructure::Section(ref s) => s,
- ref c => panic!("Expected section, not {:?}", c),
- },
- ref c => panic!("Expected SubStructure, not {:?}", c),
- }
+ match ssubel {
+ c::StructuralSubElement::SubStructure(ref b) => match **b {
+ c::SubStructure::Section(ref s) => s,
+ ref c => panic!("Expected section, not {:?}", c),
+ },
+ ref c => panic!("Expected SubStructure, not {:?}", c),
+ }
}
const SECTIONS: &str = "\
@@ -38,28 +34,61 @@ 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 lvl1a = ssubel_to_section(&lvl0[1]).children();
- assert_eq!(lvl1a.len(), 2, "The 1st lvl1 section should have (a title and) a single lvl2 section as child: {:?}", lvl1a);
- //TODO: test title lvl1a[0]
- let lvl2 = ssubel_to_section(&lvl1a[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 lvl3a = ssubel_to_section(&lvl2[1]).children();
- assert_eq!(lvl3a.len(), 1, "The 1st lvl3 section should just a title: {:?}", lvl3a);
- //TODO: test title lvl3a[0]
-
- let lvl1b = ssubel_to_section(&lvl0[2]).children();
- assert_eq!(lvl1b.len(), 2, "The 2nd lvl1 section should have (a title and) a single lvl2 section as child: {:?}", lvl1b);
- //TODO: test title lvl1b[0]
- let lvl3b = ssubel_to_section(&lvl1b[1]).children();
- assert_eq!(lvl3b.len(), 1, "The 2nd lvl3 section should have just a title: {:?}", lvl3b);
- //TODO: test title lvl3b[0]
+ 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 lvl1a = ssubel_to_section(&lvl0[1]).children();
+ assert_eq!(
+ lvl1a.len(),
+ 2,
+ "The 1st lvl1 section should have (a title and) a single lvl2 section as child: {:?}",
+ lvl1a
+ );
+ //TODO: test title lvl1a[0]
+ let lvl2 = ssubel_to_section(&lvl1a[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 lvl3a = ssubel_to_section(&lvl2[1]).children();
+ assert_eq!(
+ lvl3a.len(),
+ 1,
+ "The 1st lvl3 section should just a title: {:?}",
+ lvl3a
+ );
+ //TODO: test title lvl3a[0]
+
+ let lvl1b = ssubel_to_section(&lvl0[2]).children();
+ assert_eq!(
+ lvl1b.len(),
+ 2,
+ "The 2nd lvl1 section should have (a title and) a single lvl2 section as child: {:?}",
+ lvl1b
+ );
+ //TODO: test title lvl1b[0]
+ let lvl3b = ssubel_to_section(&lvl1b[1]).children();
+ assert_eq!(
+ lvl3b.len(),
+ 1,
+ "The 2nd lvl3 section should have just a title: {:?}",
+ lvl3b
+ );
+ //TODO: test title lvl3b[0]
}
diff --git a/parser/src/lib.rs b/parser/src/lib.rs
index 23e97c7..303e26a 100644
--- a/parser/src/lib.rs
+++ b/parser/src/lib.rs
@@ -1,28 +1,27 @@
-pub mod token;
mod conversion;
-mod simplify;
-mod pest_rst;
mod pair_ext_parse;
+mod pest_rst;
+mod simplify;
#[cfg(test)]
pub mod tests;
+pub mod token;
use failure::Error;
use pest::Parser;
use document_tree::Document;
-use self::pest_rst::{RstParser,Rule};
use self::conversion::convert_document;
+use self::pest_rst::{RstParser, Rule};
use self::simplify::resolve_references;
-
/// Parse into a document tree and resolve sections, but not references.
pub fn parse_only(source: &str) -> Result<Document, Error> {
- let pairs = RstParser::parse(Rule::document, source)?;
- convert_document(pairs)
+ let pairs = RstParser::parse(Rule::document, source)?;
+ convert_document(pairs)
}
-/// Parse into a document tree and resolve sections and references.
+/// Parse into a document tree and resolve sections and references.
pub fn parse(source: &str) -> Result<Document, Error> {
- parse_only(source).map(resolve_references)
+ parse_only(source).map(resolve_references)
}
diff --git a/parser/src/pair_ext_parse.rs b/parser/src/pair_ext_parse.rs
index a04b3dd..cbb51b0 100644
--- a/parser/src/pair_ext_parse.rs
+++ b/parser/src/pair_ext_parse.rs
@@ -1,21 +1,41 @@
use std::str::FromStr;
-use pest::Span;
+use pest::error::{Error, ErrorVariant};
use pest::iterators::Pair;
-use pest::error::{Error,ErrorVariant};
-
+use pest::Span;
-pub trait PairExt<R> where R: pest::RuleType {
- fn parse<T, E>(&self) -> Result<T, Error<R>> where T: FromStr<Err = E>, E: ToString;
+pub trait PairExt<R>
+where
+ R: pest::RuleType,
+{
+ fn parse<T, E>(&self) -> Result<T, Box<Error<R>>>
+ where
+ T: FromStr<Err = E>,
+ E: ToString;
}
-impl<'l, R> PairExt<R> for Pair<'l, R> where R: pest::RuleType {
- fn parse<T, E>(&self) -> Result<T, Error<R>> where T: FromStr<Err = E>, E: ToString {
- self.as_str().parse().map_err(|e| to_parse_error(self.as_span(), &e))
- }
+impl<'l, R> PairExt<R> for Pair<'l, R>
+where
+ R: pest::RuleType,
+{
+ fn parse<T, E>(&self) -> Result<T, Box<Error<R>>>
+ where
+ T: FromStr<Err = E>,
+ E: ToString,
+ {
+ self.as_str()
+ .parse()
+ .map_err(|e| to_parse_error(self.as_span(), &e))
+ }
}
-pub(crate) fn to_parse_error<E, R>(span: Span, e: &E) -> Error<R> where E: ToString, R: pest::RuleType {
- let var: ErrorVariant<R> = ErrorVariant::CustomError { message: e.to_string() };
- Error::new_from_span(var, span)
+pub(crate) fn to_parse_error<E, R>(span: Span, e: &E) -> Box<Error<R>>
+where
+ E: ToString,
+ R: pest::RuleType,
+{
+ let var: ErrorVariant<R> = ErrorVariant::CustomError {
+ message: e.to_string(),
+ };
+ Box::new(Error::new_from_span(var, span))
}
diff --git a/parser/src/rst.pest b/parser/src/rst.pest
index d9fb668..f942547 100644
--- a/parser/src/rst.pest
+++ b/parser/src/rst.pest
@@ -5,7 +5,7 @@
// Section headers define the hierarchy by their delimiters,
// and pest only has one stack that we need for indentation.
-document = _{ SOI ~ blocks ~ EOI }
+document = _{ SOI ~ blank_line* ~ blocks ~ EOI }
blocks = _{ block ~ (blank_line* ~ block)* ~ blank_line? }
block = _{ PEEK[..] ~ hanging_block }
@@ -257,10 +257,10 @@ known_scheme = {
"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})
+ ASCII_ALPHANUMERIC |
+ "!"|"$"|"&"|"'"|"("|")"|"*"|"+"|","|"-"|"."|"/"|":"|";"|"="|"?"|"@"|"_"|"~" |
+ (!(SURROGATE|NONCHARACTER_CODE_POINT) ~ '\u{A0}'..'\u{10FFFD}') |
+ ("%" ~ ASCII_HEX_DIGIT{2})
}
/*
diff --git a/parser/src/simplify.rs b/parser/src/simplify.rs
index 4c254af..17e9ee3 100644
--- a/parser/src/simplify.rs
+++ b/parser/src/simplify.rs
@@ -4,15 +4,15 @@ http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#hyperlink-tar
Links can have internal or external targets.
In the source, targets look like:
- .. targetname1:
- .. targetname2:
+ .. targetname1:
+ .. targetname2:
- some paragraph or list item or so
+ some paragraph or list item or so
or:
.. targetname1:
- .. targetname2: https://link
+ .. targetname2: https://link
There’s also anonymous links and targets without names.
@@ -22,641 +22,692 @@ TODO: continue documenting how it’s done via https://repo.or.cz/docutils.git/b
use std::collections::HashMap;
use document_tree::{
- url::Url,
- Document,
- HasChildren,
- attribute_types::NameToken,
- elements::{self as e, Element},
- element_categories as c,
- extra_attributes::ExtraAttributes,
+ attribute_types::NameToken,
+ element_categories as c,
+ elements::{self as e, Element},
+ extra_attributes::ExtraAttributes,
+ url::Url,
+ Document, HasChildren,
};
-
#[derive(Debug)]
+#[allow(dead_code)]
enum NamedTargetType {
- NumberedFootnote(usize),
- LabeledFootnote(usize),
- Citation,
- InternalLink,
- ExternalLink(Url),
- IndirectLink(NameToken),
- SectionTitle,
+ NumberedFootnote(usize),
+ LabeledFootnote(usize),
+ Citation,
+ InternalLink,
+ ExternalLink(Url),
+ IndirectLink(NameToken),
+ SectionTitle,
}
impl NamedTargetType {
- fn is_implicit_target(&self) -> bool {
- match self {
- NamedTargetType::SectionTitle => true,
- _ => false,
- }
- }
+ #[allow(dead_code)]
+ fn is_implicit_target(&self) -> bool {
+ matches!(self, NamedTargetType::SectionTitle)
+ }
}
#[derive(Clone, Debug)]
struct Substitution {
- content: Vec<c::TextOrInlineElement>,
- /// 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,
+ content: Vec<c::TextOrInlineElement>,
+ /// 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(Default, Debug)]
struct TargetsCollected {
- named_targets: HashMap<NameToken, NamedTargetType>,
- substitutions: HashMap<NameToken, Substitution>,
- normalized_substitutions: HashMap<String, Substitution>,
+ named_targets: HashMap<NameToken, NamedTargetType>,
+ substitutions: HashMap<NameToken, Substitution>,
+ normalized_substitutions: HashMap<String, Substitution>,
}
impl TargetsCollected {
- fn target_url<'t>(self: &'t TargetsCollected, refname: &[NameToken]) -> Option<&'t Url> {
- // TODO: Check if the target would expand circularly
- if refname.len() != 1 {
- panic!("Expected exactly one name in a reference.");
- }
- let name = refname[0].clone();
- match self.named_targets.get(&name)? {
- NamedTargetType::ExternalLink(url) => Some(url),
- _ => unimplemented!(),
- }
- }
-
- fn substitution<'t>(self: &'t TargetsCollected, refname: &[NameToken]) -> Option<&'t Substitution> {
- // TODO: Check if the substitution would expand circularly
- if refname.len() != 1 {
- panic!("Expected exactly one name in a substitution reference.");
- }
- let name = refname[0].clone();
- self.substitutions.get(&name).or_else(|| {
- self.normalized_substitutions.get(&name.0.to_lowercase())
- })
- }
+ fn target_url<'t>(self: &'t TargetsCollected, refname: &[NameToken]) -> Option<&'t Url> {
+ // TODO: Check if the target would expand circularly
+ if refname.len() != 1 {
+ panic!("Expected exactly one name in a reference.");
+ }
+ let name = refname[0].clone();
+ match self.named_targets.get(&name)? {
+ NamedTargetType::ExternalLink(url) => Some(url),
+ _ => unimplemented!(),
+ }
+ }
+
+ fn substitution<'t>(
+ self: &'t TargetsCollected,
+ refname: &[NameToken],
+ ) -> Option<&'t Substitution> {
+ // TODO: Check if the substitution would expand circularly
+ if refname.len() != 1 {
+ panic!("Expected exactly one name in a substitution reference.");
+ }
+ let name = refname[0].clone();
+ self.substitutions
+ .get(&name)
+ .or_else(|| self.normalized_substitutions.get(&name.0.to_lowercase()))
+ }
}
trait ResolvableRefs {
- fn populate_targets(&self, refs: &mut TargetsCollected);
- fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> where Self: Sized;
+ fn populate_targets(&self, refs: &mut TargetsCollected);
+ fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self>
+ where
+ Self: Sized;
}
pub fn resolve_references(mut doc: Document) -> Document {
- let mut references: TargetsCollected = Default::default();
- for c in doc.children() {
- c.populate_targets(&mut references);
- }
- let new: Vec<_> = doc.children_mut().drain(..).flat_map(|c| c.resolve_refs(&references)).collect();
- Document::with_children(new)
+ let mut references: TargetsCollected = Default::default();
+ for c in doc.children() {
+ c.populate_targets(&mut references);
+ }
+ let new: Vec<_> = doc
+ .children_mut()
+ .drain(..)
+ .flat_map(|c| c.resolve_refs(&references))
+ .collect();
+ Document::with_children(new)
}
-fn sub_pop<P, C>(parent: &P, refs: &mut TargetsCollected) where P: HasChildren<C>, C: ResolvableRefs {
- for c in parent.children() {
- c.populate_targets(refs);
- }
+fn sub_pop<P, C>(parent: &P, refs: &mut TargetsCollected)
+where
+ P: HasChildren<C>,
+ C: ResolvableRefs,
+{
+ for c in parent.children() {
+ c.populate_targets(refs);
+ }
}
-fn sub_res<P, C>(mut parent: P, refs: &TargetsCollected) -> P where P: e::Element + HasChildren<C>, C: ResolvableRefs {
- let new: Vec<_> = parent.children_mut().drain(..).flat_map(|c| c.resolve_refs(refs)).collect();
- parent.children_mut().extend(new);
- parent
+fn sub_res<P, C>(mut parent: P, refs: &TargetsCollected) -> P
+where
+ P: e::Element + HasChildren<C>,
+ C: ResolvableRefs,
+{
+ let new: Vec<_> = parent
+ .children_mut()
+ .drain(..)
+ .flat_map(|c| c.resolve_refs(refs))
+ .collect();
+ parent.children_mut().extend(new);
+ parent
}
-fn sub_sub_pop<P, C1, C2>(parent: &P, refs: &mut TargetsCollected) where P: HasChildren<C1>, C1: HasChildren<C2>, C2: ResolvableRefs {
- for c in parent.children() {
- sub_pop(c, refs);
- }
+fn sub_sub_pop<P, C1, C2>(parent: &P, refs: &mut TargetsCollected)
+where
+ P: HasChildren<C1>,
+ C1: HasChildren<C2>,
+ C2: ResolvableRefs,
+{
+ for c in parent.children() {
+ sub_pop(c, refs);
+ }
}
-fn sub_sub_res<P, C1, C2>(mut parent: P, refs: &TargetsCollected) -> P where P: e::Element + HasChildren<C1>, C1: e::Element + HasChildren<C2>, C2: ResolvableRefs {
- let new: Vec<_> = parent.children_mut().drain(..).map(|c| sub_res(c, refs)).collect();
- parent.children_mut().extend(new);
- parent
+fn sub_sub_res<P, C1, C2>(mut parent: P, refs: &TargetsCollected) -> P
+where
+ P: e::Element + HasChildren<C1>,
+ C1: e::Element + HasChildren<C2>,
+ C2: ResolvableRefs,
+{
+ let new: Vec<_> = parent
+ .children_mut()
+ .drain(..)
+ .map(|c| sub_res(c, refs))
+ .collect();
+ parent.children_mut().extend(new);
+ parent
}
impl ResolvableRefs for c::StructuralSubElement {
- fn populate_targets(&self, refs: &mut TargetsCollected) {
- use c::StructuralSubElement::*;
- match self {
- Title(e) => sub_pop(&**e, refs),
- Subtitle(e) => sub_pop(&**e, refs),
- Decoration(e) => sub_pop(&**e, refs),
- Docinfo(e) => sub_pop(&**e, refs),
- SubStructure(e) => e.populate_targets(refs),
- }
- }
- fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
- use c::StructuralSubElement::*;
- vec![match self {
- Title(e) => sub_res(*e, refs).into(),
- Subtitle(e) => sub_res(*e, refs).into(),
- Decoration(e) => sub_res(*e, refs).into(),
- Docinfo(e) => sub_res(*e, refs).into(),
- SubStructure(e) => return e.resolve_refs(refs).drain(..).map(Into::into).collect(),
- }]
- }
+ fn populate_targets(&self, refs: &mut TargetsCollected) {
+ use c::StructuralSubElement::*;
+ match self {
+ Title(e) => sub_pop(&**e, refs),
+ Subtitle(e) => sub_pop(&**e, refs),
+ Decoration(e) => sub_pop(&**e, refs),
+ Docinfo(e) => sub_pop(&**e, refs),
+ SubStructure(e) => e.populate_targets(refs),
+ }
+ }
+ fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
+ use c::StructuralSubElement::*;
+ vec![match self {
+ Title(e) => sub_res(*e, refs).into(),
+ Subtitle(e) => sub_res(*e, refs).into(),
+ Decoration(e) => sub_res(*e, refs).into(),
+ Docinfo(e) => sub_res(*e, refs).into(),
+ SubStructure(e) => return e.resolve_refs(refs).drain(..).map(Into::into).collect(),
+ }]
+ }
}
impl ResolvableRefs for c::SubStructure {
- fn populate_targets(&self, refs: &mut TargetsCollected) {
- use c::SubStructure::*;
- match self {
- Topic(e) => sub_pop(&**e, refs),
- Sidebar(e) => sub_pop(&**e, refs),
- Transition(_) => {},
- Section(e) => sub_pop(&**e, refs),
- BodyElement(e) => e.populate_targets(refs),
- }
- }
- fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
- use c::SubStructure::*;
- vec![match self {
- Topic(e) => sub_res(*e, refs).into(),
- Sidebar(e) => sub_res(*e, refs).into(),
- Transition(e) => Transition(e),
- Section(e) => sub_res(*e, refs).into(),
- BodyElement(e) => return e.resolve_refs(refs).drain(..).map(Into::into).collect(),
- }]
- }
+ fn populate_targets(&self, refs: &mut TargetsCollected) {
+ use c::SubStructure::*;
+ match self {
+ Topic(e) => sub_pop(&**e, refs),
+ Sidebar(e) => sub_pop(&**e, refs),
+ Transition(_) => {}
+ Section(e) => sub_pop(&**e, refs),
+ BodyElement(e) => e.populate_targets(refs),
+ }
+ }
+ fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
+ use c::SubStructure::*;
+ vec![match self {
+ Topic(e) => sub_res(*e, refs).into(),
+ Sidebar(e) => sub_res(*e, refs).into(),
+ Transition(e) => Transition(e),
+ Section(e) => sub_res(*e, refs).into(),
+ BodyElement(e) => return e.resolve_refs(refs).drain(..).map(Into::into).collect(),
+ }]
+ }
}
impl ResolvableRefs for c::BodyElement {
- fn populate_targets(&self, refs: &mut TargetsCollected) {
- use c::BodyElement::*;
- match self {
- Paragraph(e) => sub_pop(&**e, refs),
- LiteralBlock(e) => sub_pop(&**e, refs),
- DoctestBlock(e) => sub_pop(&**e, refs),
- MathBlock(_) => {},
- Rubric(e) => sub_pop(&**e, refs),
- SubstitutionDefinition(e) => {
- let subst = Substitution {
- content: e.children().clone(),
- ltrim: e.extra().ltrim,
- rtrim: e.extra().rtrim
- };
- for name in e.names() {
- if refs.substitutions.contains_key(name) {
- // TODO: Duplicate substitution name (level 3 system message).
- }
- // Intentionally overriding any previous values.
- refs.substitutions.insert(name.clone(), subst.clone());
- refs.normalized_substitutions.insert(name.0.to_lowercase(), subst.clone());
- }
- },
- Comment(_) => {},
- Pending(_) => {
- unimplemented!();
- },
- Target(e) => {
- if let Some(uri) = &e.extra().refuri {
- for name in e.names() {
- refs.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.
- },
- Raw(_) => {},
- Image(_) => {},
- Compound(e) => sub_pop(&**e, refs),
- Container(e) => sub_pop(&**e, refs),
- BulletList(e) => sub_sub_pop(&**e, refs),
- EnumeratedList(e) => sub_sub_pop(&**e, refs),
- DefinitionList(e) => sub_sub_pop(&**e, refs),
- FieldList(e) => sub_sub_pop(&**e, refs),
- OptionList(e) => sub_sub_pop(&**e, refs),
- LineBlock(e) => sub_pop(&**e, refs),
- BlockQuote(e) => sub_pop(&**e, refs),
- Admonition(e) => sub_pop(&**e, refs),
- Attention(e) => sub_pop(&**e, refs),
- Hint(e) => sub_pop(&**e, refs),
- Note(e) => sub_pop(&**e, refs),
- Caution(e) => sub_pop(&**e, refs),
- Danger(e) => sub_pop(&**e, refs),
- Error(e) => sub_pop(&**e, refs),
- Important(e) => sub_pop(&**e, refs),
- Tip(e) => sub_pop(&**e, refs),
- Warning(e) => sub_pop(&**e, refs),
- Footnote(e) => sub_pop(&**e, refs),
- Citation(e) => sub_pop(&**e, refs),
- SystemMessage(e) => sub_pop(&**e, refs),
- Figure(e) => sub_pop(&**e, refs),
- Table(e) => sub_pop(&**e, refs)
- }
- }
- fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
- use c::BodyElement::*;
- vec![match self {
- Paragraph(e) => sub_res(*e, refs).into(),
- LiteralBlock(e) => sub_res(*e, refs).into(),
- DoctestBlock(e) => sub_res(*e, refs).into(),
- MathBlock(e) => MathBlock(e),
- Rubric(e) => sub_res(*e, refs).into(),
- SubstitutionDefinition(_) => return vec![],
- Comment(e) => Comment(e),
- Pending(e) => Pending(e),
- Target(e) => Target(e),
- Raw(e) => Raw(e),
- Image(e) => Image(e),
- Compound(e) => sub_res(*e, refs).into(),
- Container(e) => sub_res(*e, refs).into(),
- BulletList(e) => sub_sub_res(*e, refs).into(),
- EnumeratedList(e) => sub_sub_res(*e, refs).into(),
- DefinitionList(e) => sub_sub_res(*e, refs).into(),
- FieldList(e) => sub_sub_res(*e, refs).into(),
- OptionList(e) => sub_sub_res(*e, refs).into(),
- LineBlock(e) => sub_res(*e, refs).into(),
- BlockQuote(e) => sub_res(*e, refs).into(),
- Admonition(e) => sub_res(*e, refs).into(),
- Attention(e) => sub_res(*e, refs).into(),
- Hint(e) => sub_res(*e, refs).into(),
- Note(e) => sub_res(*e, refs).into(),
- Caution(e) => sub_res(*e, refs).into(),
- Danger(e) => sub_res(*e, refs).into(),
- Error(e) => sub_res(*e, refs).into(),
- Important(e) => sub_res(*e, refs).into(),
- Tip(e) => sub_res(*e, refs).into(),
- Warning(e) => sub_res(*e, refs).into(),
- Footnote(e) => sub_res(*e, refs).into(),
- Citation(e) => sub_res(*e, refs).into(),
- SystemMessage(e) => sub_res(*e, refs).into(),
- Figure(e) => sub_res(*e, refs).into(),
- Table(e) => sub_res(*e, refs).into()
- }]
- }
+ fn populate_targets(&self, refs: &mut TargetsCollected) {
+ use c::BodyElement::*;
+ match self {
+ Paragraph(e) => sub_pop(&**e, refs),
+ LiteralBlock(e) => sub_pop(&**e, refs),
+ DoctestBlock(e) => sub_pop(&**e, refs),
+ MathBlock(_) => {}
+ Rubric(e) => sub_pop(&**e, refs),
+ SubstitutionDefinition(e) => {
+ let subst = Substitution {
+ content: e.children().clone(),
+ ltrim: e.extra().ltrim,
+ rtrim: e.extra().rtrim,
+ };
+ for name in e.names() {
+ if refs.substitutions.contains_key(name) {
+ // TODO: Duplicate substitution name (level 3 system message).
+ }
+ // Intentionally overriding any previous values.
+ refs.substitutions.insert(name.clone(), subst.clone());
+ refs.normalized_substitutions
+ .insert(name.0.to_lowercase(), subst.clone());
+ }
+ }
+ Comment(_) => {}
+ Pending(_) => {
+ unimplemented!();
+ }
+ Target(e) => {
+ if let Some(uri) = &e.extra().refuri {
+ for name in e.names() {
+ refs.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.
+ }
+ Raw(_) => {}
+ Image(_) => {}
+ Compound(e) => sub_pop(&**e, refs),
+ Container(e) => sub_pop(&**e, refs),
+ BulletList(e) => sub_sub_pop(&**e, refs),
+ EnumeratedList(e) => sub_sub_pop(&**e, refs),
+ DefinitionList(e) => sub_sub_pop(&**e, refs),
+ FieldList(e) => sub_sub_pop(&**e, refs),
+ OptionList(e) => sub_sub_pop(&**e, refs),
+ LineBlock(e) => sub_pop(&**e, refs),
+ BlockQuote(e) => sub_pop(&**e, refs),
+ Admonition(e) => sub_pop(&**e, refs),
+ Attention(e) => sub_pop(&**e, refs),
+ Hint(e) => sub_pop(&**e, refs),
+ Note(e) => sub_pop(&**e, refs),
+ Caution(e) => sub_pop(&**e, refs),
+ Danger(e) => sub_pop(&**e, refs),
+ Error(e) => sub_pop(&**e, refs),
+ Important(e) => sub_pop(&**e, refs),
+ Tip(e) => sub_pop(&**e, refs),
+ Warning(e) => sub_pop(&**e, refs),
+ Footnote(e) => sub_pop(&**e, refs),
+ Citation(e) => sub_pop(&**e, refs),
+ SystemMessage(e) => sub_pop(&**e, refs),
+ Figure(e) => sub_pop(&**e, refs),
+ Table(e) => sub_pop(&**e, refs),
+ }
+ }
+ fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
+ use c::BodyElement::*;
+ vec![match self {
+ Paragraph(e) => sub_res(*e, refs).into(),
+ LiteralBlock(e) => sub_res(*e, refs).into(),
+ DoctestBlock(e) => sub_res(*e, refs).into(),
+ MathBlock(e) => MathBlock(e),
+ Rubric(e) => sub_res(*e, refs).into(),
+ SubstitutionDefinition(_) => return vec![],
+ Comment(e) => Comment(e),
+ Pending(e) => Pending(e),
+ Target(e) => Target(e),
+ Raw(e) => Raw(e),
+ Image(e) => Image(e),
+ Compound(e) => sub_res(*e, refs).into(),
+ Container(e) => sub_res(*e, refs).into(),
+ BulletList(e) => sub_sub_res(*e, refs).into(),
+ EnumeratedList(e) => sub_sub_res(*e, refs).into(),
+ DefinitionList(e) => sub_sub_res(*e, refs).into(),
+ FieldList(e) => sub_sub_res(*e, refs).into(),
+ OptionList(e) => sub_sub_res(*e, refs).into(),
+ LineBlock(e) => sub_res(*e, refs).into(),
+ BlockQuote(e) => sub_res(*e, refs).into(),
+ Admonition(e) => sub_res(*e, refs).into(),
+ Attention(e) => sub_res(*e, refs).into(),
+ Hint(e) => sub_res(*e, refs).into(),
+ Note(e) => sub_res(*e, refs).into(),
+ Caution(e) => sub_res(*e, refs).into(),
+ Danger(e) => sub_res(*e, refs).into(),
+ Error(e) => sub_res(*e, refs).into(),
+ Important(e) => sub_res(*e, refs).into(),
+ Tip(e) => sub_res(*e, refs).into(),
+ Warning(e) => sub_res(*e, refs).into(),
+ Footnote(e) => sub_res(*e, refs).into(),
+ Citation(e) => sub_res(*e, refs).into(),
+ SystemMessage(e) => sub_res(*e, refs).into(),
+ Figure(e) => sub_res(*e, refs).into(),
+ Table(e) => sub_res(*e, refs).into(),
+ }]
+ }
}
impl ResolvableRefs for c::BibliographicElement {
- fn populate_targets(&self, refs: &mut TargetsCollected) {
- use c::BibliographicElement::*;
- match self {
- Author(e) => sub_pop(&**e, refs),
- Authors(e) => sub_pop(&**e, refs),
- Organization(e) => sub_pop(&**e, refs),
- Address(e) => sub_pop(&**e, refs),
- Contact(e) => sub_pop(&**e, refs),
- Version(e) => sub_pop(&**e, refs),
- Revision(e) => sub_pop(&**e, refs),
- Status(e) => sub_pop(&**e, refs),
- Date(e) => sub_pop(&**e, refs),
- Copyright(e) => sub_pop(&**e, refs),
- Field(e) => sub_pop(&**e, refs),
- }
- }
- fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
- use c::BibliographicElement::*;
- vec![match self {
- Author(e) => sub_res(*e, refs).into(),
- Authors(e) => sub_res(*e, refs).into(),
- Organization(e) => sub_res(*e, refs).into(),
- Address(e) => sub_res(*e, refs).into(),
- Contact(e) => sub_res(*e, refs).into(),
- Version(e) => sub_res(*e, refs).into(),
- Revision(e) => sub_res(*e, refs).into(),
- Status(e) => sub_res(*e, refs).into(),
- Date(e) => sub_res(*e, refs).into(),
- Copyright(e) => sub_res(*e, refs).into(),
- Field(e) => sub_res(*e, refs).into(),
- }]
- }
+ fn populate_targets(&self, refs: &mut TargetsCollected) {
+ use c::BibliographicElement::*;
+ match self {
+ Author(e) => sub_pop(&**e, refs),
+ Authors(e) => sub_pop(&**e, refs),
+ Organization(e) => sub_pop(&**e, refs),
+ Address(e) => sub_pop(&**e, refs),
+ Contact(e) => sub_pop(&**e, refs),
+ Version(e) => sub_pop(&**e, refs),
+ Revision(e) => sub_pop(&**e, refs),
+ Status(e) => sub_pop(&**e, refs),
+ Date(e) => sub_pop(&**e, refs),
+ Copyright(e) => sub_pop(&**e, refs),
+ Field(e) => sub_pop(&**e, refs),
+ }
+ }
+ fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
+ use c::BibliographicElement::*;
+ vec![match self {
+ Author(e) => sub_res(*e, refs).into(),
+ Authors(e) => sub_res(*e, refs).into(),
+ Organization(e) => sub_res(*e, refs).into(),
+ Address(e) => sub_res(*e, refs).into(),
+ Contact(e) => sub_res(*e, refs).into(),
+ Version(e) => sub_res(*e, refs).into(),
+ Revision(e) => sub_res(*e, refs).into(),
+ Status(e) => sub_res(*e, refs).into(),
+ Date(e) => sub_res(*e, refs).into(),
+ Copyright(e) => sub_res(*e, refs).into(),
+ Field(e) => sub_res(*e, refs).into(),
+ }]
+ }
}
impl ResolvableRefs for c::TextOrInlineElement {
- fn populate_targets(&self, refs: &mut TargetsCollected) {
- use c::TextOrInlineElement::*;
- match self {
- String(_) => {},
- Emphasis(e) => sub_pop(&**e, refs),
- Strong(e) => sub_pop(&**e, refs),
- Literal(_) => {},
- Reference(e) => sub_pop(&**e, refs),
- FootnoteReference(e) => sub_pop(&**e, refs),
- CitationReference(e) => sub_pop(&**e, refs),
- SubstitutionReference(e) => sub_pop(&**e, refs),
- TitleReference(e) => sub_pop(&**e, refs),
- Abbreviation(e) => sub_pop(&**e, refs),
- Acronym(e) => sub_pop(&**e, refs),
- Superscript(e) => sub_pop(&**e, refs),
- Subscript(e) => sub_pop(&**e, refs),
- Inline(e) => sub_pop(&**e, refs),
- Problematic(e) => sub_pop(&**e, refs),
- Generated(e) => sub_pop(&**e, refs),
- Math(_) => {},
- TargetInline(_) => {
- unimplemented!();
- },
- RawInline(_) => {},
- ImageInline(_) => {}
- }
- }
- fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
- use c::TextOrInlineElement::*;
- vec![match self {
- String(e) => String(e),
- Emphasis(e) => sub_res(*e, refs).into(),
- Strong(e) => sub_res(*e, refs).into(),
- Literal(e) => Literal(e),
- Reference(mut e) => {
- if e.extra().refuri.is_none() {
- if let Some(uri) = refs.target_url(&e.extra().refname) {
- e.extra_mut().refuri = Some(uri.clone());
- }
- }
- (*e).into()
- },
- FootnoteReference(e) => sub_res(*e, refs).into(),
- CitationReference(e) => sub_res(*e, refs).into(),
- SubstitutionReference(e) => match refs.substitution(&e.extra().refname) {
- Some(Substitution {content, ltrim, rtrim}) => {
- // (level 3 system message).
- // TODO: ltrim and rtrim.
- if *ltrim || *rtrim {
- dbg!(content, ltrim, rtrim);
- }
- return content.clone()
- },
- None => {
- // 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.
- use document_tree::Problematic;
- let mut replacement: Box<Problematic> = Box::new(Default::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.
- Problematic(replacement)
- }
- },
- TitleReference(e) => sub_res(*e, refs).into(),
- Abbreviation(e) => sub_res(*e, refs).into(),
- Acronym(e) => sub_res(*e, refs).into(),
- Superscript(e) => sub_res(*e, refs).into(),
- Subscript(e) => sub_res(*e, refs).into(),
- Inline(e) => sub_res(*e, refs).into(),
- Problematic(e) => sub_res(*e, refs).into(),
- Generated(e) => sub_res(*e, refs).into(),
- Math(e) => Math(e),
- TargetInline(e) => TargetInline(e),
- RawInline(e) => RawInline(e),
- ImageInline(e) => ImageInline(e)
- }]
- }
+ fn populate_targets(&self, refs: &mut TargetsCollected) {
+ use c::TextOrInlineElement::*;
+ match self {
+ String(_) => {}
+ Emphasis(e) => sub_pop(&**e, refs),
+ Strong(e) => sub_pop(&**e, refs),
+ Literal(_) => {}
+ Reference(e) => sub_pop(&**e, refs),
+ FootnoteReference(e) => sub_pop(&**e, refs),
+ CitationReference(e) => sub_pop(&**e, refs),
+ SubstitutionReference(e) => sub_pop(&**e, refs),
+ TitleReference(e) => sub_pop(&**e, refs),
+ Abbreviation(e) => sub_pop(&**e, refs),
+ Acronym(e) => sub_pop(&**e, refs),
+ Superscript(e) => sub_pop(&**e, refs),
+ Subscript(e) => sub_pop(&**e, refs),
+ Inline(e) => sub_pop(&**e, refs),
+ Problematic(e) => sub_pop(&**e, refs),
+ Generated(e) => sub_pop(&**e, refs),
+ Math(_) => {}
+ TargetInline(_) => {
+ unimplemented!();
+ }
+ RawInline(_) => {}
+ ImageInline(_) => {}
+ }
+ }
+ fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
+ use c::TextOrInlineElement::*;
+ vec![match self {
+ String(e) => String(e),
+ Emphasis(e) => sub_res(*e, refs).into(),
+ Strong(e) => sub_res(*e, refs).into(),
+ Literal(e) => Literal(e),
+ Reference(mut e) => {
+ if e.extra().refuri.is_none() {
+ if let Some(uri) = refs.target_url(&e.extra().refname) {
+ e.extra_mut().refuri = Some(uri.clone());
+ }
+ }
+ (*e).into()
+ }
+ FootnoteReference(e) => sub_res(*e, refs).into(),
+ CitationReference(e) => sub_res(*e, refs).into(),
+ SubstitutionReference(e) => match refs.substitution(&e.extra().refname) {
+ Some(Substitution {
+ content,
+ ltrim,
+ rtrim,
+ }) => {
+ // (level 3 system message).
+ // TODO: ltrim and rtrim.
+ if *ltrim || *rtrim {
+ dbg!(content, ltrim, rtrim);
+ }
+ return content.clone();
+ }
+ None => {
+ // 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.
+ use document_tree::Problematic;
+ let mut replacement: Box<Problematic> = 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.
+ Problematic(replacement)
+ }
+ },
+ TitleReference(e) => sub_res(*e, refs).into(),
+ Abbreviation(e) => sub_res(*e, refs).into(),
+ Acronym(e) => sub_res(*e, refs).into(),
+ Superscript(e) => sub_res(*e, refs).into(),
+ Subscript(e) => sub_res(*e, refs).into(),
+ Inline(e) => sub_res(*e, refs).into(),
+ Problematic(e) => sub_res(*e, refs).into(),
+ Generated(e) => sub_res(*e, refs).into(),
+ Math(e) => Math(e),
+ TargetInline(e) => TargetInline(e),
+ RawInline(e) => RawInline(e),
+ ImageInline(e) => ImageInline(e),
+ }]
+ }
}
impl ResolvableRefs for c::AuthorInfo {
- fn populate_targets(&self, refs: &mut TargetsCollected) {
- use c::AuthorInfo::*;
- match self {
- Author(e) => sub_pop(&**e, refs),
- Organization(e) => sub_pop(&**e, refs),
- Address(e) => sub_pop(&**e, refs),
- Contact(e) => sub_pop(&**e, refs),
- }
- }
- fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
- use c::AuthorInfo::*;
- vec![match self {
- Author(e) => sub_res(*e, refs).into(),
- Organization(e) => sub_res(*e, refs).into(),
- Address(e) => sub_res(*e, refs).into(),
- Contact(e) => sub_res(*e, refs).into(),
- }]
- }
+ fn populate_targets(&self, refs: &mut TargetsCollected) {
+ use c::AuthorInfo::*;
+ match self {
+ Author(e) => sub_pop(&**e, refs),
+ Organization(e) => sub_pop(&**e, refs),
+ Address(e) => sub_pop(&**e, refs),
+ Contact(e) => sub_pop(&**e, refs),
+ }
+ }
+ fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
+ use c::AuthorInfo::*;
+ vec![match self {
+ Author(e) => sub_res(*e, refs).into(),
+ Organization(e) => sub_res(*e, refs).into(),
+ Address(e) => sub_res(*e, refs).into(),
+ Contact(e) => sub_res(*e, refs).into(),
+ }]
+ }
}
impl ResolvableRefs for c::DecorationElement {
- fn populate_targets(&self, refs: &mut TargetsCollected) {
- use c::DecorationElement::*;
- match self {
- Header(e) => sub_pop(&**e, refs),
- Footer(e) => sub_pop(&**e, refs),
- }
- }
- fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
- use c::DecorationElement::*;
- vec![match self {
- Header(e) => sub_res(*e, refs).into(),
- Footer(e) => sub_res(*e, refs).into(),
- }]
- }
+ fn populate_targets(&self, refs: &mut TargetsCollected) {
+ use c::DecorationElement::*;
+ match self {
+ Header(e) => sub_pop(&**e, refs),
+ Footer(e) => sub_pop(&**e, refs),
+ }
+ }
+ fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
+ use c::DecorationElement::*;
+ vec![match self {
+ Header(e) => sub_res(*e, refs).into(),
+ Footer(e) => sub_res(*e, refs).into(),
+ }]
+ }
}
impl ResolvableRefs for c::SubTopic {
- fn populate_targets(&self, refs: &mut TargetsCollected) {
- use c::SubTopic::*;
- match self {
- Title(e) => sub_pop(&**e, refs),
- BodyElement(e) => e.populate_targets(refs),
- }
- }
- fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
- use c::SubTopic::*;
- match self {
- Title(e) => vec![sub_res(*e, refs).into()],
- BodyElement(e) => e.resolve_refs(refs).drain(..).map(Into::into).collect(),
- }
- }
+ fn populate_targets(&self, refs: &mut TargetsCollected) {
+ use c::SubTopic::*;
+ match self {
+ Title(e) => sub_pop(&**e, refs),
+ BodyElement(e) => e.populate_targets(refs),
+ }
+ }
+ fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
+ use c::SubTopic::*;
+ match self {
+ Title(e) => vec![sub_res(*e, refs).into()],
+ BodyElement(e) => e.resolve_refs(refs).drain(..).map(Into::into).collect(),
+ }
+ }
}
impl ResolvableRefs for c::SubSidebar {
- fn populate_targets(&self, refs: &mut TargetsCollected) {
- use c::SubSidebar::*;
- match self {
- Topic(e) => sub_pop(&**e, refs),
- Title(e) => sub_pop(&**e, refs),
- Subtitle(e) => sub_pop(&**e, refs),
- BodyElement(e) => e.populate_targets(refs),
- }
- }
- fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
- use c::SubSidebar::*;
- vec![match self {
- Topic(e) => sub_res(*e, refs).into(),
- Title(e) => sub_res(*e, refs).into(),
- Subtitle(e) => sub_res(*e, refs).into(),
- BodyElement(e) => return e.resolve_refs(refs).drain(..).map(Into::into).collect(),
- }]
- }
+ fn populate_targets(&self, refs: &mut TargetsCollected) {
+ use c::SubSidebar::*;
+ match self {
+ Topic(e) => sub_pop(&**e, refs),
+ Title(e) => sub_pop(&**e, refs),
+ Subtitle(e) => sub_pop(&**e, refs),
+ BodyElement(e) => e.populate_targets(refs),
+ }
+ }
+ fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
+ use c::SubSidebar::*;
+ vec![match self {
+ Topic(e) => sub_res(*e, refs).into(),
+ Title(e) => sub_res(*e, refs).into(),
+ Subtitle(e) => sub_res(*e, refs).into(),
+ BodyElement(e) => return e.resolve_refs(refs).drain(..).map(Into::into).collect(),
+ }]
+ }
}
impl ResolvableRefs for c::SubDLItem {
- fn populate_targets(&self, refs: &mut TargetsCollected) {
- use c::SubDLItem::*;
- match self {
- Term(e) => sub_pop(&**e, refs),
- Classifier(e) => sub_pop(&**e, refs),
- Definition(e) => sub_pop(&**e, refs),
- }
- }
- fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
- use c::SubDLItem::*;
- vec![match self {
- Term(e) => sub_res(*e, refs).into(),
- Classifier(e) => sub_res(*e, refs).into(),
- Definition(e) => sub_res(*e, refs).into(),
- }]
- }
+ fn populate_targets(&self, refs: &mut TargetsCollected) {
+ use c::SubDLItem::*;
+ match self {
+ Term(e) => sub_pop(&**e, refs),
+ Classifier(e) => sub_pop(&**e, refs),
+ Definition(e) => sub_pop(&**e, refs),
+ }
+ }
+ fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
+ use c::SubDLItem::*;
+ vec![match self {
+ Term(e) => sub_res(*e, refs).into(),
+ Classifier(e) => sub_res(*e, refs).into(),
+ Definition(e) => sub_res(*e, refs).into(),
+ }]
+ }
}
impl ResolvableRefs for c::SubField {
- fn populate_targets(&self, refs: &mut TargetsCollected) {
- use c::SubField::*;
- match self {
- FieldName(e) => sub_pop(&**e, refs),
- FieldBody(e) => sub_pop(&**e, refs),
- }
- }
- fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
- use c::SubField::*;
- vec![match self {
- FieldName(e) => sub_res(*e, refs).into(),
- FieldBody(e) => sub_res(*e, refs).into(),
- }]
- }
+ fn populate_targets(&self, refs: &mut TargetsCollected) {
+ use c::SubField::*;
+ match self {
+ FieldName(e) => sub_pop(&**e, refs),
+ FieldBody(e) => sub_pop(&**e, refs),
+ }
+ }
+ fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
+ use c::SubField::*;
+ vec![match self {
+ FieldName(e) => sub_res(*e, refs).into(),
+ FieldBody(e) => sub_res(*e, refs).into(),
+ }]
+ }
}
impl ResolvableRefs for c::SubOptionListItem {
- fn populate_targets(&self, refs: &mut TargetsCollected) {
- use c::SubOptionListItem::*;
- match self {
- OptionGroup(e) => sub_sub_pop(&**e, refs),
- Description(e) => sub_pop(&**e, refs),
- }
- }
- fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
- use c::SubOptionListItem::*;
- vec![match self {
- OptionGroup(e) => sub_sub_res(*e, refs).into(),
- Description(e) => sub_res(*e, refs).into(),
- }]
- }
+ fn populate_targets(&self, refs: &mut TargetsCollected) {
+ use c::SubOptionListItem::*;
+ match self {
+ OptionGroup(e) => sub_sub_pop(&**e, refs),
+ Description(e) => sub_pop(&**e, refs),
+ }
+ }
+ fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
+ use c::SubOptionListItem::*;
+ vec![match self {
+ OptionGroup(e) => sub_sub_res(*e, refs).into(),
+ Description(e) => sub_res(*e, refs).into(),
+ }]
+ }
}
impl ResolvableRefs for c::SubOption {
- fn populate_targets(&self, _: &mut TargetsCollected) {}
- fn resolve_refs(self, _: &TargetsCollected) -> Vec<Self> { vec![self] }
+ fn populate_targets(&self, _: &mut TargetsCollected) {}
+ fn resolve_refs(self, _: &TargetsCollected) -> Vec<Self> {
+ vec![self]
+ }
}
impl ResolvableRefs for c::SubLineBlock {
- fn populate_targets(&self, refs: &mut TargetsCollected) {
- use c::SubLineBlock::*;
- match self {
- LineBlock(e) => sub_pop(&**e, refs),
- Line(e) => sub_pop(&**e, refs),
- }
- }
- fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
- use c::SubLineBlock::*;
- vec![match self {
- LineBlock(e) => sub_res(*e, refs).into(),
- Line(e) => sub_res(*e, refs).into(),
- }]
- }
+ fn populate_targets(&self, refs: &mut TargetsCollected) {
+ use c::SubLineBlock::*;
+ match self {
+ LineBlock(e) => sub_pop(&**e, refs),
+ Line(e) => sub_pop(&**e, refs),
+ }
+ }
+ fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
+ use c::SubLineBlock::*;
+ vec![match self {
+ LineBlock(e) => sub_res(*e, refs).into(),
+ Line(e) => sub_res(*e, refs).into(),
+ }]
+ }
}
impl ResolvableRefs for c::SubBlockQuote {
- fn populate_targets(&self, refs: &mut TargetsCollected) {
- use c::SubBlockQuote::*;
- match self {
- Attribution(e) => sub_pop(&**e, refs),
- BodyElement(e) => e.populate_targets(refs),
- }
- }
- fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
- use c::SubBlockQuote::*;
- match self {
- Attribution(e) => vec![sub_res(*e, refs).into()],
- BodyElement(e) => e.resolve_refs(refs).drain(..).map(Into::into).collect(),
- }
- }
+ fn populate_targets(&self, refs: &mut TargetsCollected) {
+ use c::SubBlockQuote::*;
+ match self {
+ Attribution(e) => sub_pop(&**e, refs),
+ BodyElement(e) => e.populate_targets(refs),
+ }
+ }
+ fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
+ use c::SubBlockQuote::*;
+ match self {
+ Attribution(e) => vec![sub_res(*e, refs).into()],
+ BodyElement(e) => e.resolve_refs(refs).drain(..).map(Into::into).collect(),
+ }
+ }
}
impl ResolvableRefs for c::SubFootnote {
- fn populate_targets(&self, refs: &mut TargetsCollected) {
- use c::SubFootnote::*;
- match self {
- Label(e) => sub_pop(&**e, refs),
- BodyElement(e) => e.populate_targets(refs),
- }
- }
- fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
- use c::SubFootnote::*;
- match self {
- Label(e) => vec![sub_res(*e, refs).into()],
- BodyElement(e) => e.resolve_refs(refs).drain(..).map(Into::into).collect(),
- }
- }
+ fn populate_targets(&self, refs: &mut TargetsCollected) {
+ use c::SubFootnote::*;
+ match self {
+ Label(e) => sub_pop(&**e, refs),
+ BodyElement(e) => e.populate_targets(refs),
+ }
+ }
+ fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
+ use c::SubFootnote::*;
+ match self {
+ Label(e) => vec![sub_res(*e, refs).into()],
+ BodyElement(e) => e.resolve_refs(refs).drain(..).map(Into::into).collect(),
+ }
+ }
}
impl ResolvableRefs for c::SubFigure {
- fn populate_targets(&self, refs: &mut TargetsCollected) {
- use c::SubFigure::*;
- match self {
- Caption(e) => sub_pop(&**e, refs),
- Legend(e) => sub_pop(&**e, refs),
- BodyElement(e) => e.populate_targets(refs),
- }
- }
- fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
- use c::SubFigure::*;
- vec![match self {
- Caption(e) => sub_res(*e, refs).into(),
- Legend(e) => sub_res(*e, refs).into(),
- BodyElement(e) => return e.resolve_refs(refs).drain(..).map(Into::into).collect(),
- }]
- }
+ fn populate_targets(&self, refs: &mut TargetsCollected) {
+ use c::SubFigure::*;
+ match self {
+ Caption(e) => sub_pop(&**e, refs),
+ Legend(e) => sub_pop(&**e, refs),
+ BodyElement(e) => e.populate_targets(refs),
+ }
+ }
+ fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
+ use c::SubFigure::*;
+ vec![match self {
+ Caption(e) => sub_res(*e, refs).into(),
+ Legend(e) => sub_res(*e, refs).into(),
+ BodyElement(e) => return e.resolve_refs(refs).drain(..).map(Into::into).collect(),
+ }]
+ }
}
impl ResolvableRefs for c::SubTable {
- fn populate_targets(&self, refs: &mut TargetsCollected) {
- use c::SubTable::*;
- match self {
- Title(e) => sub_pop(&**e, refs),
- TableGroup(e) => sub_pop(&**e, refs),
- }
- }
- fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
- use c::SubTable::*;
- vec![match self {
- Title(e) => sub_res(*e, refs).into(),
- TableGroup(e) => sub_res(*e, refs).into(),
- }]
- }
+ fn populate_targets(&self, refs: &mut TargetsCollected) {
+ use c::SubTable::*;
+ match self {
+ Title(e) => sub_pop(&**e, refs),
+ TableGroup(e) => sub_pop(&**e, refs),
+ }
+ }
+ fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
+ use c::SubTable::*;
+ vec![match self {
+ Title(e) => sub_res(*e, refs).into(),
+ TableGroup(e) => sub_res(*e, refs).into(),
+ }]
+ }
}
impl ResolvableRefs for c::SubTableGroup {
- fn populate_targets(&self, refs: &mut TargetsCollected) {
- use c::SubTableGroup::*;
- match self {
- TableColspec(_) => {
- unimplemented!();
- },
- TableHead(e) => {
- for c in e.children() {
- sub_sub_pop(c, refs);
- }
- },
- TableBody(e) => {
- for c in e.children() {
- sub_sub_pop(c, refs);
- }
- },
- }
- }
- fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
- use c::SubTableGroup::*;
- vec![match self {
- TableColspec(e) => TableColspec(e),
- TableHead(mut e) => {
- let new: Vec<_> = e.children_mut().drain(..).map(|c| sub_sub_res(c, refs)).collect();
- e.children_mut().extend(new);
- TableHead(e)
- },
- TableBody(mut e) => {
- let new: Vec<_> = e.children_mut().drain(..).map(|c| sub_sub_res(c, refs)).collect();
- e.children_mut().extend(new);
- TableBody(e)
- },
- }]
- }
+ fn populate_targets(&self, refs: &mut TargetsCollected) {
+ use c::SubTableGroup::*;
+ match self {
+ TableColspec(_) => {
+ unimplemented!();
+ }
+ TableHead(e) => {
+ for c in e.children() {
+ sub_sub_pop(c, refs);
+ }
+ }
+ TableBody(e) => {
+ for c in e.children() {
+ sub_sub_pop(c, refs);
+ }
+ }
+ }
+ }
+ fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> {
+ use c::SubTableGroup::*;
+ vec![match self {
+ TableColspec(e) => TableColspec(e),
+ TableHead(mut e) => {
+ let new: Vec<_> = e
+ .children_mut()
+ .drain(..)
+ .map(|c| sub_sub_res(c, refs))
+ .collect();
+ e.children_mut().extend(new);
+ TableHead(e)
+ }
+ TableBody(mut e) => {
+ let new: Vec<_> = e
+ .children_mut()
+ .drain(..)
+ .map(|c| sub_sub_res(c, refs))
+ .collect();
+ e.children_mut().extend(new);
+ TableBody(e)
+ }
+ }]
+ }
}
diff --git a/parser/src/tests.rs b/parser/src/tests.rs
index ffe0ee7..b5dfe6a 100644
--- a/parser/src/tests.rs
+++ b/parser/src/tests.rs
@@ -5,103 +5,103 @@ 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)
- ])
- ]
- };
+ 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)])
- ]
- };
+ 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)]),
- ])
- ]
- };
+ 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)]),
+ ])
+ ]
+ };
}
#[test]
fn title() {
- parses_to! {
- parser: RstParser,
- input: "\
+ 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),
- ]) ])
- ]
- };
+ 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: "\
+ 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) ]),
- ]) ])
- ]
- };
+ 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: "\
+ 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),
- ]),
- ]
- };
+ 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]
@@ -119,9 +119,9 @@ fn inline_code_literal_with_underscore() {
#[allow(clippy::cognitive_complexity)]
#[test]
fn admonitions() {
- parses_to! {
- parser: RstParser,
- input: "\
+ parses_to! {
+ parser: RstParser,
+ input: "\
.. note::
Just next line
.. admonition:: In line title
@@ -130,30 +130,30 @@ fn admonitions() {
.. danger:: Just this line
",
- rule: Rule::document,
- tokens: [
- admonition_gen(0, 27, [
- admonition_type(3, 7),
- paragraph(13, 27, [ str(13, 27) ]),
- ]),
- admonition(28, 71, [
- 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) ]),
- ]),
- ]
- };
+ rule: Rule::document,
+ tokens: [
+ admonition_gen(0, 27, [
+ admonition_type(3, 7),
+ paragraph(13, 27, [ str(13, 27) ]),
+ ]),
+ admonition(28, 71, [
+ 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) ]),
+ ]),
+ ]
+ };
}
#[allow(clippy::cognitive_complexity)]
#[test]
fn literal_block() {
- parses_to! {
- parser: RstParser,
- input: "\
+ parses_to! {
+ parser: RstParser,
+ input: "\
::
print('x')
@@ -162,26 +162,26 @@ fn literal_block() {
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) ]),
- ]
- };
+ 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: "\
+ parses_to! {
+ parser: RstParser,
+ input: "\
.. code::
Single line
@@ -194,30 +194,30 @@ fn code_directive() {
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) ]),
- ]
- };
+ 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: "\
+ parses_to! {
+ parser: RstParser,
+ input: "\
.. raw:: html
hello <span>world</span>
@@ -230,33 +230,33 @@ fn raw() {
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) ]),
- ]
- };
+ 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: "\
+ parses_to! {
+ parser: RstParser,
+ input: "\
.. This is a comment
..
@@ -280,118 +280,117 @@ fn comments() {
.. 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),
- ]),
- ]
- };
+ 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: "\
+ 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),
- ]) ]),
- ]),
- ]
- };
+ 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),
- ]),
- ]
- };
+ 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: "\
+ 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) ]),
- ]),
- ]),
- ]),
- ]
- };
+ 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) ]),
+ ]),
+ ]),
+ ]),
+ ]
+ };
}
// TODO: test images
@@ -399,9 +398,9 @@ fn substitution_image() {
#[allow(clippy::cognitive_complexity)]
#[test]
fn nested_lists() {
- parses_to! {
- parser: RstParser,
- input: "\
+ parses_to! {
+ parser: RstParser,
+ input: "\
paragraph
- item 1
@@ -413,27 +412,27 @@ paragraph
- 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)]) ]),
- ]),
- ]),
- ]),
- ]
- }
+ 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)]) ]),
+ ]),
+ ]),
+ ]),
+ ]
+ }
}
diff --git a/parser/src/token.rs b/parser/src/token.rs
index b3b7bac..1b1d49d 100644
--- a/parser/src/token.rs
+++ b/parser/src/token.rs
@@ -1,16 +1,68 @@
//http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#bullet-lists
// *, +, -, •, ‣, ⁃
-pub enum BulletListType { Ast, Plus, Minus, Bullet, TriBullet, HyphenBullet }
+pub enum BulletListType {
+ Ast,
+ Plus,
+ Minus,
+ Bullet,
+ TriBullet,
+ HyphenBullet,
+}
// 1, A, a, I, i
-pub enum EnumListChar { Arabic, AlphaUpper, AlphaLower, RomanUpper, RomanLower, Auto }
+pub enum EnumListChar {
+ Arabic,
+ AlphaUpper,
+ AlphaLower,
+ RomanUpper,
+ RomanLower,
+ Auto,
+}
// 1., (1), 1)
-pub enum EnumListType { Period, ParenEnclosed, Paren }
+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,
+ 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) }
+pub enum FootnoteType {
+ Numbered(usize),
+ AutoNumber,
+ AutoSymbol,
+ AutoNamed(String),
+}
diff --git a/renderer/Cargo.toml b/renderer/Cargo.toml
index 051d175..20b92bc 100644
--- a/renderer/Cargo.toml
+++ b/renderer/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = 'rst_renderer'
-version = '0.3.1'
+version = '0.4.0'
authors = ['Philipp A. <flying-sheep@web.de>']
edition = '2018'
description = 'a reStructuredText renderer'
@@ -12,7 +12,7 @@ homepage = 'https://github.com/flying-sheep/rust-rst'
repository = 'https://github.com/flying-sheep/rust-rst'
[dependencies]
-document_tree = { path = '../document_tree', version = '0.3.0' }
+document_tree = { path = '../document_tree', version = '0.4.0' }
failure = '0.1.6'
serde_json = '1.0.44'
diff --git a/renderer/src/html.rs b/renderer/src/html.rs
index 4244349..feef29d 100644
--- a/renderer/src/html.rs
+++ b/renderer/src/html.rs
@@ -7,406 +7,601 @@ use failure::Error;
// use crate::url::Url;
use document_tree::{
- Document,Element,HasChildren,ExtraAttributes,
- elements as e,
- extra_attributes as a,
- element_categories as c,
- attribute_types as at,
+ attribute_types as at, element_categories as c, elements as e, extra_attributes as a, Document,
+ Element, ExtraAttributes, HasChildren,
};
-
// static FOOTNOTE_SYMBOLS: [char; 10] = ['*', '†', '‡', '§', '¶', '#', '♠', '♥', '♦', '♣'];
-pub fn render_html<W>(document: &Document, stream: W, standalone: bool) -> Result<(), Error> where W: Write {
- let mut renderer = HTMLRenderer { stream, level: 0 };
- if standalone {
- document.render_html(&mut renderer)
- } else {
- for c in document.children() {
- (*c).render_html(&mut renderer)?;
- writeln!(renderer.stream)?;
- }
- Ok(())
- }
+pub fn render_html<W>(document: &Document, stream: W, standalone: bool) -> Result<(), Error>
+where
+ W: Write,
+{
+ let mut renderer = HTMLRenderer { stream, level: 0 };
+ if standalone {
+ document.render_html(&mut renderer)
+ } else {
+ for c in document.children() {
+ (*c).render_html(&mut renderer)?;
+ writeln!(renderer.stream)?;
+ }
+ Ok(())
+ }
}
fn escape_html(text: &str) -> String {
- text.replace('&', "&amp;")
- .replace('<', "&lt;")
- .replace('>', "&gt;")
- .replace('"', "&quot;")
+ text.replace('&', "&amp;")
+ .replace('<', "&lt;")
+ .replace('>', "&gt;")
+ .replace('"', "&quot;")
}
-struct HTMLRenderer<W> where W: Write {
- stream: W,
- level: u8,
+struct HTMLRenderer<W>
+where
+ W: Write,
+{
+ stream: W,
+ level: u8,
}
trait HTMLRender {
- fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write;
+ fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
+ where
+ W: Write;
}
macro_rules! impl_html_render_cat {($cat:ident { $($member:ident),+ }) => {
- impl HTMLRender for c::$cat {
- fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write {
- match self {$(
- c::$cat::$member(elem) => (**elem).render_html(renderer),
- )+}
- }
- }
+ impl HTMLRender for c::$cat {
+ fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write {
+ match self {$(
+ c::$cat::$member(elem) => (**elem).render_html(renderer),
+ )+}
+ }
+ }
}}
macro_rules! impl_html_render_simple {
- (
- $type1:ident => $tag1:ident $( [$($post1:tt)+] )?,
- $( $type:ident => $tag:ident $( [$($post:tt)+] )? ),+
- ) => {
- impl_html_render_simple!($type1 => $tag1 $([$($post1)+])?);
- $( impl_html_render_simple!($type => $tag $([$($post)+])?); )+
- };
- ( $type:ident => $tag:ident ) => {
- impl_html_render_simple!($type => $tag[""]);
- };
- ( $type:ident => $tag:ident [ $post:expr ] ) => {
- impl HTMLRender for e::$type {
- fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write {
- let multiple_children = self.children().len() > 1;
- write!(renderer.stream, "<{}", stringify!($tag))?;
- if self.classes().len() > 0 {
- write!(renderer.stream, " class=\"{}\"", self.classes().join(" "))?;
- }
- write!(renderer.stream, ">")?;
- if multiple_children { write!(renderer.stream, $post)?; }
- for c in self.children() {
- (*c).render_html(renderer)?;
- if multiple_children { write!(renderer.stream, $post)?; }
- }
- write!(renderer.stream, "</{}>", stringify!($tag))?;
- Ok(())
- }
- }
- };
+ (
+ $type1:ident => $tag1:ident $( [$($post1:tt)+] )?,
+ $( $type:ident => $tag:ident $( [$($post:tt)+] )? ),+
+ ) => {
+ impl_html_render_simple!($type1 => $tag1 $([$($post1)+])?);
+ $( impl_html_render_simple!($type => $tag $([$($post)+])?); )+
+ };
+ ( $type:ident => $tag:ident ) => {
+ impl_html_render_simple!($type => $tag[""]);
+ };
+ ( $type:ident => $tag:ident [ $post:expr ] ) => {
+ impl HTMLRender for e::$type {
+ fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write {
+ let multiple_children = self.children().len() > 1;
+ write!(renderer.stream, "<{}", stringify!($tag))?;
+ if self.classes().len() > 0 {
+ write!(renderer.stream, " class=\"{}\"", self.classes().join(" "))?;
+ }
+ write!(renderer.stream, ">")?;
+ if multiple_children { write!(renderer.stream, $post)?; }
+ for c in self.children() {
+ (*c).render_html(renderer)?;
+ if multiple_children { write!(renderer.stream, $post)?; }
+ }
+ write!(renderer.stream, "</{}>", stringify!($tag))?;
+ Ok(())
+ }
+ }
+ };
}
macro_rules! impl_html_render_simple_nochildren {( $($type:ident => $tag:ident),+ ) => { $(
- impl HTMLRender for e::$type {
- fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write {
- write!(renderer.stream, "<{0}></{0}>", stringify!($tag))?;
- Ok(())
- }
- }
+ impl HTMLRender for e::$type {
+ fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write {
+ write!(renderer.stream, "<{0}></{0}>", stringify!($tag))?;
+ Ok(())
+ }
+ }
)+ }}
// Impl
impl HTMLRender for Document {
- fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write {
- writeln!(renderer.stream, "<!doctype html><html>")?;
- for c in self.children() {
- (*c).render_html(renderer)?;
- writeln!(renderer.stream)?;
- }
- writeln!(renderer.stream, "</html>")?;
- Ok(())
- }
-}
-
-impl_html_render_cat!(StructuralSubElement { Title, Subtitle, Decoration, Docinfo, SubStructure });
+ fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
+ where
+ W: Write,
+ {
+ writeln!(renderer.stream, "<!doctype html><html>")?;
+ for c in self.children() {
+ (*c).render_html(renderer)?;
+ writeln!(renderer.stream)?;
+ }
+ writeln!(renderer.stream, "</html>")?;
+ Ok(())
+ }
+}
+
+impl_html_render_cat!(StructuralSubElement {
+ Title,
+ Subtitle,
+ Decoration,
+ Docinfo,
+ SubStructure
+});
impl_html_render_simple!(Subtitle => h2);
impl HTMLRender for e::Title {
- fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write {
- let level = if renderer.level > 6 { 6 } else { renderer.level };
- write!(renderer.stream, "<h{0}>", level)?;
- for c in self.children() {
- (*c).render_html(renderer)?;
- }
- write!(renderer.stream, "</h{0}>", level)?;
- Ok(())
- }
+ fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
+ where
+ W: Write,
+ {
+ let level = if renderer.level > 6 {
+ 6
+ } else {
+ renderer.level
+ };
+ write!(renderer.stream, "<h{0}>", level)?;
+ for c in self.children() {
+ (*c).render_html(renderer)?;
+ }
+ write!(renderer.stream, "</h{0}>", level)?;
+ Ok(())
+ }
}
impl HTMLRender for e::Docinfo {
- fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write {
- // Like “YAML frontmatter” in Markdown
- unimplemented!();
- }
+ fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
+ where
+ W: Write,
+ {
+ // Like “YAML frontmatter” in Markdown
+ unimplemented!();
+ }
}
impl HTMLRender for e::Decoration {
- fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write {
- // Header or footer
- unimplemented!();
- }
-}
-
-impl_html_render_cat!(SubStructure { Topic, Sidebar, Transition, Section, BodyElement });
+ fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
+ where
+ W: Write,
+ {
+ // Header or footer
+ unimplemented!();
+ }
+}
+
+impl_html_render_cat!(SubStructure {
+ Topic,
+ Sidebar,
+ Transition,
+ Section,
+ BodyElement
+});
impl_html_render_simple!(Sidebar => aside);
impl HTMLRender for e::Section {
- fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write {
- renderer.level += 1;
- writeln!(renderer.stream, "<section id=\"{0}\">", self.ids()[0].0)?;
- for c in self.children() {
- (*c).render_html(renderer)?;
- writeln!(renderer.stream)?;
- }
- write!(renderer.stream, "</section>")?;
- renderer.level -= 1;
- Ok(())
- }
+ fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
+ where
+ W: Write,
+ {
+ renderer.level += 1;
+ writeln!(renderer.stream, "<section id=\"{0}\">", self.ids()[0].0)?;
+ for c in self.children() {
+ (*c).render_html(renderer)?;
+ writeln!(renderer.stream)?;
+ }
+ write!(renderer.stream, "</section>")?;
+ renderer.level -= 1;
+ Ok(())
+ }
}
impl HTMLRender for e::Transition {
- fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write {
- write!(renderer.stream, "<hr/>")?;
- Ok(())
- }
+ fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
+ where
+ W: Write,
+ {
+ write!(renderer.stream, "<hr/>")?;
+ Ok(())
+ }
}
impl HTMLRender for e::Topic {
- fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write {
- // A mini section with title
- unimplemented!();
- }
-}
-
-impl_html_render_cat!(BodyElement { Paragraph, LiteralBlock, DoctestBlock, MathBlock, Rubric, SubstitutionDefinition, Comment, Pending, Target, Raw, Image, Compound, Container, BulletList, EnumeratedList, DefinitionList, FieldList, OptionList, LineBlock, BlockQuote, Admonition, Attention, Hint, Note, Caution, Danger, Error, Important, Tip, Warning, Footnote, Citation, SystemMessage, Figure, Table });
+ fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
+ where
+ W: Write,
+ {
+ // A mini section with title
+ unimplemented!();
+ }
+}
+
+impl_html_render_cat!(BodyElement {
+ Paragraph,
+ LiteralBlock,
+ DoctestBlock,
+ MathBlock,
+ Rubric,
+ SubstitutionDefinition,
+ Comment,
+ Pending,
+ Target,
+ Raw,
+ Image,
+ Compound,
+ Container,
+ BulletList,
+ EnumeratedList,
+ DefinitionList,
+ FieldList,
+ OptionList,
+ LineBlock,
+ BlockQuote,
+ Admonition,
+ Attention,
+ Hint,
+ Note,
+ Caution,
+ Danger,
+ Error,
+ Important,
+ Tip,
+ Warning,
+ Footnote,
+ Citation,
+ SystemMessage,
+ Figure,
+ Table
+});
impl_html_render_simple!(Paragraph => p, MathBlock => math, Rubric => a, Compound => p, Container => div, BulletList => ul["\n"], EnumeratedList => ol["\n"], DefinitionList => dl["\n"], FieldList => dl["\n"], OptionList => pre, LineBlock => div["\n"], BlockQuote => blockquote, Admonition => aside, Attention => aside, Hint => aside, Note => aside, Caution => aside, Danger => aside, Error => aside, Important => aside, Tip => aside, Warning => aside, Figure => figure);
-impl_html_render_simple_nochildren!(Table => table); //TODO: after implementing the table, move it to elems with children
+impl_html_render_simple_nochildren!(Table => table); //TODO: after implementing the table, move it to elems with children
// circumvent E0119
-trait IMark {} impl IMark for e::Image {} impl IMark for e::ImageInline {}
-impl<I> HTMLRender for I where I: e::Element + a::ExtraAttributes<a::Image> + IMark {
- fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write {
- let extra = self.extra();
- if let Some(ref target) = extra.target {
- write!(renderer.stream, "<a href=\"{}\">", escape_html(target.as_str()))?;
- }
- write!(renderer.stream, "<img")?;
- if let Some(ref alt) = extra.alt {
- write!(renderer.stream, " alt=\"{}\"", escape_html(alt))?;
- }
- // TODO: align: Option<AlignHV>
- // TODO: height: Option<Measure>
- // TODO: width: Option<Measure>
- // TODO: scale: Option<u8>
- write!(renderer.stream, " src=\"{}\" />", escape_html(extra.uri.as_str()))?;
- if extra.target.is_some() {
- write!(renderer.stream, "</a>")?;
- }
- Ok(())
- }
+trait IMark {}
+impl IMark for e::Image {}
+impl IMark for e::ImageInline {}
+impl<I> HTMLRender for I
+where
+ I: e::Element + a::ExtraAttributes<a::Image> + IMark,
+{
+ fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
+ where
+ W: Write,
+ {
+ let extra = self.extra();
+ if let Some(ref target) = extra.target {
+ write!(
+ renderer.stream,
+ "<a href=\"{}\">",
+ escape_html(target.as_str())
+ )?;
+ }
+ write!(renderer.stream, "<img")?;
+ if let Some(ref alt) = extra.alt {
+ write!(renderer.stream, " alt=\"{}\"", escape_html(alt))?;
+ }
+ // TODO: align: Option<AlignHV>
+ // TODO: height: Option<Measure>
+ // TODO: width: Option<Measure>
+ // TODO: scale: Option<u8>
+ write!(
+ renderer.stream,
+ " src=\"{}\" />",
+ escape_html(extra.uri.as_str())
+ )?;
+ if extra.target.is_some() {
+ write!(renderer.stream, "</a>")?;
+ }
+ Ok(())
+ }
}
impl HTMLRender for e::LiteralBlock {
- fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write {
- let mut cls_iter = self.classes().iter();
- let is_code = cls_iter.next() == Some(&"code".to_owned());
- write!(renderer.stream, "<pre>")?;
- if is_code { // TODO: support those classes not being at the start
- if let Some(lang) = cls_iter.next() {
- write!(renderer.stream, "<code class=\"language-{}\">", lang)?;
- } else {
- write!(renderer.stream, "<code>")?;
- }
- }
- for c in self.children() {
- (*c).render_html(renderer)?;
- }
- if is_code { write!(renderer.stream, "</code>")?; }
- write!(renderer.stream, "</pre>")?;
- Ok(())
- }
+ fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
+ where
+ W: Write,
+ {
+ let mut cls_iter = self.classes().iter();
+ let is_code = cls_iter.next() == Some(&"code".to_owned());
+ write!(renderer.stream, "<pre>")?;
+ if is_code {
+ // TODO: support those classes not being at the start
+ if let Some(lang) = cls_iter.next() {
+ write!(renderer.stream, "<code class=\"language-{}\">", lang)?;
+ } else {
+ write!(renderer.stream, "<code>")?;
+ }
+ }
+ for c in self.children() {
+ (*c).render_html(renderer)?;
+ }
+ if is_code {
+ write!(renderer.stream, "</code>")?;
+ }
+ write!(renderer.stream, "</pre>")?;
+ Ok(())
+ }
}
impl HTMLRender for e::DoctestBlock {
- fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write {
- // TODO
- unimplemented!();
- }
+ fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
+ where
+ W: Write,
+ {
+ // TODO
+ unimplemented!();
+ }
}
impl HTMLRender for e::SubstitutionDefinition {
- fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write {
- // TODO: Should those be removed after resolving them
- Ok(())
- }
+ fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
+ where
+ W: Write,
+ {
+ // TODO: Should those be removed after resolving them
+ Ok(())
+ }
}
impl HTMLRender for e::Comment {
- fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write {
- write!(renderer.stream, "<!--")?;
- for c in self.children() {
- (*c).render_html(renderer)?;
- }
- write!(renderer.stream, "-->")?;
- Ok(())
- }
+ fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
+ where
+ W: Write,
+ {
+ write!(renderer.stream, "<!--")?;
+ for c in self.children() {
+ (*c).render_html(renderer)?;
+ }
+ write!(renderer.stream, "-->")?;
+ Ok(())
+ }
}
impl HTMLRender for e::Pending {
- fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write {
- // Will those be resolved by the time we get here?
- unimplemented!();
- }
+ fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
+ where
+ W: Write,
+ {
+ // Will those be resolved by the time we get here?
+ unimplemented!();
+ }
}
impl HTMLRender for e::Target {
- fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write {
- // Should be resolved by now
- Ok(())
- }
+ fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
+ where
+ W: Write,
+ {
+ // Should be resolved by now
+ Ok(())
+ }
}
impl HTMLRender for e::Raw {
- fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write {
- let extra = self.extra();
- if extra.format.contains(&at::NameToken("html".to_owned())) {
- for c in self.children() {
- write!(renderer.stream, "{}", c)?;
- }
- }
- Ok(())
- }
+ fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
+ where
+ W: Write,
+ {
+ let extra = self.extra();
+ if extra.format.contains(&at::NameToken("html".to_owned())) {
+ for c in self.children() {
+ write!(renderer.stream, "{}", c)?;
+ }
+ }
+ Ok(())
+ }
}
impl HTMLRender for e::Footnote {
- fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write {
- unimplemented!();
- }
+ fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
+ where
+ W: Write,
+ {
+ unimplemented!();
+ }
}
impl HTMLRender for e::Citation {
- fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write {
- unimplemented!();
- }
+ fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
+ where
+ W: Write,
+ {
+ unimplemented!();
+ }
}
impl HTMLRender for e::SystemMessage {
- fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write {
- write!(renderer.stream, "<figure><caption>System Message</caption>")?;
- for c in self.children() {
- (*c).render_html(renderer)?;
- }
- write!(renderer.stream, "</figure>")?;
- Ok(())
- }
-}
-
-impl_html_render_cat!(TextOrInlineElement { String, Emphasis, Strong, Literal, Reference, FootnoteReference, CitationReference, SubstitutionReference, TitleReference, Abbreviation, Acronym, Superscript, Subscript, Inline, Problematic, Generated, Math, TargetInline, RawInline, ImageInline });
+ fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
+ where
+ W: Write,
+ {
+ write!(renderer.stream, "<figure><caption>System Message</caption>")?;
+ for c in self.children() {
+ (*c).render_html(renderer)?;
+ }
+ write!(renderer.stream, "</figure>")?;
+ Ok(())
+ }
+}
+
+impl_html_render_cat!(TextOrInlineElement {
+ String,
+ Emphasis,
+ Strong,
+ Literal,
+ Reference,
+ FootnoteReference,
+ CitationReference,
+ SubstitutionReference,
+ TitleReference,
+ Abbreviation,
+ Acronym,
+ Superscript,
+ Subscript,
+ Inline,
+ Problematic,
+ Generated,
+ Math,
+ TargetInline,
+ RawInline,
+ ImageInline
+});
impl_html_render_simple!(Emphasis => em, Strong => strong, Literal => code, FootnoteReference => a, CitationReference => a, TitleReference => a, Abbreviation => abbr, Acronym => acronym, Superscript => sup, Subscript => sub, Inline => span, Math => math, TargetInline => a);
impl HTMLRender for String {
- fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write {
- write!(renderer.stream, "{}", escape_html(self))?;
- Ok(())
- }
+ fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
+ where
+ W: Write,
+ {
+ write!(renderer.stream, "{}", escape_html(self))?;
+ Ok(())
+ }
}
impl HTMLRender for e::Reference {
- fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write {
- let extra = self.extra();
- write!(renderer.stream, "<a")?;
- if let Some(ref target) = extra.refuri {
- write!(renderer.stream, " href=\"{}\"", escape_html(target.as_str()))?;
- }
- /*
- if let Some(ref name) = extra.name {
- write!(renderer.stream, " title=\"{}\"", escape_html(&name.0))?;
- }
- */
- write!(renderer.stream, ">")?;
- for c in self.children() {
- (*c).render_html(renderer)?;
- }
- write!(renderer.stream, "</a>")?;
- Ok(())
- }
+ fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
+ where
+ W: Write,
+ {
+ let extra = self.extra();
+ write!(renderer.stream, "<a")?;
+ if let Some(ref target) = extra.refuri {
+ write!(
+ renderer.stream,
+ " href=\"{}\"",
+ escape_html(target.as_str())
+ )?;
+ }
+ /*
+ if let Some(ref name) = extra.name {
+ write!(renderer.stream, " title=\"{}\"", escape_html(&name.0))?;
+ }
+ */
+ write!(renderer.stream, ">")?;
+ for c in self.children() {
+ (*c).render_html(renderer)?;
+ }
+ write!(renderer.stream, "</a>")?;
+ Ok(())
+ }
}
impl HTMLRender for e::SubstitutionReference {
- fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write {
- // Will those be resolved by the time we get here?
- unimplemented!();
- }
+ fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
+ where
+ W: Write,
+ {
+ // Will those be resolved by the time we get here?
+ unimplemented!();
+ }
}
impl HTMLRender for e::Problematic {
- fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write {
- // Broken inline markup leads to insertion of this in docutils
- unimplemented!();
- }
+ fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
+ where
+ W: Write,
+ {
+ // Broken inline markup leads to insertion of this in docutils
+ unimplemented!();
+ }
}
impl HTMLRender for e::Generated {
- fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write {
- // Section numbers and so on
- unimplemented!();
- }
+ fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
+ where
+ W: Write,
+ {
+ // Section numbers and so on
+ unimplemented!();
+ }
}
impl HTMLRender for e::RawInline {
- fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write {
- for c in self.children() {
- write!(renderer.stream, "{}", c)?;
- }
- Ok(())
- }
+ fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
+ where
+ W: Write,
+ {
+ for c in self.children() {
+ write!(renderer.stream, "{}", c)?;
+ }
+ Ok(())
+ }
}
-
//--------------\\
//Content Models\\
//--------------\\
impl_html_render_cat!(SubTopic { Title, BodyElement });
-impl_html_render_cat!(SubSidebar { Topic, Title, Subtitle, BodyElement });
+impl_html_render_cat!(SubSidebar {
+ Topic,
+ Title,
+ Subtitle,
+ BodyElement
+});
impl_html_render_simple!(ListItem => li);
impl HTMLRender for e::DefinitionListItem {
- fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write {
- // Term→dt, Definition→dd, Classifier→???
- unimplemented!();
- }
+ fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
+ where
+ W: Write,
+ {
+ // Term→dt, Definition→dd, Classifier→???
+ unimplemented!();
+ }
}
impl HTMLRender for e::Field {
- fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write {
- // FieldName→dt, FieldBody→dd
- unimplemented!();
- }
+ fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
+ where
+ W: Write,
+ {
+ // FieldName→dt, FieldBody→dd
+ unimplemented!();
+ }
}
impl HTMLRender for e::OptionListItem {
- fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write {
- // OptionGroup→dt(s), Description→dd
- unimplemented!();
- }
+ fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
+ where
+ W: Write,
+ {
+ // OptionGroup→dt(s), Description→dd
+ unimplemented!();
+ }
}
impl_html_render_cat!(SubLineBlock { LineBlock, Line });
impl HTMLRender for e::Line {
- fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write {
- for c in self.children() {
- (*c).render_html(renderer)?;
- }
- write!(renderer.stream, "<br>")?;
- Ok(())
- }
-}
-
-impl_html_render_cat!(SubBlockQuote { Attribution, BodyElement });
+ fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
+ where
+ W: Write,
+ {
+ for c in self.children() {
+ (*c).render_html(renderer)?;
+ }
+ write!(renderer.stream, "<br>")?;
+ Ok(())
+ }
+}
+
+impl_html_render_cat!(SubBlockQuote {
+ Attribution,
+ BodyElement
+});
impl_html_render_simple!(Attribution => cite); //TODO: correct?
-impl_html_render_cat!(SubFigure { Caption, Legend, BodyElement });
+impl_html_render_cat!(SubFigure {
+ Caption,
+ Legend,
+ BodyElement
+});
impl_html_render_simple!(Caption => caption);
impl HTMLRender for e::Legend {
- fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write {
- unimplemented!();
- }
+ fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error>
+ where
+ W: Write,
+ {
+ unimplemented!();
+ }
}
//------------\\
diff --git a/renderer/src/html/tests.rs b/renderer/src/html/tests.rs
index a4ec633..4734d65 100644
--- a/renderer/src/html/tests.rs
+++ b/renderer/src/html/tests.rs
@@ -5,43 +5,40 @@ use rst_parser::parse;
use crate::html::render_html;
fn check_renders_to(rst: &str, expected: &str) {
- println!("Rendering:\n{}\n---", rst);
- let doc = parse(rst).expect("Cannot parse");
- let mut result_data: Vec<u8> = vec![];
- render_html(&doc, &mut result_data, false).expect("Render error");
- let result = String::from_utf8(result_data).expect("Could not decode");
- assert_eq!(result.as_str().trim(), expected);
- println!("{}", expected);
+ println!("Rendering:\n{}\n---", rst);
+ let doc = parse(rst).expect("Cannot parse");
+ let mut result_data: Vec<u8> = vec![];
+ render_html(&doc, &mut result_data, false).expect("Render error");
+ let result = String::from_utf8(result_data).expect("Could not decode");
+ assert_eq!(result.as_str().trim(), expected);
+ println!("{}", expected);
}
#[test]
fn simple_string() {
- check_renders_to(
- "Simple String",
- "<p>Simple String</p>",
- );
+ check_renders_to("Simple String", "<p>Simple String</p>");
}
#[test]
fn simple_string_with_markup() {
- check_renders_to(
- "Simple String with *emph* and **strong**",
- "<p>Simple String with <em>emph</em> and <strong>strong</strong></p>",
- );
+ check_renders_to(
+ "Simple String with *emph* and **strong**",
+ "<p>Simple String with <em>emph</em> and <strong>strong</strong></p>",
+ );
}
#[test]
fn inline_literal() {
- check_renders_to(
- "Simple String with an even simpler ``inline literal``",
- "<p>Simple String with an even simpler <code>inline literal</code></p>",
- );
+ check_renders_to(
+ "Simple String with an even simpler ``inline literal``",
+ "<p>Simple String with an even simpler <code>inline literal</code></p>",
+ );
}
/*
#[test]
fn test_reference_anonymous() {
- check_renders_to("\
+ check_renders_to("\
A simple `anonymous reference`__
__ http://www.test.com/test_url
@@ -53,28 +50,31 @@ __ http://www.test.com/test_url
#[test]
fn two_paragraphs() {
- check_renders_to(
- "One paragraph.\n\nTwo paragraphs.",
- "<p>One paragraph.</p>\n<p>Two paragraphs.</p>",
- );
+ check_renders_to(
+ "One paragraph.\n\nTwo paragraphs.",
+ "<p>One paragraph.</p>\n<p>Two paragraphs.</p>",
+ );
}
#[test]
fn named_reference() {
- check_renders_to("\
+ check_renders_to(
+ "\
A simple `named reference`_ with stuff in between the
reference and the target.
.. _`named reference`: http://www.test.com/test_url
-", "\
+",
+ "\
<p>A simple <a href=\"http://www.test.com/test_url\">named reference</a> with stuff in between the \
reference and the target.</p>\
-");
+",
+ );
}
#[test]
fn standalone_hyperlinks() {
- check_renders_to("\
+ check_renders_to("\
Some http://url and a not_url_scheme:foo that is not supposed to be an url.
", "\
<p>Some <a href=\"http://url/\">http://url</a> and a not_url_scheme:foo that is not supposed to be an url.</p>\
@@ -83,16 +83,20 @@ Some http://url and a not_url_scheme:foo that is not supposed to be an url.
#[test]
fn substitution() {
- check_renders_to("\
+ check_renders_to(
+ "\
A |subst|.
.. |subst| replace:: text substitution
-", "<p>A text substitution.</p>");
+",
+ "<p>A text substitution.</p>",
+ );
}
#[test]
fn not_substitution_literal() {
- check_renders_to("\
+ check_renders_to(
+ "\
hello ``foo.map(|world| world + 42)``
.. |world| replace:: something different
@@ -104,15 +108,17 @@ hello ``foo.map(|world| world + 42)``
::
hay! |x|
-", "<p>hello <code>foo.map(|world| world + 42)</code></p>
+",
+ "<p>hello <code>foo.map(|world| world + 42)</code></p>
<pre><code>foo.map(|world| world + 42)\n</code></pre>
-<pre>hay! |x|\n</pre>");
+<pre>hay! |x|\n</pre>",
+ );
}
/*
#[test]
fn test_section_hierarchy() {
- check_renders_to("\
+ check_renders_to("\
+++++
Title
+++++
@@ -146,7 +152,7 @@ And even more stuff
#[test]
fn test_docinfo_title() {
- check_renders_to("\
+ check_renders_to("\
+++++
Title
+++++
@@ -169,7 +175,8 @@ Some stuff
#[test]
fn section_hierarchy() {
- check_renders_to("\
+ check_renders_to(
+ "\
+++++
Title
+++++
@@ -188,7 +195,8 @@ Another Section
...............
And even more stuff
-", "\
+",
+ "\
<section id=\"title\">
<h1>Title</h1>
<section id=\"not-a-subtitle\">
@@ -204,12 +212,14 @@ And even more stuff
</section>
</section>
</section>\
-");
+",
+ );
}
#[test]
fn many_sections() {
- check_renders_to("\
+ check_renders_to(
+ "\
+++++++++
heading 1
+++++++++
@@ -233,7 +243,8 @@ heading 3a
----------
Second detail
-", "\
+",
+ "\
<section id=\"heading-1\">
<h1>heading 1</h1>
<section id=\"heading-2\">
@@ -253,26 +264,30 @@ Second detail
</section>
</section>
</section>\
-");
+",
+ );
}
#[test]
fn bullet_list() {
- check_renders_to("\
+ check_renders_to(
+ "\
* bullet
* list
-", "\
+",
+ "\
<ul>
<li><p>bullet</p></li>
<li><p>list</p></li>
</ul>\
-");
+",
+ );
}
/*
#[test]
fn test_table() {
- check_renders_to("\
+ check_renders_to("\
.. table::
:align: right
@@ -302,25 +317,29 @@ fn test_table() {
#[test]
fn code() {
- check_renders_to("\
+ check_renders_to(
+ "\
.. code:: python
def foo():
print('Hi!')
-
+
# comment
-", "\
+",
+ "\
<pre><code class=\"language-python\">def foo():
print('Hi!')
# comment
</code></pre>\
-");
+",
+ );
}
#[test]
fn raw_html() {
- check_renders_to("\
+ check_renders_to(
+ "\
.. raw:: html
hello <span>world</span>
@@ -333,19 +352,22 @@ after
.. raw:: something_else
into a black hole this goes
-", "\
+",
+ "\
hello <span>world</span>
<p>paragraph
paragraph</p>
<p>after</p>\
-");
+",
+ );
}
#[test]
fn comments() {
- check_renders_to("\
+ check_renders_to(
+ "\
.. Run-in
comment
@@ -354,7 +376,8 @@ fn comments() {
block-like
with blank lines
-", "\
+",
+ "\
<!--Run-in
comment
-->
@@ -362,13 +385,14 @@ comment
with blank lines
-->\
-");
+",
+ );
}
/*
#[test]
fn test_field_list() {
- check_renders_to("\
+ check_renders_to("\
Not a docinfo.
:This: .. _target:
@@ -398,7 +422,7 @@ Not a docinfo.
/*
#[test]
fn test_field_list_long() {
- check_renders_to("\
+ check_renders_to("\
Not a docinfo.
:This is: a
diff --git a/renderer/src/lib.rs b/renderer/src/lib.rs
index 4d6bfdb..6e349be 100644
--- a/renderer/src/lib.rs
+++ b/renderer/src/lib.rs
@@ -1,21 +1,25 @@
mod html;
-
use std::io::Write;
use failure::Error;
use document_tree::Document;
-
-pub fn render_json<W>(document: &Document, stream: W) -> Result<(), Error> where W: Write {
- serde_json::to_writer(stream, &document)?;
- Ok(())
+pub fn render_json<W>(document: &Document, stream: W) -> Result<(), Error>
+where
+ W: Write,
+{
+ serde_json::to_writer(stream, &document)?;
+ Ok(())
}
-pub fn render_xml<W>(document: &Document, stream: W) -> Result<(), Error> where W: Write {
- serde_xml_rs::to_writer(stream, &document).map_err(failure::SyncFailure::new)?;
- Ok(())
+pub fn render_xml<W>(document: &Document, stream: W) -> Result<(), Error>
+where
+ W: Write,
+{
+ serde_xml_rs::to_writer(stream, &document).map_err(failure::SyncFailure::new)?;
+ Ok(())
}
pub use html::render_html;
diff --git a/rst/Cargo.toml b/rst/Cargo.toml
index a0a288e..310fab6 100644
--- a/rst/Cargo.toml
+++ b/rst/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = 'rst'
-version = '0.3.0'
+version = '0.4.0'
authors = ['Philipp A. <flying-sheep@web.de>']
edition = '2018'
description = 'a reStructuredText parser and renderer for the command line'
@@ -12,8 +12,8 @@ homepage = 'https://github.com/flying-sheep/rust-rst'
repository = 'https://github.com/flying-sheep/rust-rst'
[dependencies]
-rst_renderer = { path = '../renderer', version = '0.3.0' }
-rst_parser = { path = '../parser', version = '0.3.0' }
+rst_renderer = { path = '../renderer', version = '0.4.0' }
+rst_parser = { path = '../parser', version = '0.4.0' }
quicli = '0.4.0'
structopt = '0.2.15'
diff --git a/rst/src/main.rs b/rst/src/main.rs
index 1418a5e..6a7ed44 100644
--- a/rst/src/main.rs
+++ b/rst/src/main.rs
@@ -1,59 +1,59 @@
-use structopt::StructOpt;
use clap::arg_enum;
use quicli::{
- fs::read_file,
- prelude::{CliResult,Verbosity},
+ fs::read_file,
+ prelude::{CliResult, Verbosity},
};
+use structopt::StructOpt;
use rst_parser::parse;
-use rst_renderer::{
- render_json,
- render_xml,
- render_html,
-};
+use rst_renderer::{render_html, render_json, render_xml};
use std::io::{self, Read};
arg_enum! {
- #[derive(Debug)]
- #[allow(non_camel_case_types)]
- enum Format { json, xml, html }
+ #[derive(Debug)]
+ #[allow(non_camel_case_types)]
+ enum Format { json, xml, html }
}
#[derive(Debug, StructOpt)]
#[structopt(raw(setting = "structopt::clap::AppSettings::ColoredHelp"))]
struct Cli {
- #[structopt(
- long = "format", short = "f", default_value = "html", // xml is pretty defunct…
- raw(possible_values = "&Format::variants()", case_insensitive = "true"),
- )]
- format: Format,
- file: Option<String>,
- #[structopt(flatten)]
- verbosity: Verbosity,
+ #[structopt(
+ long = "format", short = "f", default_value = "html", // xml is pretty defunct…
+ raw(possible_values = "&Format::variants()", case_insensitive = "true"),
+ )]
+ format: Format,
+ file: Option<String>,
+ #[structopt(flatten)]
+ verbosity: Verbosity,
}
fn main() -> CliResult {
- let args = Cli::from_args();
- args.verbosity.setup_env_logger("rst")?;
-
- let content = if let Some(file) = args.file {
- read_file(file)?
- } else {
- let mut stdin = String::new();
- io::stdin().read_to_string(&mut stdin)?;
-
- stdin
- };
-
- // TODO: somehow make it work without replacing tabs
- let content = content.replace('\t', " ".repeat(8).as_ref());
- let document = parse(&content)?;
- let stdout = std::io::stdout();
- match args.format {
- Format::json => render_json(&document, stdout)?,
- Format::xml => render_xml (&document, stdout)?,
- Format::html => render_html(&document, stdout, true)?,
- }
- Ok(())
+ let args = Cli::from_args();
+ args.verbosity.setup_env_logger("rst")?;
+
+ let content = if let Some(file) = args.file {
+ read_file(file)?
+ } else {
+ let mut stdin = String::new();
+ io::stdin().read_to_string(&mut stdin)?;
+
+ stdin
+ };
+
+ // TODO: somehow make it work without replacing tabs
+ let mut content = read_file(args.file)?.replace('\t', " ".repeat(8).as_ref());
+ // Allows for less complex grammar
+ if !content.ends_with('\n') {
+ content.push('\n');
+ }
+ let document = parse(&content)?;
+ let stdout = std::io::stdout();
+ match args.format {
+ Format::json => render_json(&document, stdout)?,
+ Format::xml => render_xml (&document, stdout)?,
+ Format::html => render_html(&document, stdout, true)?,
+ }
+ Ok(())
}
diff --git a/rust-rst.svg b/rust-rst.svg
index 894715d..6ff77a3 100644
--- a/rust-rst.svg
+++ b/rust-rst.svg
@@ -1,57 +1,57 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" height="104.49973" width="195.40294" version="1.1">
- <style>
- /* colors */
- :root { --rust-rst-lite: #ccc; --rust-rst-dark: #333 }
- .rust-rst-logo { fill: var(--rust-rst-dark); stroke: none; fill-rule: evenodd }
- .rust-rst-logo .stroked { stroke: var(--rust-rst-dark) }
- /* with a dark system theme, use: */
- @media (prefers-color-scheme: dark) {
- .rust-rst-logo { fill: var(--rust-rst-lite) }
- .rust-rst-logo .stroked { stroke: var(--rust-rst-lite) }
- }
- /* If this SVG is embedded in a document, use the text color */
- body .rust-rst-logo { fill: currentColor }
- body .rust-rst-logo .stroked { stroke: currentColor }
+ <style>
+ /* colors */
+ :root { --rust-rst-lite: #ccc; --rust-rst-dark: #333 }
+ .rust-rst-logo { fill: var(--rust-rst-dark); stroke: none; fill-rule: evenodd }
+ .rust-rst-logo .stroked { stroke: var(--rust-rst-dark) }
+ /* with a dark system theme, use: */
+ @media (prefers-color-scheme: dark) {
+ .rust-rst-logo { fill: var(--rust-rst-lite) }
+ .rust-rst-logo .stroked { stroke: var(--rust-rst-lite) }
+ }
+ /* If this SVG is embedded in a document, use the text color */
+ body .rust-rst-logo { fill: currentColor }
+ body .rust-rst-logo .stroked { stroke: currentColor }
- /* parts that use stroke */
- .rust-rst-logo .stroked {
- stroke-width: 3;
- stroke-linecap: round;
- stroke-linejoin: round;
- }
+ /* parts that use stroke */
+ .rust-rst-logo .stroked {
+ stroke-width: 3;
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ }
- /* can be used to regenerate/change the logo */
- .rust-rst-logo text {
- display: none;
- font-size: 90.66667175px;
- line-height: 1.25;
- font-family: "OCR-A", OCRA;
- }
- </style>
- <g class="rust-rst-logo">
- <path
- d="M 52.25,4.9804688 C 26.19,4.9804688 4.9804688,26.2 4.9804688,52.25 4.9804688,78.3 26.2,99.519531 52.25,99.519531 78.3,99.519531 99.519531,78.3 99.519531,52.25 99.519531,26.2 78.3,4.9804688 52.25,4.9804688 Z m -0.07031,4.1992187 c 1.68076,0.043529 3.020091,1.4226295 3.019531,3.1093745 0,4.146665 -6.21875,4.146665 -6.21875,0 -7.34e-4,-1.753375 1.446577,-3.1601154 3.199219,-3.1093745 z m -7.029297,5.1210935 4.789062,5.019531 c 1.08,1.13 2.87,1.179844 4,0.08984 l 5.361328,-5.109375 C 70.351003,16.370525 79.931432,23.192597 85.5,32.958984 l -3.669922,8.28125 c -0.63,1.43 0.01945,3.11 1.439453,3.75 l 7.060547,3.128907 c 1.806609,8.8722 -3.653366,24.198569 -10.800779,30.611328 L 72.958984,77.320312 C 71.428984,76.990313 69.919844,77.97 69.589844,79.5 l -1.560547,7.279297 C 57.878136,91.377741 46.226622,91.32258 36.119141,86.628906 l -1.558594,-7.279297 c -0.33,-1.53 -1.831328,-2.509687 -3.361328,-2.179687 l -6.429688,1.380859 C 17.284085,70.513922 13.167601,57.997225 14.199219,47.789062 l 6.701172,-2.978515 c 1.43,-0.64 2.079453,-2.310235 1.439453,-3.740235 L 20.958984,37.958984 18.81585,33.087893 C 23.531492,24.265903 34.944722,15.885966 45.150391,14.300781 Z M 15.099609,37.320312 c 1.682359,0.04871 3.020237,1.426312 3.019532,3.109376 0,4.146664 -6.21875,4.146665 -6.21875,0 -7.34e-4,-1.753377 1.446577,-3.160117 3.199218,-3.109376 z M 89.25,37.458984 c 1.682359,0.04871 3.020236,1.428264 3.019531,3.111328 0,4.146665 -6.21875,4.146666 -6.21875,0 -7.34e-4,-1.753376 1.446578,-3.162069 3.199219,-3.111328 z M 29.150391,81.109375 c 1.682359,0.04871 3.020236,1.428264 3.019531,3.111328 0,4.146665 -6.220703,4.146665 -6.220703,0 -7.34e-4,-1.753376 1.448531,-3.162069 3.201172,-3.111328 z M 75.199219,81.25 c 1.682359,0.04871 3.022189,1.426311 3.021484,3.109375 0,4.146665 -6.220703,4.146665 -6.220703,0 -7.34e-4,-1.753376 1.446578,-3.160116 3.199219,-3.109375 z"
- />
- <path class="stroked"
- d="m 96.879865,52.249865 a 44.63,44.63 0 0 1 -44.63,44.63 44.63,44.63 0 0 1 -44.6299999,-44.63 44.63,44.63 0 0 1 44.6299999,-44.6299999 44.63,44.63 0 0 1 44.63,44.6299999 z m -0.84,-4.31 6.960005,4.31 -6.960005,4.31 5.980005,5.59 -7.660005,2.87 4.78,6.65 -8.09,1.32 3.4,7.46 -8.19,-0.29 1.88,7.98 -7.98,-1.88 0.29,8.19 -7.46,-3.4 -1.32,8.09 -6.65,-4.78 -2.87,7.660005 -5.59,-5.980005 -4.31,6.960005 -4.31,-6.960005 -5.59,5.980005 -2.87,-7.660005 -6.65,4.78 -1.32,-8.09 -7.46,3.4 0.29,-8.19 -7.98,1.88 1.88,-7.98 -8.19,0.29 3.4,-7.46 -8.0899999,-1.32 4.7799999,-6.65 -7.6599999,-2.87 5.98,-5.59 -6.96,-4.31 6.96,-4.31 -5.98,-5.59 7.6599999,-2.87 -4.7799999,-6.65 8.0899999,-1.32 -3.4,-7.46 8.19,0.29 -1.88,-7.98 7.98,1.88 -0.29,-8.19 7.46,3.4 1.32,-8.0899999 6.65,4.7799999 2.87,-7.6599999 5.59,5.98 4.31,-6.96 4.31,6.96 5.59,-5.98 2.87,7.6599999 6.65,-4.7799999 1.32,8.0899999 7.46,-3.4 -0.29,8.19 7.98,-1.88 -1.88,7.98 8.19,-0.29 -3.4,7.46 8.09,1.32 -4.78,6.65 7.660005,2.87 z"
- />
- <text x="21.311935" y="82.67424">
- <tspan dx="0 7.5999999 -15.63">RST</tspan>
- </text>
- <g>
- <path
- style="font-size:90.66667175px;font-family:OCRA;word-spacing:0px"
- d="M 31.013269,47.132905 V 15.580903 H 46.51727 q 8.160001,0 11.877334,0.09067 3.717334,0 4.170667,0.09067 0.453333,0 1.36,0.272 3.898667,0.906667 6.618667,4.080001 2.72,3.082666 3.173334,7.162667 0.09067,0.544 0.09067,3.989333 -0.09067,3.626667 -0.453333,5.077334 -0.816,3.445333 -3.445334,5.984 -2.538666,2.538667 -5.984,3.445334 -1.36,0.362666 -5.077333,0.453333 h -3.173334 l 8.885334,15.141334 q 8.885334,15.232001 8.885334,15.413334 1.088,2.901334 -1.088,4.714667 -1.269334,1.269334 -3.082667,1.178667 -1.813334,-0.09067 -3.082667,-1.450667 -0.272,-0.272 -0.634667,-0.997333 L 55.402604,62.908906 45.610603,46.226238 h -2.992 -2.992 v 16.410668 q 0,16.501334 -0.09067,16.773334 -0.362666,0.997333 -0.816,1.632 -0.634666,0.816 -1.450666,1.178667 -1.813334,0.816 -3.445334,0.181333 -0.816,-0.272 -1.541333,-0.997333 -0.816,-0.906667 -0.997334,-1.541334 0,-0.09067 -0.09067,-0.09067 -0.181334,-0.544 -0.181334,-16.048001 0,-9.701334 0,-16.592001 z M 61.839938,24.37557 q -0.272,-0.09067 -11.242668,-0.09067 H 39.626603 v 6.618667 6.618667 H 50.59727 q 5.712001,0 8.341334,0 2.629334,-0.09067 2.901334,-0.09067 0.272,-0.09067 0.816,-0.362667 1.450666,-0.725333 1.994667,-2.085333 0.453333,-0.816 0.453333,-4.08 0,-3.264001 -0.453333,-4.170667 -0.816001,-1.632 -2.810667,-2.357334 z"
- />
- <path
- style="font-size:90.66667175px;font-family:OCRA;word-spacing:0px"
- d="m 111.85777,15.67157 q 0.0907,0 0.816,0 0.72533,0 1.81333,0 1.17867,-0.09067 3.62667,-0.09067 2.53867,0 5.712,0 11.51467,0 12.05867,0.09067 4.08,0.544 6.98133,3.445333 2.81067,2.810667 3.264,6.981334 0.36267,2.72 -1.26933,4.261334 -1.26934,1.178666 -2.992,1.178666 -0.72534,0 -1.17867,-0.09067 -1.088,-0.362667 -1.904,-1.178667 -1.17867,-1.178667 -1.26933,-2.992 0,-0.544 -0.18134,-1.088 -0.45333,-1.269334 -1.81333,-1.722667 l -0.36267,-0.181333 h -11.152 -11.24267 l -0.18133,0.181333 q -0.72533,0.634667 -0.45333,1.36 0.0907,0.09067 16.13867,20.762668 16.13866,20.581334 16.32,20.944001 1.26933,1.994667 1.54133,4.170667 0.45333,3.626667 -1.632,6.709334 -2.08533,2.992 -5.62133,3.898666 -1.36,0.362667 -13.23734,0.362667 -11.424,0 -11.968,-0.09067 -4.08,-0.544 -6.98133,-3.445333 -2.81067,-2.992 -3.264,-6.981334 -0.272,-2.538667 1.17866,-4.08 0.816,-0.906667 1.99467,-1.178667 0.36267,-0.181333 1.088,-0.181333 1.81333,0 2.992,1.178667 1.26933,1.088 1.36,3.082666 l 0.18133,1.088001 q 0.544,1.269333 1.81334,1.722666 l 0.36267,0.181334 h 11.152 11.06133 l 0.272,-0.09067 q 1.088,-0.544 0.544,-1.36 0,-0.272 -16.13867,-20.853335 -16.048,-20.672001 -16.32,-20.944001 -1.54133,-2.448 -1.54133,-5.349333 0,-2.992001 1.632,-5.349334 2.448,-3.808 6.8,-4.352 z"
- />
- <path
- style="font-size:90.66667175px;font-family:OCRA;word-spacing:0px"
- d="m 152.60827,21.564903 v -5.984 h 21.39734 21.39733 v 5.984 q 0,6.437334 -0.36267,7.253334 -0.544,1.722667 -2.35733,2.448 -0.63467,0.272 -1.632,0.272 -1.17867,0 -1.904,-0.362666 -1.45067,-0.816 -2.176,-2.357334 -0.272,-1.088 -0.272,-2.901333 v -1.632 h -4.17067 -4.26133 v 27.381334 q 0,27.472002 -0.0907,27.744002 -0.544,1.994667 -2.26666,2.810667 -1.81334,0.816 -3.44534,0.181333 -0.72533,-0.181333 -1.54133,-0.997333 -0.816,-0.906667 -0.99734,-1.541334 0,-0.09067 -0.0907,-0.09067 -0.18134,-0.544 -0.18134,-13.962668 0,-8.341334 0,-14.325334 V 24.284904 h -4.26133 -4.17067 v 1.632 q 0,2.176 -0.272,2.901333 -0.72533,1.722667 -2.448,2.448 -0.544,0.272 -1.632,0.272 -1.088,0 -1.81333,-0.362666 -1.632,-0.906667 -2.176,-2.357334 -0.272,-0.816 -0.272,-7.253334 z"
- />
- </g>
- </g>
+ /* can be used to regenerate/change the logo */
+ .rust-rst-logo text {
+ display: none;
+ font-size: 90.66667175px;
+ line-height: 1.25;
+ font-family: "OCR-A", OCRA;
+ }
+ </style>
+ <g class="rust-rst-logo">
+ <path
+ d="M 52.25,4.9804688 C 26.19,4.9804688 4.9804688,26.2 4.9804688,52.25 4.9804688,78.3 26.2,99.519531 52.25,99.519531 78.3,99.519531 99.519531,78.3 99.519531,52.25 99.519531,26.2 78.3,4.9804688 52.25,4.9804688 Z m -0.07031,4.1992187 c 1.68076,0.043529 3.020091,1.4226295 3.019531,3.1093745 0,4.146665 -6.21875,4.146665 -6.21875,0 -7.34e-4,-1.753375 1.446577,-3.1601154 3.199219,-3.1093745 z m -7.029297,5.1210935 4.789062,5.019531 c 1.08,1.13 2.87,1.179844 4,0.08984 l 5.361328,-5.109375 C 70.351003,16.370525 79.931432,23.192597 85.5,32.958984 l -3.669922,8.28125 c -0.63,1.43 0.01945,3.11 1.439453,3.75 l 7.060547,3.128907 c 1.806609,8.8722 -3.653366,24.198569 -10.800779,30.611328 L 72.958984,77.320312 C 71.428984,76.990313 69.919844,77.97 69.589844,79.5 l -1.560547,7.279297 C 57.878136,91.377741 46.226622,91.32258 36.119141,86.628906 l -1.558594,-7.279297 c -0.33,-1.53 -1.831328,-2.509687 -3.361328,-2.179687 l -6.429688,1.380859 C 17.284085,70.513922 13.167601,57.997225 14.199219,47.789062 l 6.701172,-2.978515 c 1.43,-0.64 2.079453,-2.310235 1.439453,-3.740235 L 20.958984,37.958984 18.81585,33.087893 C 23.531492,24.265903 34.944722,15.885966 45.150391,14.300781 Z M 15.099609,37.320312 c 1.682359,0.04871 3.020237,1.426312 3.019532,3.109376 0,4.146664 -6.21875,4.146665 -6.21875,0 -7.34e-4,-1.753377 1.446577,-3.160117 3.199218,-3.109376 z M 89.25,37.458984 c 1.682359,0.04871 3.020236,1.428264 3.019531,3.111328 0,4.146665 -6.21875,4.146666 -6.21875,0 -7.34e-4,-1.753376 1.446578,-3.162069 3.199219,-3.111328 z M 29.150391,81.109375 c 1.682359,0.04871 3.020236,1.428264 3.019531,3.111328 0,4.146665 -6.220703,4.146665 -6.220703,0 -7.34e-4,-1.753376 1.448531,-3.162069 3.201172,-3.111328 z M 75.199219,81.25 c 1.682359,0.04871 3.022189,1.426311 3.021484,3.109375 0,4.146665 -6.220703,4.146665 -6.220703,0 -7.34e-4,-1.753376 1.446578,-3.160116 3.199219,-3.109375 z"
+ />
+ <path class="stroked"
+ d="m 96.879865,52.249865 a 44.63,44.63 0 0 1 -44.63,44.63 44.63,44.63 0 0 1 -44.6299999,-44.63 44.63,44.63 0 0 1 44.6299999,-44.6299999 44.63,44.63 0 0 1 44.63,44.6299999 z m -0.84,-4.31 6.960005,4.31 -6.960005,4.31 5.980005,5.59 -7.660005,2.87 4.78,6.65 -8.09,1.32 3.4,7.46 -8.19,-0.29 1.88,7.98 -7.98,-1.88 0.29,8.19 -7.46,-3.4 -1.32,8.09 -6.65,-4.78 -2.87,7.660005 -5.59,-5.980005 -4.31,6.960005 -4.31,-6.960005 -5.59,5.980005 -2.87,-7.660005 -6.65,4.78 -1.32,-8.09 -7.46,3.4 0.29,-8.19 -7.98,1.88 1.88,-7.98 -8.19,0.29 3.4,-7.46 -8.0899999,-1.32 4.7799999,-6.65 -7.6599999,-2.87 5.98,-5.59 -6.96,-4.31 6.96,-4.31 -5.98,-5.59 7.6599999,-2.87 -4.7799999,-6.65 8.0899999,-1.32 -3.4,-7.46 8.19,0.29 -1.88,-7.98 7.98,1.88 -0.29,-8.19 7.46,3.4 1.32,-8.0899999 6.65,4.7799999 2.87,-7.6599999 5.59,5.98 4.31,-6.96 4.31,6.96 5.59,-5.98 2.87,7.6599999 6.65,-4.7799999 1.32,8.0899999 7.46,-3.4 -0.29,8.19 7.98,-1.88 -1.88,7.98 8.19,-0.29 -3.4,7.46 8.09,1.32 -4.78,6.65 7.660005,2.87 z"
+ />
+ <text x="21.311935" y="82.67424">
+ <tspan dx="0 7.5999999 -15.63">RST</tspan>
+ </text>
+ <g>
+ <path
+ style="font-size:90.66667175px;font-family:OCRA;word-spacing:0px"
+ d="M 31.013269,47.132905 V 15.580903 H 46.51727 q 8.160001,0 11.877334,0.09067 3.717334,0 4.170667,0.09067 0.453333,0 1.36,0.272 3.898667,0.906667 6.618667,4.080001 2.72,3.082666 3.173334,7.162667 0.09067,0.544 0.09067,3.989333 -0.09067,3.626667 -0.453333,5.077334 -0.816,3.445333 -3.445334,5.984 -2.538666,2.538667 -5.984,3.445334 -1.36,0.362666 -5.077333,0.453333 h -3.173334 l 8.885334,15.141334 q 8.885334,15.232001 8.885334,15.413334 1.088,2.901334 -1.088,4.714667 -1.269334,1.269334 -3.082667,1.178667 -1.813334,-0.09067 -3.082667,-1.450667 -0.272,-0.272 -0.634667,-0.997333 L 55.402604,62.908906 45.610603,46.226238 h -2.992 -2.992 v 16.410668 q 0,16.501334 -0.09067,16.773334 -0.362666,0.997333 -0.816,1.632 -0.634666,0.816 -1.450666,1.178667 -1.813334,0.816 -3.445334,0.181333 -0.816,-0.272 -1.541333,-0.997333 -0.816,-0.906667 -0.997334,-1.541334 0,-0.09067 -0.09067,-0.09067 -0.181334,-0.544 -0.181334,-16.048001 0,-9.701334 0,-16.592001 z M 61.839938,24.37557 q -0.272,-0.09067 -11.242668,-0.09067 H 39.626603 v 6.618667 6.618667 H 50.59727 q 5.712001,0 8.341334,0 2.629334,-0.09067 2.901334,-0.09067 0.272,-0.09067 0.816,-0.362667 1.450666,-0.725333 1.994667,-2.085333 0.453333,-0.816 0.453333,-4.08 0,-3.264001 -0.453333,-4.170667 -0.816001,-1.632 -2.810667,-2.357334 z"
+ />
+ <path
+ style="font-size:90.66667175px;font-family:OCRA;word-spacing:0px"
+ d="m 111.85777,15.67157 q 0.0907,0 0.816,0 0.72533,0 1.81333,0 1.17867,-0.09067 3.62667,-0.09067 2.53867,0 5.712,0 11.51467,0 12.05867,0.09067 4.08,0.544 6.98133,3.445333 2.81067,2.810667 3.264,6.981334 0.36267,2.72 -1.26933,4.261334 -1.26934,1.178666 -2.992,1.178666 -0.72534,0 -1.17867,-0.09067 -1.088,-0.362667 -1.904,-1.178667 -1.17867,-1.178667 -1.26933,-2.992 0,-0.544 -0.18134,-1.088 -0.45333,-1.269334 -1.81333,-1.722667 l -0.36267,-0.181333 h -11.152 -11.24267 l -0.18133,0.181333 q -0.72533,0.634667 -0.45333,1.36 0.0907,0.09067 16.13867,20.762668 16.13866,20.581334 16.32,20.944001 1.26933,1.994667 1.54133,4.170667 0.45333,3.626667 -1.632,6.709334 -2.08533,2.992 -5.62133,3.898666 -1.36,0.362667 -13.23734,0.362667 -11.424,0 -11.968,-0.09067 -4.08,-0.544 -6.98133,-3.445333 -2.81067,-2.992 -3.264,-6.981334 -0.272,-2.538667 1.17866,-4.08 0.816,-0.906667 1.99467,-1.178667 0.36267,-0.181333 1.088,-0.181333 1.81333,0 2.992,1.178667 1.26933,1.088 1.36,3.082666 l 0.18133,1.088001 q 0.544,1.269333 1.81334,1.722666 l 0.36267,0.181334 h 11.152 11.06133 l 0.272,-0.09067 q 1.088,-0.544 0.544,-1.36 0,-0.272 -16.13867,-20.853335 -16.048,-20.672001 -16.32,-20.944001 -1.54133,-2.448 -1.54133,-5.349333 0,-2.992001 1.632,-5.349334 2.448,-3.808 6.8,-4.352 z"
+ />
+ <path
+ style="font-size:90.66667175px;font-family:OCRA;word-spacing:0px"
+ d="m 152.60827,21.564903 v -5.984 h 21.39734 21.39733 v 5.984 q 0,6.437334 -0.36267,7.253334 -0.544,1.722667 -2.35733,2.448 -0.63467,0.272 -1.632,0.272 -1.17867,0 -1.904,-0.362666 -1.45067,-0.816 -2.176,-2.357334 -0.272,-1.088 -0.272,-2.901333 v -1.632 h -4.17067 -4.26133 v 27.381334 q 0,27.472002 -0.0907,27.744002 -0.544,1.994667 -2.26666,2.810667 -1.81334,0.816 -3.44534,0.181333 -0.72533,-0.181333 -1.54133,-0.997333 -0.816,-0.906667 -0.99734,-1.541334 0,-0.09067 -0.0907,-0.09067 -0.18134,-0.544 -0.18134,-13.962668 0,-8.341334 0,-14.325334 V 24.284904 h -4.26133 -4.17067 v 1.632 q 0,2.176 -0.272,2.901333 -0.72533,1.722667 -2.448,2.448 -0.544,0.272 -1.632,0.272 -1.088,0 -1.81333,-0.362666 -1.632,-0.906667 -2.176,-2.357334 -0.272,-0.816 -0.272,-7.253334 z"
+ />
+ </g>
+ </g>
</svg>