aboutsummaryrefslogtreecommitdiffstats
path: root/parser
diff options
context:
space:
mode:
Diffstat (limited to 'parser')
-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
11 files changed, 1469 insertions, 1288 deletions
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),
+}