diff options
28 files changed, 3005 insertions, 2425 deletions
| diff --git a/.editorconfig b/.editorconfig index c730fb1..73554fa 100644 --- a/.editorconfig +++ b/.editorconfig @@ -2,12 +2,12 @@  root = true  [*] -indent_style = tab +indent_style = space  indent_size = 4  end_of_line = lf  charset = utf-8 -trim_trailing_whitespace = false +trim_trailing_whitespace = true  insert_final_newline = true -[*.pest] -indent_style = space +[*.{yml,yaml}] +indent_size = 2 diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index eccef38..6e35283 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -27,7 +27,7 @@ jobs:      - uses: Swatinem/rust-cache@v2      - run: just build      - run: just check -    #- run: just fmt --check +    - run: just fmt --check      - run: just test    doc:      name: Build and publish docs diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f32687a..1c4f592 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,3 +1,8 @@ +ci: +  skip: +    - cargo-fmt +    - cargo-clippy +  repos:    - repo: https://github.com/pre-commit/pre-commit-hooks      rev: v4.5.0 @@ -8,3 +13,8 @@ repos:        - id: check-added-large-files        - id: detect-private-key        - id: check-merge-conflict +  - repo: https://github.com/kykosic/pre-commit-rust +    rev: '0.4.0' +    hooks: +      - id: cargo-fmt +      - id: cargo-clippy @@ -1,7 +1,7 @@  [workspace]  members = [ -	'document_tree', -	'parser', -	'renderer', -	'rst', +    'document_tree', +    'parser', +    'renderer', +    'rst',  ] @@ -1,9 +1,6 @@ -========================= -RuSTructuredText |travis| -========================= - -.. |travis| image:: https://travis-ci.com/flying-sheep/rust-rst.svg?branch=master -   :target: https://travis-ci.com/flying-sheep/rust-rst +================ +RuSTructuredText +================  .. image:: rust-rst.svg diff --git a/document_tree/src/attribute_types.rs b/document_tree/src/attribute_types.rs index ef00544..5303c5b 100644 --- a/document_tree/src/attribute_types.rs +++ b/document_tree/src/attribute_types.rs @@ -1,153 +1,201 @@  use std::str::FromStr; -use failure::{Error,bail,format_err}; -use serde_derive::Serialize; +use failure::{bail, format_err, Error};  use regex::Regex; +use serde_derive::Serialize;  use crate::url::Url; -#[derive(Debug,PartialEq,Eq,Hash,Serialize,Clone)] +#[derive(Debug, PartialEq, Eq, Hash, Serialize, Clone)]  pub enum EnumeratedListType { -	Arabic, -	LowerAlpha, -	UpperAlpha, -	LowerRoman, -	UpperRoman, +    Arabic, +    LowerAlpha, +    UpperAlpha, +    LowerRoman, +    UpperRoman,  } -#[derive(Default,Debug,PartialEq,Eq,Hash,Serialize,Clone)] +#[derive(Default, Debug, PartialEq, Eq, Hash, Serialize, Clone)]  pub enum FixedSpace { -	Default, -	// yes, default really is not “Default” -	#[default] -	Preserve, +    Default, +    // yes, default really is not “Default” +    #[default] +    Preserve,  } -#[derive(Debug,PartialEq,Eq,Hash,Serialize,Clone)] pub enum AlignH { Left, Center, Right} -#[derive(Debug,PartialEq,Eq,Hash,Serialize,Clone)] pub enum AlignHV { Top, Middle, Bottom, Left, Center, Right } -#[derive(Debug,PartialEq,Eq,Hash,Serialize,Clone)] pub enum AlignV { Top, Middle, Bottom } +#[derive(Debug, PartialEq, Eq, Hash, Serialize, Clone)] +pub enum AlignH { +    Left, +    Center, +    Right, +} +#[derive(Debug, PartialEq, Eq, Hash, Serialize, Clone)] +pub enum AlignHV { +    Top, +    Middle, +    Bottom, +    Left, +    Center, +    Right, +} +#[derive(Debug, PartialEq, Eq, Hash, Serialize, Clone)] +pub enum AlignV { +    Top, +    Middle, +    Bottom, +} -#[derive(Debug,PartialEq,Eq,Hash,Serialize,Clone)] pub enum TableAlignH { Left, Right, Center, Justify, Char } -#[derive(Debug,PartialEq,Eq,Hash,Serialize,Clone)] pub enum TableBorder { Top, Bottom, TopBottom, All, Sides, None } +#[derive(Debug, PartialEq, Eq, Hash, Serialize, Clone)] +pub enum TableAlignH { +    Left, +    Right, +    Center, +    Justify, +    Char, +} +#[derive(Debug, PartialEq, Eq, Hash, Serialize, Clone)] +pub enum TableBorder { +    Top, +    Bottom, +    TopBottom, +    All, +    Sides, +    None, +} -#[derive(Debug,PartialEq,Eq,Hash,Serialize,Clone)] pub struct ID(pub String); -#[derive(Debug,PartialEq,Eq,Hash,Serialize,Clone)] pub struct NameToken(pub String); +#[derive(Debug, PartialEq, Eq, Hash, Serialize, Clone)] +pub struct ID(pub String); +#[derive(Debug, PartialEq, Eq, Hash, Serialize, Clone)] +pub struct NameToken(pub String);  // The table DTD has the cols attribute of tgroup as required, but having  // TableGroupCols not implement Default would leave no possible implementation  // for TableGroup::with_children. -#[derive(Default,Debug,PartialEq,Eq,Hash,Serialize,Clone)] pub struct TableGroupCols(pub usize); +#[derive(Default, Debug, PartialEq, Eq, Hash, Serialize, Clone)] +pub struct TableGroupCols(pub usize);  // no eq for f64 -#[derive(Debug,PartialEq,Serialize,Clone)] -pub enum Measure {  // http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#length-units -	Em(f64), -	Ex(f64), -	Mm(f64), -	Cm(f64), -	In(f64), -	Px(f64), -	Pt(f64), -	Pc(f64), +#[derive(Debug, PartialEq, Serialize, Clone)] +pub enum Measure { +    // http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#length-units +    Em(f64), +    Ex(f64), +    Mm(f64), +    Cm(f64), +    In(f64), +    Px(f64), +    Pt(f64), +    Pc(f64),  }  impl FromStr for AlignHV { -	type Err = Error; -	fn from_str(s: &str) -> Result<Self, Self::Err> { -		use self::AlignHV::*; -		Ok(match s { -			"top"    => Top, -			"middle" => Middle, -			"bottom" => Bottom, -			"left"   => Left, -			"center" => Center, -			"right"  => Right, -			s => bail!("Invalid Alignment {}", s), -		}) -	} +    type Err = Error; +    fn from_str(s: &str) -> Result<Self, Self::Err> { +        use self::AlignHV::*; +        Ok(match s { +            "top" => Top, +            "middle" => Middle, +            "bottom" => Bottom, +            "left" => Left, +            "center" => Center, +            "right" => Right, +            s => bail!("Invalid Alignment {}", s), +        }) +    }  }  impl From<&str> for ID { -	fn from(s: &str) -> Self { -		ID(s.to_owned().replace(' ', "-")) -	} +    fn from(s: &str) -> Self { +        ID(s.to_owned().replace(' ', "-")) +    }  }  impl From<&str> for NameToken { -	fn from(s: &str) -> Self { -		NameToken(s.to_owned()) -	} +    fn from(s: &str) -> Self { +        NameToken(s.to_owned()) +    }  }  impl FromStr for Measure { -	type Err = Error; -	fn from_str(s: &str) -> Result<Self, Self::Err> { -		use self::Measure::*; -		let re = Regex::new(r"(?P<float>\d+\.\d*|\.?\d+)\s*(?P<unit>em|ex|mm|cm|in|px|pt|pc)").unwrap(); -		let caps: regex::Captures = re.captures(s).ok_or_else(|| format_err!("Invalid measure"))?; -		let value: f64 = caps["float"].parse()?; -		Ok(match &caps["unit"] { -			"em" => Em(value), -			"ex" => Ex(value), -			"mm" => Mm(value), -			"cm" => Cm(value), -			"in" => In(value), -			"px" => Px(value), -			"pt" => Pt(value), -			"pc" => Pc(value), -			_ => unreachable!(), -		}) -	} +    type Err = Error; +    fn from_str(s: &str) -> Result<Self, Self::Err> { +        use self::Measure::*; +        let re = +            Regex::new(r"(?P<float>\d+\.\d*|\.?\d+)\s*(?P<unit>em|ex|mm|cm|in|px|pt|pc)").unwrap(); +        let caps: regex::Captures = re +            .captures(s) +            .ok_or_else(|| format_err!("Invalid measure"))?; +        let value: f64 = caps["float"].parse()?; +        Ok(match &caps["unit"] { +            "em" => Em(value), +            "ex" => Ex(value), +            "mm" => Mm(value), +            "cm" => Cm(value), +            "in" => In(value), +            "px" => Px(value), +            "pt" => Pt(value), +            "pc" => Pc(value), +            _ => unreachable!(), +        }) +    }  }  #[cfg(test)]  mod parse_tests { -	use super::*; - -	#[test] -	fn measure() { -		let _a: Measure = "1.5em".parse().unwrap(); -		let _b: Measure = "20 mm".parse().unwrap(); -		let _c: Measure = ".5in".parse().unwrap(); -		let _d: Measure = "1.pc".parse().unwrap(); -	} +    use super::*; + +    #[test] +    fn measure() { +        let _a: Measure = "1.5em".parse().unwrap(); +        let _b: Measure = "20 mm".parse().unwrap(); +        let _c: Measure = ".5in".parse().unwrap(); +        let _d: Measure = "1.pc".parse().unwrap(); +    }  }  pub(crate) trait CanBeEmpty { -	fn is_empty(&self) -> bool; +    fn is_empty(&self) -> bool;  }  /* Specialization necessary  impl<T> CanBeEmpty for T { -	fn is_empty(&self) -> bool { false } +    fn is_empty(&self) -> bool { false }  }  */  macro_rules! impl_cannot_be_empty { -	($t:ty) => { -		impl CanBeEmpty for $t { -			fn is_empty(&self) -> bool { false } -		} -	}; -	($t:ty, $($ts:ty),*) => { -		impl_cannot_be_empty!($t); -		impl_cannot_be_empty!($($ts),*); -	}; +    ($t:ty) => { +        impl CanBeEmpty for $t { +            fn is_empty(&self) -> bool { false } +        } +    }; +    ($t:ty, $($ts:ty),*) => { +        impl_cannot_be_empty!($t); +        impl_cannot_be_empty!($($ts),*); +    };  }  impl_cannot_be_empty!(Url);  impl_cannot_be_empty!(TableGroupCols);  impl<T> CanBeEmpty for Option<T> { -	fn is_empty(&self) -> bool { self.is_none() } +    fn is_empty(&self) -> bool { +        self.is_none() +    }  }  impl<T> CanBeEmpty for Vec<T> { -	fn is_empty(&self) -> bool { self.is_empty() } +    fn is_empty(&self) -> bool { +        self.is_empty() +    }  }  impl CanBeEmpty for bool { -	fn is_empty(&self) -> bool { !self } +    fn is_empty(&self) -> bool { +        !self +    }  }  impl CanBeEmpty for FixedSpace { -	fn is_empty(&self) -> bool { self == &FixedSpace::default() } +    fn is_empty(&self) -> bool { +        self == &FixedSpace::default() +    }  } diff --git a/document_tree/src/element_categories.rs b/document_tree/src/element_categories.rs index 1f5fcde..e0a1886 100644 --- a/document_tree/src/element_categories.rs +++ b/document_tree/src/element_categories.rs @@ -1,130 +1,200 @@ -use std::fmt::{self,Debug,Formatter}; +use std::fmt::{self, Debug, Formatter};  use serde_derive::Serialize;  use crate::elements::*;  pub trait HasChildren<C> { -	fn with_children(children: Vec<C>) -> Self; -	fn children(&self) -> &Vec<C>; -	fn children_mut(&mut self) -> &mut Vec<C>; -	fn append_child<R: Into<C>>(&mut self, child: R) { -		self.children_mut().push(child.into()); -	} -	fn append_children<R: Into<C> + Clone>(&mut self, more: &[R]) { -		let children = self.children_mut(); -		children.reserve(more.len()); -		for child in more { -			children.push(child.clone().into()); -		} -	} +    fn with_children(children: Vec<C>) -> Self; +    fn children(&self) -> &Vec<C>; +    fn children_mut(&mut self) -> &mut Vec<C>; +    fn append_child<R: Into<C>>(&mut self, child: R) { +        self.children_mut().push(child.into()); +    } +    fn append_children<R: Into<C> + Clone>(&mut self, more: &[R]) { +        let children = self.children_mut(); +        children.reserve(more.len()); +        for child in more { +            children.push(child.clone().into()); +        } +    }  }  macro_rules! impl_into { -	([ $( (($subcat:ident :: $entry:ident), $supcat:ident), )+ ]) => { -		$( impl_into!($subcat::$entry => $supcat); )+ -	}; -	($subcat:ident :: $entry:ident => $supcat:ident ) => { -		impl From<$entry> for $supcat { -			fn from(inner: $entry) -> Self { -				$supcat::$subcat(Box::new(inner.into())) -			} -		} -	}; +    ([ $( (($subcat:ident :: $entry:ident), $supcat:ident), )+ ]) => { +        $( impl_into!($subcat::$entry => $supcat); )+ +    }; +    ($subcat:ident :: $entry:ident => $supcat:ident ) => { +        impl From<$entry> for $supcat { +            fn from(inner: $entry) -> Self { +                $supcat::$subcat(Box::new(inner.into())) +            } +        } +    };  }  macro_rules! synonymous_enum { -	( $subcat:ident : $($supcat:ident),+ ; $midcat:ident : $supsupcat:ident { $($entry:ident),+ $(,)* } ) => { -		synonymous_enum!($subcat : $( $supcat ),+ , $midcat { $($entry,)* }); -		$( impl_into!($midcat::$entry => $supsupcat); )+ -	}; -	( $subcat:ident : $($supcat:ident),+ { $($entry:ident),+ $(,)* } ) => { -		synonymous_enum!($subcat { $( $entry, )* }); -		cartesian!(impl_into, [ $( ($subcat::$entry) ),+ ], [ $($supcat),+ ]); -	}; -	( $name:ident { $( $entry:ident ),+ $(,)* } ) => { -		#[derive(PartialEq,Serialize,Clone)] -		pub enum $name { $( -			$entry(Box<$entry>), -		)* } - -		impl Debug for $name { -			fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> { -				match *self { -					$( $name::$entry(ref inner) => inner.fmt(fmt), )* -				} -			} -		} - -		$( impl From<$entry> for $name { -			fn from(inner: $entry) -> Self { -				$name::$entry(Box::new(inner)) -			} -		} )* -	}; +    ( $subcat:ident : $($supcat:ident),+ ; $midcat:ident : $supsupcat:ident { $($entry:ident),+ $(,)* } ) => { +        synonymous_enum!($subcat : $( $supcat ),+ , $midcat { $($entry,)* }); +        $( impl_into!($midcat::$entry => $supsupcat); )+ +    }; +    ( $subcat:ident : $($supcat:ident),+ { $($entry:ident),+ $(,)* } ) => { +        synonymous_enum!($subcat { $( $entry, )* }); +        cartesian!(impl_into, [ $( ($subcat::$entry) ),+ ], [ $($supcat),+ ]); +    }; +    ( $name:ident { $( $entry:ident ),+ $(,)* } ) => { +        #[derive(PartialEq,Serialize,Clone)] +        pub enum $name { $( +            $entry(Box<$entry>), +        )* } + +        impl Debug for $name { +            fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> { +                match *self { +                    $( $name::$entry(ref inner) => inner.fmt(fmt), )* +                } +            } +        } + +        $( impl From<$entry> for $name { +            fn from(inner: $entry) -> Self { +                $name::$entry(Box::new(inner)) +            } +        } )* +    };  } -synonymous_enum!(StructuralSubElement { Title, Subtitle, Decoration, Docinfo, SubStructure }); +synonymous_enum!(StructuralSubElement { +    Title, +    Subtitle, +    Decoration, +    Docinfo, +    SubStructure +});  synonymous_enum!(SubStructure: StructuralSubElement { Topic, Sidebar, Transition, Section, BodyElement });  synonymous_enum!(BodyElement: SubTopic, SubSidebar, SubBlockQuote, SubFootnote, SubFigure; SubStructure: StructuralSubElement { -	//Simple -	Paragraph, LiteralBlock, DoctestBlock, MathBlock, Rubric, SubstitutionDefinition, Comment, Pending, Target, Raw, Image, -	//Compound -	Compound, Container, -	BulletList, EnumeratedList, DefinitionList, FieldList, OptionList, -	LineBlock, BlockQuote, Admonition, Attention, Hint, Note, Caution, Danger, Error, Important, Tip, Warning, Footnote, Citation, SystemMessage, Figure, Table +    //Simple +    Paragraph, LiteralBlock, DoctestBlock, MathBlock, Rubric, SubstitutionDefinition, Comment, Pending, Target, Raw, Image, +    //Compound +    Compound, Container, +    BulletList, EnumeratedList, DefinitionList, FieldList, OptionList, +    LineBlock, BlockQuote, Admonition, Attention, Hint, Note, Caution, Danger, Error, Important, Tip, Warning, Footnote, Citation, SystemMessage, Figure, Table  }); -synonymous_enum!(BibliographicElement { Author, Authors, Organization, Address, Contact, Version, Revision, Status, Date, Copyright, Field }); +synonymous_enum!(BibliographicElement { +    Author, +    Authors, +    Organization, +    Address, +    Contact, +    Version, +    Revision, +    Status, +    Date, +    Copyright, +    Field +});  synonymous_enum!(TextOrInlineElement { -	String, Emphasis, Strong, Literal, Reference, FootnoteReference, CitationReference, SubstitutionReference, TitleReference, Abbreviation, Acronym, Superscript, Subscript, Inline, Problematic, Generated, Math, -	//also have non-inline versions. Inline image is no figure child, inline target has content -	TargetInline, RawInline, ImageInline +    String, +    Emphasis, +    Strong, +    Literal, +    Reference, +    FootnoteReference, +    CitationReference, +    SubstitutionReference, +    TitleReference, +    Abbreviation, +    Acronym, +    Superscript, +    Subscript, +    Inline, +    Problematic, +    Generated, +    Math, +    //also have non-inline versions. Inline image is no figure child, inline target has content +    TargetInline, +    RawInline, +    ImageInline  });  //--------------\\  //Content Models\\  //--------------\\ -synonymous_enum!(AuthorInfo { Author, Organization, Address, Contact }); +synonymous_enum!(AuthorInfo { +    Author, +    Organization, +    Address, +    Contact +});  synonymous_enum!(DecorationElement { Header, Footer });  synonymous_enum!(SubTopic { Title, BodyElement }); -synonymous_enum!(SubSidebar { Topic, Title, Subtitle, BodyElement }); -synonymous_enum!(SubDLItem { Term, Classifier, Definition }); -synonymous_enum!(SubField { FieldName, FieldBody }); -synonymous_enum!(SubOptionListItem { OptionGroup, Description }); -synonymous_enum!(SubOption { OptionString, OptionArgument }); +synonymous_enum!(SubSidebar { +    Topic, +    Title, +    Subtitle, +    BodyElement +}); +synonymous_enum!(SubDLItem { +    Term, +    Classifier, +    Definition +}); +synonymous_enum!(SubField { +    FieldName, +    FieldBody +}); +synonymous_enum!(SubOptionListItem { +    OptionGroup, +    Description +}); +synonymous_enum!(SubOption { +    OptionString, +    OptionArgument +});  synonymous_enum!(SubLineBlock { LineBlock, Line }); -synonymous_enum!(SubBlockQuote { Attribution, BodyElement }); +synonymous_enum!(SubBlockQuote { +    Attribution, +    BodyElement +});  synonymous_enum!(SubFootnote { Label, BodyElement }); -synonymous_enum!(SubFigure { Caption, Legend, BodyElement }); +synonymous_enum!(SubFigure { +    Caption, +    Legend, +    BodyElement +});  synonymous_enum!(SubTable { Title, TableGroup }); -synonymous_enum!(SubTableGroup { TableColspec, TableHead, TableBody }); +synonymous_enum!(SubTableGroup { +    TableColspec, +    TableHead, +    TableBody +});  #[cfg(test)]  mod conversion_tests { -	use std::default::Default; -	use super::*; - -	#[test] -	fn basic() { -		let _: BodyElement = Paragraph::default().into(); -	} - -	#[test] -	fn more() { -		let _: SubStructure = Paragraph::default().into(); -	} - -	#[test] -	fn even_more() { -		let _: StructuralSubElement = Paragraph::default().into(); -	} - -	#[test] -	fn super_() { -		let be: BodyElement = Paragraph::default().into(); -		let _: StructuralSubElement = be.into(); -	} +    use super::*; +    use std::default::Default; + +    #[test] +    fn basic() { +        let _: BodyElement = Paragraph::default().into(); +    } + +    #[test] +    fn more() { +        let _: SubStructure = Paragraph::default().into(); +    } + +    #[test] +    fn even_more() { +        let _: StructuralSubElement = Paragraph::default().into(); +    } + +    #[test] +    fn super_() { +        let be: BodyElement = Paragraph::default().into(); +        let _: StructuralSubElement = be.into(); +    }  } diff --git a/document_tree/src/element_types.rs b/document_tree/src/element_types.rs index 429573e..42571d1 100644 --- a/document_tree/src/element_types.rs +++ b/document_tree/src/element_types.rs @@ -1,96 +1,96 @@  // enum ElementType { -// 	//structual elements -// 	Section, Topic, Sidebar, -// -// 	//structural subelements -// 	Title, Subtitle, Decoration, Docinfo, Transition, -// -// 	//bibliographic elements -// 	Author, Authors, Organization, -// 	Address { space: FixedSpace }, -// 	Contact, Version, Revision, Status, -// 	Date, Copyright, Field, -// -// 	//decoration elements -// 	Header, Footer, -// -// 	//simple body elements -// 	Paragraph, -// 	LiteralBlock { space: FixedSpace }, -// 	DoctestBlock { space: FixedSpace }, -// 	MathBlock, Rubric, -// 	SubstitutionDefinition { ltrim: bool, rtrim: bool }, -// 	Comment { space: FixedSpace }, -// 	Pending, -// 	Target { refuri: Url, refid: ID, refname: Vec<NameToken>, anonymous: bool }, -// 	Raw { space: FixedSpace, format: Vec<NameToken> }, -// 	Image { -// 		align: AlignHV, -// 		uri: Url, -// 		alt: String, -// 		height: Measure, -// 		width: Measure, -// 		scale: f64, -// 	}, -// -// 	//compound body elements -// 	Compound, Container, -// -// 	BulletList { bullet: String }, -// 	EnumeratedList { enumtype: EnumeratedListType, prefix: String, suffix: String }, -// 	DefinitionList, FieldList, OptionList, -// -// 	LineBlock, BlockQuote, -// 	Admonition, Attention, Hint, Note, -// 	Caution, Danger, Error, Important, -// 	Tip, Warning, -// 	Footnote { backrefs: Vec<ID>, auto: bool }, -// 	Citation { backrefs: Vec<ID> }, -// 	SystemMessage { backrefs: Vec<ID>, level: usize, line: usize, type_: NameToken }, -// 	Figure { align: AlignH, width: usize }, -// 	Table, //TODO: Table -// -// 	//body sub elements -// 	ListItem, -// -// 	DefinitionListItem, Term, -// 	Classifier, Definition, -// -// 	FieldName, FieldBody, -// -// 	OptionListItem, OptionGroup, Description, Option_, OptionString, -// 	OptionArgument { delimiter: String }, -// -// 	Line, Attribution, Label, -// -// 	Caption, Legend, -// -// 	//inline elements -// 	Emphasis, Strong, Literal, -// 	Reference { name: String, refuri: Url, refid: ID, refname: Vec<NameToken> }, -// 	FootnoteReference { refid: ID, refname: Vec<NameToken>, auto: bool }, -// 	CitationReference { refid: ID, refname: Vec<NameToken> }, -// 	SubstitutionReference { refname: Vec<NameToken> }, -// 	TitleReference, -// 	Abbreviation, Acronym, -// 	Superscript, Subscript, -// 	Inline, -// 	Problematic { refid: ID }, -// 	Generated, Math, -// -// 	//also have non-inline versions. Inline image is no figure child, inline target has content -// 	TargetInline { refuri: Url, refid: ID, refname: Vec<NameToken>, anonymous: bool }, -// 	RawInline { space: FixedSpace, format: Vec<NameToken> }, -// 	ImageInline { -// 		align: AlignHV, -// 		uri: Url, -// 		alt: String, -// 		height: Measure, -// 		width: Measure, -// 		scale: f64, -// 	}, -// -// 	//text element -// 	TextElement, +//     //structual elements +//     Section, Topic, Sidebar, +// +//     //structural subelements +//     Title, Subtitle, Decoration, Docinfo, Transition, +// +//     //bibliographic elements +//     Author, Authors, Organization, +//     Address { space: FixedSpace }, +//     Contact, Version, Revision, Status, +//     Date, Copyright, Field, +// +//     //decoration elements +//     Header, Footer, +// +//     //simple body elements +//     Paragraph, +//     LiteralBlock { space: FixedSpace }, +//     DoctestBlock { space: FixedSpace }, +//     MathBlock, Rubric, +//     SubstitutionDefinition { ltrim: bool, rtrim: bool }, +//     Comment { space: FixedSpace }, +//     Pending, +//     Target { refuri: Url, refid: ID, refname: Vec<NameToken>, anonymous: bool }, +//     Raw { space: FixedSpace, format: Vec<NameToken> }, +//     Image { +//         align: AlignHV, +//         uri: Url, +//         alt: String, +//         height: Measure, +//         width: Measure, +//         scale: f64, +//     }, +// +//     //compound body elements +//     Compound, Container, +// +//     BulletList { bullet: String }, +//     EnumeratedList { enumtype: EnumeratedListType, prefix: String, suffix: String }, +//     DefinitionList, FieldList, OptionList, +// +//     LineBlock, BlockQuote, +//     Admonition, Attention, Hint, Note, +//     Caution, Danger, Error, Important, +//     Tip, Warning, +//     Footnote { backrefs: Vec<ID>, auto: bool }, +//     Citation { backrefs: Vec<ID> }, +//     SystemMessage { backrefs: Vec<ID>, level: usize, line: usize, type_: NameToken }, +//     Figure { align: AlignH, width: usize }, +//     Table, //TODO: Table +// +//     //body sub elements +//     ListItem, +// +//     DefinitionListItem, Term, +//     Classifier, Definition, +// +//     FieldName, FieldBody, +// +//     OptionListItem, OptionGroup, Description, Option_, OptionString, +//     OptionArgument { delimiter: String }, +// +//     Line, Attribution, Label, +// +//     Caption, Legend, +// +//     //inline elements +//     Emphasis, Strong, Literal, +//     Reference { name: String, refuri: Url, refid: ID, refname: Vec<NameToken> }, +//     FootnoteReference { refid: ID, refname: Vec<NameToken>, auto: bool }, +//     CitationReference { refid: ID, refname: Vec<NameToken> }, +//     SubstitutionReference { refname: Vec<NameToken> }, +//     TitleReference, +//     Abbreviation, Acronym, +//     Superscript, Subscript, +//     Inline, +//     Problematic { refid: ID }, +//     Generated, Math, +// +//     //also have non-inline versions. Inline image is no figure child, inline target has content +//     TargetInline { refuri: Url, refid: ID, refname: Vec<NameToken>, anonymous: bool }, +//     RawInline { space: FixedSpace, format: Vec<NameToken> }, +//     ImageInline { +//         align: AlignHV, +//         uri: Url, +//         alt: String, +//         height: Measure, +//         width: Measure, +//         scale: f64, +//     }, +// +//     //text element +//     TextElement,  // } diff --git a/document_tree/src/elements.rs b/document_tree/src/elements.rs index e32c677..4921e1a 100644 --- a/document_tree/src/elements.rs +++ b/document_tree/src/elements.rs @@ -1,288 +1,348 @@ -use std::path::PathBuf;  use serde_derive::Serialize; +use std::path::PathBuf; -use crate::attribute_types::{CanBeEmpty,ID,NameToken}; -use crate::extra_attributes::{self,ExtraAttributes}; +use crate::attribute_types::{CanBeEmpty, NameToken, ID};  use crate::element_categories::*; - +use crate::extra_attributes::{self, ExtraAttributes};  //-----------------\\  //Element hierarchy\\  //-----------------\\  pub trait Element { -	/// A list containing one or more unique identifier keys -	fn     ids    (&    self) -> &    Vec<ID>; -	fn     ids_mut(&mut self) -> &mut Vec<ID>; -	/// a list containing the names of an element, typically originating from the element's title or content. -	/// Each name in names must be unique; if there are name conflicts (two or more elements want to the same name), -	/// the contents will be transferred to the dupnames attribute on the duplicate elements. -	/// An element may have at most one of the names or dupnames attributes, but not both. -	fn   names    (&    self) -> &    Vec<NameToken>; -	fn   names_mut(&mut self) -> &mut Vec<NameToken>; -	fn  source    (&    self) -> &    Option<PathBuf>; -	fn  source_mut(&mut self) -> &mut Option<PathBuf>; -	fn classes    (&    self) -> &    Vec<String>; -	fn classes_mut(&mut self) -> &mut Vec<String>; +    /// A list containing one or more unique identifier keys +    fn ids(&self) -> &Vec<ID>; +    fn ids_mut(&mut self) -> &mut Vec<ID>; +    /// a list containing the names of an element, typically originating from the element's title or content. +    /// Each name in names must be unique; if there are name conflicts (two or more elements want to the same name), +    /// the contents will be transferred to the dupnames attribute on the duplicate elements. +    /// An element may have at most one of the names or dupnames attributes, but not both. +    fn names(&self) -> &Vec<NameToken>; +    fn names_mut(&mut self) -> &mut Vec<NameToken>; +    fn source(&self) -> &Option<PathBuf>; +    fn source_mut(&mut self) -> &mut Option<PathBuf>; +    fn classes(&self) -> &Vec<String>; +    fn classes_mut(&mut self) -> &mut Vec<String>;  } -#[derive(Debug,Default,PartialEq,Serialize,Clone)] +#[derive(Debug, Default, PartialEq, Serialize, Clone)]  pub struct CommonAttributes { -	#[serde(skip_serializing_if = "CanBeEmpty::is_empty")] -	ids: Vec<ID>, -	#[serde(skip_serializing_if = "CanBeEmpty::is_empty")] -	names: Vec<NameToken>, -	#[serde(skip_serializing_if = "CanBeEmpty::is_empty")] -	source: Option<PathBuf>, -	#[serde(skip_serializing_if = "CanBeEmpty::is_empty")] -	classes: Vec<String>, -	//TODO: dupnames +    #[serde(skip_serializing_if = "CanBeEmpty::is_empty")] +    ids: Vec<ID>, +    #[serde(skip_serializing_if = "CanBeEmpty::is_empty")] +    names: Vec<NameToken>, +    #[serde(skip_serializing_if = "CanBeEmpty::is_empty")] +    source: Option<PathBuf>, +    #[serde(skip_serializing_if = "CanBeEmpty::is_empty")] +    classes: Vec<String>, +    //TODO: dupnames  }  //----\\  //impl\\  //----\\ -macro_rules! impl_element { ($name:ident) => ( -	impl Element for $name { -		fn     ids    (&    self) -> &    Vec<ID>         { &    self.common.ids     } -		fn     ids_mut(&mut self) -> &mut Vec<ID>         { &mut self.common.ids     } -		fn   names    (&    self) -> &    Vec<NameToken>  { &    self.common.names   } -		fn   names_mut(&mut self) -> &mut Vec<NameToken>  { &mut self.common.names   } -		fn  source    (&    self) -> &    Option<PathBuf> { &    self.common.source  } -		fn  source_mut(&mut self) -> &mut Option<PathBuf> { &mut self.common.source  } -		fn classes    (&    self) -> &    Vec<String> { &    self.common.classes } -		fn classes_mut(&mut self) -> &mut Vec<String> { &mut self.common.classes } -	} -)} +macro_rules! impl_element { +    ($name:ident) => { +        impl Element for $name { +            fn ids(&self) -> &Vec<ID> { +                &self.common.ids +            } +            fn ids_mut(&mut self) -> &mut Vec<ID> { +                &mut self.common.ids +            } +            fn names(&self) -> &Vec<NameToken> { +                &self.common.names +            } +            fn names_mut(&mut self) -> &mut Vec<NameToken> { +                &mut self.common.names +            } +            fn source(&self) -> &Option<PathBuf> { +                &self.common.source +            } +            fn source_mut(&mut self) -> &mut Option<PathBuf> { +                &mut self.common.source +            } +            fn classes(&self) -> &Vec<String> { +                &self.common.classes +            } +            fn classes_mut(&mut self) -> &mut Vec<String> { +                &mut self.common.classes +            } +        } +    }; +} -macro_rules! impl_children { ($name:ident, $childtype:ident) => ( -	impl HasChildren<$childtype> for $name { -		#[allow(clippy::needless_update)] -		fn with_children(children: Vec<$childtype>) -> $name { $name { children, ..Default::default() } } -		fn children    (&    self) -> &    Vec<$childtype> { &    self.children } -		fn children_mut(&mut self) -> &mut Vec<$childtype> { &mut self.children } -	} -)} +macro_rules! impl_children { +    ($name:ident, $childtype:ident) => { +        impl HasChildren<$childtype> for $name { +            #[allow(clippy::needless_update)] +            fn with_children(children: Vec<$childtype>) -> $name { +                $name { +                    children, +                    ..Default::default() +                } +            } +            fn children(&self) -> &Vec<$childtype> { +                &self.children +            } +            fn children_mut(&mut self) -> &mut Vec<$childtype> { +                &mut self.children +            } +        } +    }; +}  macro_rules! impl_extra { ($name:ident $($more:tt)*) => ( -	impl ExtraAttributes<extra_attributes::$name> for $name { -		#[allow(clippy::needless_update)] -		fn with_extra(extra: extra_attributes::$name) -> $name { $name { common: Default::default(), extra $($more)* } } -		fn extra    (&    self) -> &    extra_attributes::$name { &    self.extra } -		fn extra_mut(&mut self) -> &mut extra_attributes::$name { &mut self.extra } -	} +    impl ExtraAttributes<extra_attributes::$name> for $name { +        #[allow(clippy::needless_update)] +        fn with_extra(extra: extra_attributes::$name) -> $name { $name { common: Default::default(), extra $($more)* } } +        fn extra    (&    self) -> &    extra_attributes::$name { &    self.extra } +        fn extra_mut(&mut self) -> &mut extra_attributes::$name { &mut self.extra } +    }  )}  trait HasExtraAndChildren<C, A> { -	fn with_extra_and_children(extra: A, children: Vec<C>) -> Self; +    fn with_extra_and_children(extra: A, children: Vec<C>) -> Self;  } -impl<T, C, A> HasExtraAndChildren<C, A> for T where T: HasChildren<C> + ExtraAttributes<A> { -	#[allow(clippy::needless_update)] -	fn with_extra_and_children(extra: A, mut children: Vec<C>) -> Self { -		let mut r = Self::with_extra(extra); -		r.children_mut().append(&mut children); -		r -	} +impl<T, C, A> HasExtraAndChildren<C, A> for T +where +    T: HasChildren<C> + ExtraAttributes<A>, +{ +    #[allow(clippy::needless_update)] +    fn with_extra_and_children(extra: A, mut children: Vec<C>) -> Self { +        let mut r = Self::with_extra(extra); +        r.children_mut().append(&mut children); +        r +    }  }  macro_rules! impl_new {( -	$(#[$attr:meta])* -	pub struct $name:ident { $( -		$(#[$fattr:meta])* -		$field:ident : $typ:path -	),* $(,)* } +    $(#[$attr:meta])* +    pub struct $name:ident { $( +        $(#[$fattr:meta])* +        $field:ident : $typ:path +    ),* $(,)* }  ) => ( -	$(#[$attr])* -	#[derive(Debug,PartialEq,Serialize,Clone)] -	pub struct $name { $( -		$(#[$fattr])* $field: $typ, -	)* } -	impl $name { -		pub fn new( $( $field: $typ, )* ) -> $name { $name { $( $field, )* } } -	} +    $(#[$attr])* +    #[derive(Debug,PartialEq,Serialize,Clone)] +    pub struct $name { $( +        $(#[$fattr])* $field: $typ, +    )* } +    impl $name { +        pub fn new( $( $field: $typ, )* ) -> $name { $name { $( $field, )* } } +    }  )}  macro_rules! impl_elem { -	($name:ident) => { -		impl_new!(#[derive(Default)] pub struct $name { -			#[serde(flatten)] common: CommonAttributes, -		}); -		impl_element!($name); -	}; -	($name:ident; +) => { -		impl_new!(#[derive(Default)] pub struct $name { -			#[serde(flatten)] common: CommonAttributes, -			#[serde(flatten)] extra: extra_attributes::$name, -		}); -		impl_element!($name); impl_extra!($name, ..Default::default()); -	}; -	($name:ident; *) => { //same as above with no default -		impl_new!(pub struct $name { -			#[serde(flatten)] common: CommonAttributes, -			#[serde(flatten)] extra: extra_attributes::$name -		}); -		impl_element!($name); impl_extra!($name); -	}; -	($name:ident, $childtype:ident) => { -		impl_new!(#[derive(Default)] pub struct $name { -			#[serde(flatten)] common: CommonAttributes, -			children: Vec<$childtype>, -		}); -		impl_element!($name); impl_children!($name, $childtype); -	}; -	($name:ident, $childtype:ident; +) => { -		impl_new!(#[derive(Default)] pub struct $name { -			#[serde(flatten)] common: CommonAttributes, -			#[serde(flatten)] extra: extra_attributes::$name, -			children: Vec<$childtype>, -		}); -		impl_element!($name); impl_extra!($name, ..Default::default()); impl_children!($name, $childtype); -	}; +    ($name:ident) => { +        impl_new!( +            #[derive(Default)] +            pub struct $name { +                #[serde(flatten)] +                common: CommonAttributes, +            } +        ); +        impl_element!($name); +    }; +    ($name:ident; +) => { +        impl_new!( +            #[derive(Default)] +            pub struct $name { +                #[serde(flatten)] +                common: CommonAttributes, +                #[serde(flatten)] +                extra: extra_attributes::$name, +            } +        ); +        impl_element!($name); +        impl_extra!($name, ..Default::default()); +    }; +    ($name:ident; *) => { +        //same as above with no default +        impl_new!( +            pub struct $name { +                #[serde(flatten)] +                common: CommonAttributes, +                #[serde(flatten)] +                extra: extra_attributes::$name, +            } +        ); +        impl_element!($name); +        impl_extra!($name); +    }; +    ($name:ident, $childtype:ident) => { +        impl_new!( +            #[derive(Default)] +            pub struct $name { +                #[serde(flatten)] +                common: CommonAttributes, +                children: Vec<$childtype>, +            } +        ); +        impl_element!($name); +        impl_children!($name, $childtype); +    }; +    ($name:ident, $childtype:ident; +) => { +        impl_new!( +            #[derive(Default)] +            pub struct $name { +                #[serde(flatten)] +                common: CommonAttributes, +                #[serde(flatten)] +                extra: extra_attributes::$name, +                children: Vec<$childtype>, +            } +        ); +        impl_element!($name); +        impl_extra!($name, ..Default::default()); +        impl_children!($name, $childtype); +    };  }  macro_rules! impl_elems { ( $( ($($args:tt)*) )* ) => ( -	$( impl_elem!($($args)*); )* +    $( impl_elem!($($args)*); )*  )} - -#[derive(Default,Debug,Serialize)] -pub struct Document { children: Vec<StructuralSubElement> } +#[derive(Default, Debug, Serialize)] +pub struct Document { +    children: Vec<StructuralSubElement>, +}  impl_children!(Document, StructuralSubElement);  impl_elems!( -	//structual elements -	(Section, StructuralSubElement) -	(Topic,   SubTopic) -	(Sidebar, SubSidebar) - -	//structural subelements -	(Title,      TextOrInlineElement) -	(Subtitle,   TextOrInlineElement) -	(Decoration, DecorationElement) -	(Docinfo,    BibliographicElement) -	(Transition) - -	//bibliographic elements -	(Author,       TextOrInlineElement) -	(Authors,      AuthorInfo) -	(Organization, TextOrInlineElement) -	(Address,      TextOrInlineElement; +) -	(Contact,      TextOrInlineElement) -	(Version,      TextOrInlineElement) -	(Revision,     TextOrInlineElement) -	(Status,       TextOrInlineElement) -	(Date,         TextOrInlineElement) -	(Copyright,    TextOrInlineElement) -	(Field,        SubField) - -	//decoration elements -	(Header, BodyElement) -	(Footer, BodyElement) - -	//simple body elements -	(Paragraph,              TextOrInlineElement) -	(LiteralBlock,           TextOrInlineElement; +) -	(DoctestBlock,           TextOrInlineElement; +) -	(MathBlock,              String) -	(Rubric,                 TextOrInlineElement) -	(SubstitutionDefinition, TextOrInlineElement; +) -	(Comment,                TextOrInlineElement; +) -	(Pending) -	(Target; +) -	(Raw, String; +) -	(Image; *) - -	//compound body elements -	(Compound,  BodyElement) -	(Container, BodyElement) - -	(BulletList,     ListItem; +) -	(EnumeratedList, ListItem; +) -	(DefinitionList, DefinitionListItem) -	(FieldList,      Field) -	(OptionList,     OptionListItem) - -	(LineBlock,     SubLineBlock) -	(BlockQuote,    SubBlockQuote) -	(Admonition,    SubTopic) -	(Attention,     BodyElement) -	(Hint,          BodyElement) -	(Note,          BodyElement) -	(Caution,       BodyElement) -	(Danger,        BodyElement) -	(Error,         BodyElement) -	(Important,     BodyElement) -	(Tip,           BodyElement) -	(Warning,       BodyElement) -	(Footnote,      SubFootnote; +) -	(Citation,      SubFootnote; +) -	(SystemMessage, BodyElement; +) -	(Figure,        SubFigure;   +) -	(Table,         SubTable;    +) - -	//table elements -	(TableGroup, SubTableGroup; +) -	(TableHead,  TableRow;      +) -	(TableBody,  TableRow;      +) -	(TableRow,   TableEntry;    +) -	(TableEntry, BodyElement;   +) -	(TableColspec; +) - -	//body sub elements -	(ListItem, BodyElement) - -	(DefinitionListItem, SubDLItem) -	(Term,               TextOrInlineElement) -	(Classifier,         TextOrInlineElement) -	(Definition,         BodyElement) - -	(FieldName, TextOrInlineElement) -	(FieldBody, BodyElement) - -	(OptionListItem, SubOptionListItem) -	(OptionGroup,    Option_) -	(Description,    BodyElement) -	(Option_,        SubOption) -	(OptionString,   String) -	(OptionArgument, String; +) - -	(Line,        TextOrInlineElement) -	(Attribution, TextOrInlineElement) -	(Label,       TextOrInlineElement) - -	(Caption, TextOrInlineElement) -	(Legend,  BodyElement) - -	//inline elements -	(Emphasis,              TextOrInlineElement) -	(Literal,               String) -	(Reference,             TextOrInlineElement; +) -	(Strong,                TextOrInlineElement) -	(FootnoteReference,     TextOrInlineElement; +) -	(CitationReference,     TextOrInlineElement; +) -	(SubstitutionReference, TextOrInlineElement; +) -	(TitleReference,        TextOrInlineElement) -	(Abbreviation,          TextOrInlineElement) -	(Acronym,               TextOrInlineElement) -	(Superscript,           TextOrInlineElement) -	(Subscript,             TextOrInlineElement) -	(Inline,                TextOrInlineElement) -	(Problematic,           TextOrInlineElement; +) -	(Generated,             TextOrInlineElement) -	(Math,                  String) - -	//also have non-inline versions. Inline image is no figure child, inline target has content -	(TargetInline, String; +) -	(RawInline,    String; +) -	(ImageInline; *) - -	//text element = String +    //structual elements +    (Section, StructuralSubElement) +    (Topic,   SubTopic) +    (Sidebar, SubSidebar) + +    //structural subelements +    (Title,      TextOrInlineElement) +    (Subtitle,   TextOrInlineElement) +    (Decoration, DecorationElement) +    (Docinfo,    BibliographicElement) +    (Transition) + +    //bibliographic elements +    (Author,       TextOrInlineElement) +    (Authors,      AuthorInfo) +    (Organization, TextOrInlineElement) +    (Address,      TextOrInlineElement; +) +    (Contact,      TextOrInlineElement) +    (Version,      TextOrInlineElement) +    (Revision,     TextOrInlineElement) +    (Status,       TextOrInlineElement) +    (Date,         TextOrInlineElement) +    (Copyright,    TextOrInlineElement) +    (Field,        SubField) + +    //decoration elements +    (Header, BodyElement) +    (Footer, BodyElement) + +    //simple body elements +    (Paragraph,              TextOrInlineElement) +    (LiteralBlock,           TextOrInlineElement; +) +    (DoctestBlock,           TextOrInlineElement; +) +    (MathBlock,              String) +    (Rubric,                 TextOrInlineElement) +    (SubstitutionDefinition, TextOrInlineElement; +) +    (Comment,                TextOrInlineElement; +) +    (Pending) +    (Target; +) +    (Raw, String; +) +    (Image; *) + +    //compound body elements +    (Compound,  BodyElement) +    (Container, BodyElement) + +    (BulletList,     ListItem; +) +    (EnumeratedList, ListItem; +) +    (DefinitionList, DefinitionListItem) +    (FieldList,      Field) +    (OptionList,     OptionListItem) + +    (LineBlock,     SubLineBlock) +    (BlockQuote,    SubBlockQuote) +    (Admonition,    SubTopic) +    (Attention,     BodyElement) +    (Hint,          BodyElement) +    (Note,          BodyElement) +    (Caution,       BodyElement) +    (Danger,        BodyElement) +    (Error,         BodyElement) +    (Important,     BodyElement) +    (Tip,           BodyElement) +    (Warning,       BodyElement) +    (Footnote,      SubFootnote; +) +    (Citation,      SubFootnote; +) +    (SystemMessage, BodyElement; +) +    (Figure,        SubFigure;   +) +    (Table,         SubTable;    +) + +    //table elements +    (TableGroup, SubTableGroup; +) +    (TableHead,  TableRow;      +) +    (TableBody,  TableRow;      +) +    (TableRow,   TableEntry;    +) +    (TableEntry, BodyElement;   +) +    (TableColspec; +) + +    //body sub elements +    (ListItem, BodyElement) + +    (DefinitionListItem, SubDLItem) +    (Term,               TextOrInlineElement) +    (Classifier,         TextOrInlineElement) +    (Definition,         BodyElement) + +    (FieldName, TextOrInlineElement) +    (FieldBody, BodyElement) + +    (OptionListItem, SubOptionListItem) +    (OptionGroup,    Option_) +    (Description,    BodyElement) +    (Option_,        SubOption) +    (OptionString,   String) +    (OptionArgument, String; +) + +    (Line,        TextOrInlineElement) +    (Attribution, TextOrInlineElement) +    (Label,       TextOrInlineElement) + +    (Caption, TextOrInlineElement) +    (Legend,  BodyElement) + +    //inline elements +    (Emphasis,              TextOrInlineElement) +    (Literal,               String) +    (Reference,             TextOrInlineElement; +) +    (Strong,                TextOrInlineElement) +    (FootnoteReference,     TextOrInlineElement; +) +    (CitationReference,     TextOrInlineElement; +) +    (SubstitutionReference, TextOrInlineElement; +) +    (TitleReference,        TextOrInlineElement) +    (Abbreviation,          TextOrInlineElement) +    (Acronym,               TextOrInlineElement) +    (Superscript,           TextOrInlineElement) +    (Subscript,             TextOrInlineElement) +    (Inline,                TextOrInlineElement) +    (Problematic,           TextOrInlineElement; +) +    (Generated,             TextOrInlineElement) +    (Math,                  String) + +    //also have non-inline versions. Inline image is no figure child, inline target has content +    (TargetInline, String; +) +    (RawInline,    String; +) +    (ImageInline; *) + +    //text element = String  );  impl<'a> From<&'a str> for TextOrInlineElement { -	fn from(s: &'a str) -> Self { -		s.to_owned().into() -	} +    fn from(s: &'a str) -> Self { +        s.to_owned().into() +    }  } diff --git a/document_tree/src/extra_attributes.rs b/document_tree/src/extra_attributes.rs index 45fcf32..9dcabd7 100644 --- a/document_tree/src/extra_attributes.rs +++ b/document_tree/src/extra_attributes.rs @@ -1,62 +1,60 @@  use serde_derive::Serialize; -use crate::url::Url;  use crate::attribute_types::{ -	CanBeEmpty, -	FixedSpace, -	ID,NameToken, -	AlignHV,AlignH,AlignV, -	TableAlignH,TableBorder,TableGroupCols, -	Measure, -	EnumeratedListType, +    AlignH, AlignHV, AlignV, CanBeEmpty, EnumeratedListType, FixedSpace, Measure, NameToken, +    TableAlignH, TableBorder, TableGroupCols, ID,  }; +use crate::url::Url;  pub trait ExtraAttributes<A> { -	fn with_extra(extra: A) -> Self; -	fn extra    (&    self) -> &    A; -	fn extra_mut(&mut self) -> &mut A; +    fn with_extra(extra: A) -> Self; +    fn extra(&self) -> &A; +    fn extra_mut(&mut self) -> &mut A;  }  macro_rules! impl_extra { -	( $name:ident { $( $(#[$pattr:meta])* $param:ident : $type:ty ),* $(,)* } ) => ( -		impl_extra!( -			#[derive(Default,Debug,PartialEq,Serialize,Clone)] -			$name { $( $(#[$pattr])* $param : $type, )* } -		); -	); -	( $(#[$attr:meta])+ $name:ident { $( $(#[$pattr:meta])* $param:ident : $type:ty ),* $(,)* } ) => ( -		$(#[$attr])+ -		pub struct $name { $( -			$(#[$pattr])* -			#[serde(skip_serializing_if = "CanBeEmpty::is_empty")] -			pub $param : $type, -		)* } -	); +    ( $name:ident { $( $(#[$pattr:meta])* $param:ident : $type:ty ),* $(,)* } ) => ( +        impl_extra!( +            #[derive(Default,Debug,PartialEq,Serialize,Clone)] +            $name { $( $(#[$pattr])* $param : $type, )* } +        ); +    ); +    ( $(#[$attr:meta])+ $name:ident { $( $(#[$pattr:meta])* $param:ident : $type:ty ),* $(,)* } ) => ( +        $(#[$attr])+ +        pub struct $name { $( +            $(#[$pattr])* +            #[serde(skip_serializing_if = "CanBeEmpty::is_empty")] +            pub $param : $type, +        )* } +    );  }  impl_extra!(Address { space: FixedSpace });  impl_extra!(LiteralBlock { space: FixedSpace });  impl_extra!(DoctestBlock { space: FixedSpace }); -impl_extra!(SubstitutionDefinition { ltrim: bool, rtrim: bool }); +impl_extra!(SubstitutionDefinition { +    ltrim: bool, +    rtrim: bool +});  impl_extra!(Comment { space: FixedSpace });  impl_extra!(Target { -	/// External reference to a URI/URL -	refuri: Option<Url>, -	/// References to ids attributes in other elements -	refid: Option<ID>, -	/// Internal reference to the names attribute of another element. May resolve to either an internal or external reference. -	refname: Vec<NameToken>, -	anonymous: bool, +    /// External reference to a URI/URL +    refuri: Option<Url>, +    /// References to ids attributes in other elements +    refid: Option<ID>, +    /// Internal reference to the names attribute of another element. May resolve to either an internal or external reference. +    refname: Vec<NameToken>, +    anonymous: bool,  });  impl_extra!(Raw { space: FixedSpace, format: Vec<NameToken> });  impl_extra!(#[derive(Debug,PartialEq,Serialize,Clone)] Image { -	uri: Url, -	align: Option<AlignHV>, -	alt: Option<String>, -	height: Option<Measure>, -	width: Option<Measure>, -	scale: Option<u8>, -	target: Option<Url>,  // Not part of the DTD but a valid argument +    uri: Url, +    align: Option<AlignHV>, +    alt: Option<String>, +    height: Option<Measure>, +    width: Option<Measure>, +    scale: Option<u8>, +    target: Option<Url>,  // Not part of the DTD but a valid argument  });  //bools usually are XML yesorno. “auto” however either exists and is set to something random like “1” or doesn’t exist @@ -81,13 +79,13 @@ impl_extra!(TableColspec { colnum: Option<usize>, colname: Option<NameToken>, co  impl_extra!(OptionArgument { delimiter: Option<String> });  impl_extra!(Reference { -	name: Option<NameToken>,  //TODO: is CDATA in the DTD, so maybe no nametoken? -	/// External reference to a URI/URL -	refuri: Option<Url>, -	/// References to ids attributes in other elements -	refid: Option<ID>, -	/// Internal reference to the names attribute of another element -	refname: Vec<NameToken>, +    name: Option<NameToken>,  //TODO: is CDATA in the DTD, so maybe no nametoken? +    /// External reference to a URI/URL +    refuri: Option<Url>, +    /// References to ids attributes in other elements +    refid: Option<ID>, +    /// Internal reference to the names attribute of another element +    refname: Vec<NameToken>,  });  impl_extra!(FootnoteReference { refid: Option<ID>, refname: Vec<NameToken>, auto: bool });  impl_extra!(CitationReference { refid: Option<ID>, refname: Vec<NameToken> }); @@ -96,25 +94,27 @@ impl_extra!(Problematic { refid: Option<ID> });  //also have non-inline versions. Inline image is no figure child, inline target has content  impl_extra!(TargetInline { -	/// External reference to a URI/URL -	refuri: Option<Url>, -	/// References to ids attributes in other elements -	refid: Option<ID>, -	/// Internal reference to the names attribute of another element. May resolve to either an internal or external reference. -	refname: Vec<NameToken>, -	anonymous: bool, +    /// External reference to a URI/URL +    refuri: Option<Url>, +    /// References to ids attributes in other elements +    refid: Option<ID>, +    /// Internal reference to the names attribute of another element. May resolve to either an internal or external reference. +    refname: Vec<NameToken>, +    anonymous: bool,  });  impl_extra!(RawInline { space: FixedSpace, format: Vec<NameToken> });  pub type ImageInline = Image;  impl Image { -	pub fn new(uri: Url) -> Image { Image { -		uri, -		align: None, -		alt: None, -		height: None, -		width: None, -		scale: None, -		target: None, -	} } +    pub fn new(uri: Url) -> Image { +        Image { +            uri, +            align: None, +            alt: None, +            height: None, +            width: None, +            scale: None, +            target: None, +        } +    }  } diff --git a/document_tree/src/lib.rs b/document_tree/src/lib.rs index 00d6cb2..4b34ea9 100644 --- a/document_tree/src/lib.rs +++ b/document_tree/src/lib.rs @@ -1,4 +1,4 @@ -#![recursion_limit="256"] +#![recursion_limit = "256"]  /// See [doctree][] reference.  /// Serves as AST. @@ -8,45 +8,45 @@  #[macro_use]  mod macro_util; -pub mod url; -pub mod elements; +pub mod attribute_types;  pub mod element_categories; +pub mod elements;  pub mod extra_attributes; -pub mod attribute_types; +pub mod url; +pub use self::element_categories::HasChildren;  pub use self::elements::*; //Element,CommonAttributes,HasExtraAndChildren  pub use self::extra_attributes::ExtraAttributes; -pub use self::element_categories::HasChildren;  #[cfg(test)]  mod tests { -	use super::*; -	use std::default::Default; - -	#[test] -	fn imperative() { -		let mut doc = Document::default(); -		let mut title = Title::default(); -		let url = "https://example.com/image.jpg".parse().unwrap(); -		let image = ImageInline::with_extra(extra_attributes::ImageInline::new(url)); -		title.append_child("Hi"); -		title.append_child(image); -		doc.append_child(title); - -		println!("{:?}", doc); -	} - -	#[test] -	fn descriptive() { -		let doc = Document::with_children(vec![ -			Title::with_children(vec![ -				"Hi".into(), -				ImageInline::with_extra(extra_attributes::ImageInline::new( -					"https://example.com/image.jpg".parse().unwrap() -				)).into(), -			]).into() -		]); - -		println!("{:?}", doc); -	} +    use super::*; +    use std::default::Default; + +    #[test] +    fn imperative() { +        let mut doc = Document::default(); +        let mut title = Title::default(); +        let url = "https://example.com/image.jpg".parse().unwrap(); +        let image = ImageInline::with_extra(extra_attributes::ImageInline::new(url)); +        title.append_child("Hi"); +        title.append_child(image); +        doc.append_child(title); + +        println!("{:?}", doc); +    } + +    #[test] +    fn descriptive() { +        let doc = Document::with_children(vec![Title::with_children(vec![ +            "Hi".into(), +            ImageInline::with_extra(extra_attributes::ImageInline::new( +                "https://example.com/image.jpg".parse().unwrap(), +            )) +            .into(), +        ]) +        .into()]); + +        println!("{:?}", doc); +    }  } diff --git a/document_tree/src/macro_util.rs b/document_tree/src/macro_util.rs index dcf3725..e370011 100644 --- a/document_tree/src/macro_util.rs +++ b/document_tree/src/macro_util.rs @@ -1,42 +1,41 @@  macro_rules! cartesian_impl { -	($out:tt [] $b:tt $init_b:tt $submacro:tt) => { -		$submacro!{$out} -	}; -	($out:tt [$a:tt, $($at:tt)*] [] $init_b:tt $submacro:tt) => { -		cartesian_impl!{$out [$($at)*] $init_b $init_b $submacro} -	}; -	([$($out:tt)*] [$a:tt, $($at:tt)*] [$b:tt, $($bt:tt)*] $init_b:tt $submacro:tt) => { -		cartesian_impl!{[$($out)* ($a, $b),] [$a, $($at)*] [$($bt)*] $init_b $submacro} -	}; +    ($out:tt [] $b:tt $init_b:tt $submacro:tt) => { +        $submacro!{$out} +    }; +    ($out:tt [$a:tt, $($at:tt)*] [] $init_b:tt $submacro:tt) => { +        cartesian_impl!{$out [$($at)*] $init_b $init_b $submacro} +    }; +    ([$($out:tt)*] [$a:tt, $($at:tt)*] [$b:tt, $($bt:tt)*] $init_b:tt $submacro:tt) => { +        cartesian_impl!{[$($out)* ($a, $b),] [$a, $($at)*] [$($bt)*] $init_b $submacro} +    };  }  macro_rules! cartesian { -	( $submacro:tt, [$($a:tt)*], [$($b:tt)*]) => { -		cartesian_impl!{[] [$($a)*,] [$($b)*,] [$($b)*,] $submacro} -	}; +    ( $submacro:tt, [$($a:tt)*], [$($b:tt)*]) => { +        cartesian_impl!{[] [$($a)*,] [$($b)*,] [$($b)*,] $submacro} +    };  } -  #[cfg(test)]  mod tests { -	macro_rules! print_cartesian { -		( [ $(($a1:tt, $a2:tt)),* , ] ) => { -			fn test_f(x:i64, y:i64) -> Result<(i64, i64), ()> { -				match (x, y) { -				$( -					($a1, $a2) => { Ok(($a1, $a2)) } -				)* -				_ => { Err(()) } -				} -			} -		}; -	} +    macro_rules! print_cartesian { +        ( [ $(($a1:tt, $a2:tt)),* , ] ) => { +            fn test_f(x:i64, y:i64) -> Result<(i64, i64), ()> { +                match (x, y) { +                $( +                    ($a1, $a2) => { Ok(($a1, $a2)) } +                )* +                _ => { Err(()) } +                } +            } +        }; +    } -	#[test] -	fn print_cartesian() { -		cartesian!(print_cartesian, [1, 2, 3], [4, 5, 6]); -		assert_eq!(test_f(1, 4), Ok((1, 4))); -		assert_eq!(test_f(1, 3), Err(())); -		assert_eq!(test_f(3, 5), Ok((3, 5))); -	} +    #[test] +    fn print_cartesian() { +        cartesian!(print_cartesian, [1, 2, 3], [4, 5, 6]); +        assert_eq!(test_f(1, 4), Ok((1, 4))); +        assert_eq!(test_f(1, 3), Err(())); +        assert_eq!(test_f(3, 5), Ok((3, 5))); +    }  } diff --git a/document_tree/src/url.rs b/document_tree/src/url.rs index 543f9e5..be320e9 100644 --- a/document_tree/src/url.rs +++ b/document_tree/src/url.rs @@ -1,78 +1,74 @@  use std::fmt;  use std::str::FromStr; -use url::{self,ParseError};  use serde_derive::Serialize; - +use url::{self, ParseError};  fn starts_with_scheme(input: &str) -> bool { -	let scheme = input.split(':').next().unwrap(); -	if scheme == input || scheme.is_empty() { -		return false; -	} -	let mut chars = input.chars(); -	// First character. -	if !chars.next().unwrap().is_ascii_alphabetic() { -		return false; -	} -	for ch in chars { -		if !ch.is_ascii_alphanumeric() && ch != '+' && ch != '-' && ch != '.' { -			return false; -		} -	} -	true +    let scheme = input.split(':').next().unwrap(); +    if scheme == input || scheme.is_empty() { +        return false; +    } +    let mut chars = input.chars(); +    // First character. +    if !chars.next().unwrap().is_ascii_alphabetic() { +        return false; +    } +    for ch in chars { +        if !ch.is_ascii_alphanumeric() && ch != '+' && ch != '-' && ch != '.' { +            return false; +        } +    } +    true  }  /// The string representation of a URL, either absolute or relative, that has  /// been verified as a valid URL on construction. -#[derive(Debug,PartialEq,Serialize,Clone)] +#[derive(Debug, PartialEq, Serialize, Clone)]  #[serde(transparent)]  pub struct Url(String);  impl Url { -	pub fn parse_absolute(input: &str) -> Result<Self, ParseError> { -		Ok(url::Url::parse(input)?.into()) -	} -	pub fn parse_relative(input: &str) -> Result<Self, ParseError> { -		// We're assuming that any scheme through which RsT documents are being -		// accessed is a hierarchical scheme, and so we can parse relative to a -		// random hierarchical URL. -		if input.starts_with('/') || !starts_with_scheme(input) { -			// Continue only if the parse succeeded, disregarding its result. -			let random_base_url = url::Url::parse("https://a/b").unwrap(); -			url::Url::options() -				.base_url(Some(&random_base_url)) -				.parse(input)?; -			Ok(Url(input.into())) -		} else { -			// If this is a URL at all, it's an absolute one. -			// There's no appropriate variant of url::ParseError really. -			Err(ParseError::SetHostOnCannotBeABaseUrl) -		} -	} -	pub fn as_str(&self) -> &str { -		self.0.as_str() -	} +    pub fn parse_absolute(input: &str) -> Result<Self, ParseError> { +        Ok(url::Url::parse(input)?.into()) +    } +    pub fn parse_relative(input: &str) -> Result<Self, ParseError> { +        // We're assuming that any scheme through which RsT documents are being +        // accessed is a hierarchical scheme, and so we can parse relative to a +        // random hierarchical URL. +        if input.starts_with('/') || !starts_with_scheme(input) { +            // Continue only if the parse succeeded, disregarding its result. +            let random_base_url = url::Url::parse("https://a/b").unwrap(); +            url::Url::options() +                .base_url(Some(&random_base_url)) +                .parse(input)?; +            Ok(Url(input.into())) +        } else { +            // If this is a URL at all, it's an absolute one. +            // There's no appropriate variant of url::ParseError really. +            Err(ParseError::SetHostOnCannotBeABaseUrl) +        } +    } +    pub fn as_str(&self) -> &str { +        self.0.as_str() +    }  }  impl From<url::Url> for Url { -	fn from(url: url::Url) -> Self { -		Url(url.into()) -	} +    fn from(url: url::Url) -> Self { +        Url(url.into()) +    }  } -  impl fmt::Display for Url { -	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { -		write!(f, "{}", self.as_str()) -	} +    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +        write!(f, "{}", self.as_str()) +    }  } -  impl FromStr for Url { -	type Err = ParseError; -	fn from_str(input: &str) -> Result<Self, Self::Err> { -		Url::parse_absolute(input) -			.or_else(|_| Url::parse_relative(input)) -	} +    type Err = ParseError; +    fn from_str(input: &str) -> Result<Self, Self::Err> { +        Url::parse_absolute(input).or_else(|_| Url::parse_relative(input)) +    }  } diff --git a/parser/src/conversion.rs b/parser/src/conversion.rs index 5e855ef..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::*; +    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![]; +    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, §ion_idxs); -				let slug = title.names().iter().next().map(|at::NameToken(name)| at::ID(name.to_owned())); -				let mut section = e::Section::with_children(vec![title.into()]); -				section.ids_mut().extend(slug.into_iter()); -				super_level.push(section.into()); -				section_idxs.push(Some(super_level.len() - 1)); -			}, -			Ssubel(elem) => get_level(&mut toplevel, §ion_idxs).push(elem), -		}} -	} -	Ok(e::Document::with_children(toplevel)) +    for pair in pairs { +        if let Some(ssubel) = block::convert_ssubel(pair)? { +            match ssubel { +                Title(title, kind) => { +                    match kinds.iter().position(|k| k == &kind) { +                        // Idx points to the level we want to add, +                        // so idx-1 needs to be the last valid index. +                        Some(idx) => { +                            // If idx < len: Remove found section and all below +                            section_idxs.truncate(idx); +                            // If idx > len: Add None for skipped levels +                            // TODO: test skipped levels +                            while section_idxs.len() < idx { +                                section_idxs.push(None) +                            } +                        } +                        None => kinds.push(kind), +                    } +                    let super_level = get_level(&mut toplevel, §ion_idxs); +                    let slug = title +                        .names() +                        .iter() +                        .next() +                        .map(|at::NameToken(name)| at::ID(name.to_owned())); +                    let mut section = e::Section::with_children(vec![title.into()]); +                    section.ids_mut().extend(slug.into_iter()); +                    super_level.push(section.into()); +                    section_idxs.push(Some(super_level.len() - 1)); +                } +                Ssubel(elem) => get_level(&mut toplevel, §ion_idxs).push(elem), +            } +        } +    } +    Ok(e::Document::with_children(toplevel))  }  /// Normalizes a name in terms of whitespace. Equivalent to docutils's  /// `docutils.nodes.whitespace_normalize_name`.  pub fn whitespace_normalize_name(name: &str) -> String { -	// Python's string.split() defines whitespace differently than Rust does. -	let split_iter = name.split( -		|ch: char| ch.is_whitespace() || ('\x1C'..='\x1F').contains(&ch) -	).filter(|split| !split.is_empty()); -	let mut ret = String::new(); -	for split in split_iter { -		if !ret.is_empty() { -			ret.push(' '); -		} -		ret.push_str(split); -	} -	ret +    // 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 a68dd17..97f0e23 100644 --- a/parser/src/conversion/block.rs +++ b/parser/src/conversion/block.rs @@ -1,257 +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> { -	#[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(), -	}) +    #[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)?, -		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().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()) -	})?) +    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(); -	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 e042d01..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); +    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"); +    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 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 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 4f1b8dd..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.  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 fb5ba6b..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, Box<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, Box<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) -> 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)) +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..dbf201d 100644 --- a/parser/src/rst.pest +++ b/parser/src/rst.pest @@ -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 ab04964..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,640 +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 { -	#[allow(dead_code)] -	fn is_implicit_target(&self) -> bool { -		matches!(self, NamedTargetType::SectionTitle) -	} +    #[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::default(); -					replacement.children_mut().push( -						c::TextOrInlineElement::String(Box::new(format!("|{}|", e.extra().refname[0].0))) -					); -					// TODO: Create an ID for replacement for the system_message to reference. -					// TODO: replacement.refid pointing to the system_message. -					Problematic(replacement) -				} -			}, -			TitleReference(e) => sub_res(*e, refs).into(), -			Abbreviation(e) => sub_res(*e, refs).into(), -			Acronym(e) => sub_res(*e, refs).into(), -			Superscript(e) => sub_res(*e, refs).into(), -			Subscript(e) => sub_res(*e, refs).into(), -			Inline(e) => sub_res(*e, refs).into(), -			Problematic(e) => sub_res(*e, refs).into(), -			Generated(e) => sub_res(*e, refs).into(), -			Math(e) => Math(e), -			TargetInline(e) => TargetInline(e), -			RawInline(e) => RawInline(e), -			ImageInline(e) => ImageInline(e) -		}] -	} +    fn populate_targets(&self, refs: &mut TargetsCollected) { +        use c::TextOrInlineElement::*; +        match self { +            String(_) => {} +            Emphasis(e) => sub_pop(&**e, refs), +            Strong(e) => sub_pop(&**e, refs), +            Literal(_) => {} +            Reference(e) => sub_pop(&**e, refs), +            FootnoteReference(e) => sub_pop(&**e, refs), +            CitationReference(e) => sub_pop(&**e, refs), +            SubstitutionReference(e) => sub_pop(&**e, refs), +            TitleReference(e) => sub_pop(&**e, refs), +            Abbreviation(e) => sub_pop(&**e, refs), +            Acronym(e) => sub_pop(&**e, refs), +            Superscript(e) => sub_pop(&**e, refs), +            Subscript(e) => sub_pop(&**e, refs), +            Inline(e) => sub_pop(&**e, refs), +            Problematic(e) => sub_pop(&**e, refs), +            Generated(e) => sub_pop(&**e, refs), +            Math(_) => {} +            TargetInline(_) => { +                unimplemented!(); +            } +            RawInline(_) => {} +            ImageInline(_) => {} +        } +    } +    fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> { +        use c::TextOrInlineElement::*; +        vec![match self { +            String(e) => String(e), +            Emphasis(e) => sub_res(*e, refs).into(), +            Strong(e) => sub_res(*e, refs).into(), +            Literal(e) => Literal(e), +            Reference(mut e) => { +                if e.extra().refuri.is_none() { +                    if let Some(uri) = refs.target_url(&e.extra().refname) { +                        e.extra_mut().refuri = Some(uri.clone()); +                    } +                } +                (*e).into() +            } +            FootnoteReference(e) => sub_res(*e, refs).into(), +            CitationReference(e) => sub_res(*e, refs).into(), +            SubstitutionReference(e) => match refs.substitution(&e.extra().refname) { +                Some(Substitution { +                    content, +                    ltrim, +                    rtrim, +                }) => { +                    // (level 3 system message). +                    // TODO: ltrim and rtrim. +                    if *ltrim || *rtrim { +                        dbg!(content, ltrim, rtrim); +                    } +                    return content.clone(); +                } +                None => { +                    // Undefined substitution name (level 3 system message). +                    // TODO: This replaces the reference by a Problematic node. +                    // The corresponding SystemMessage node should go in a generated +                    // section with class "system-messages" at the end of the document. +                    use document_tree::Problematic; +                    let mut replacement: Box<Problematic> = Box::default(); +                    replacement +                        .children_mut() +                        .push(c::TextOrInlineElement::String(Box::new(format!( +                            "|{}|", +                            e.extra().refname[0].0 +                        )))); +                    // TODO: Create an ID for replacement for the system_message to reference. +                    // TODO: replacement.refid pointing to the system_message. +                    Problematic(replacement) +                } +            }, +            TitleReference(e) => sub_res(*e, refs).into(), +            Abbreviation(e) => sub_res(*e, refs).into(), +            Acronym(e) => sub_res(*e, refs).into(), +            Superscript(e) => sub_res(*e, refs).into(), +            Subscript(e) => sub_res(*e, refs).into(), +            Inline(e) => sub_res(*e, refs).into(), +            Problematic(e) => sub_res(*e, refs).into(), +            Generated(e) => sub_res(*e, refs).into(), +            Math(e) => Math(e), +            TargetInline(e) => TargetInline(e), +            RawInline(e) => RawInline(e), +            ImageInline(e) => ImageInline(e), +        }] +    }  }  impl ResolvableRefs for c::AuthorInfo { -	fn populate_targets(&self, refs: &mut TargetsCollected) { -		use c::AuthorInfo::*; -		match self { -			Author(e) => sub_pop(&**e, refs), -			Organization(e) => sub_pop(&**e, refs), -			Address(e) => sub_pop(&**e, refs), -			Contact(e) => sub_pop(&**e, refs), -		} -	} -	fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> { -		use c::AuthorInfo::*; -		vec![match self { -			Author(e) => sub_res(*e, refs).into(), -			Organization(e) => sub_res(*e, refs).into(), -			Address(e) => sub_res(*e, refs).into(), -			Contact(e) => sub_res(*e, refs).into(), -		}] -	} +    fn populate_targets(&self, refs: &mut TargetsCollected) { +        use c::AuthorInfo::*; +        match self { +            Author(e) => sub_pop(&**e, refs), +            Organization(e) => sub_pop(&**e, refs), +            Address(e) => sub_pop(&**e, refs), +            Contact(e) => sub_pop(&**e, refs), +        } +    } +    fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> { +        use c::AuthorInfo::*; +        vec![match self { +            Author(e) => sub_res(*e, refs).into(), +            Organization(e) => sub_res(*e, refs).into(), +            Address(e) => sub_res(*e, refs).into(), +            Contact(e) => sub_res(*e, refs).into(), +        }] +    }  }  impl ResolvableRefs for c::DecorationElement { -	fn populate_targets(&self, refs: &mut TargetsCollected) { -		use c::DecorationElement::*; -		match self { -			Header(e) => sub_pop(&**e, refs), -			Footer(e) => sub_pop(&**e, refs), -		} -	} -	fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> { -		use c::DecorationElement::*; -		vec![match self { -			Header(e) => sub_res(*e, refs).into(), -			Footer(e) => sub_res(*e, refs).into(), -		}] -	} +    fn populate_targets(&self, refs: &mut TargetsCollected) { +        use c::DecorationElement::*; +        match self { +            Header(e) => sub_pop(&**e, refs), +            Footer(e) => sub_pop(&**e, refs), +        } +    } +    fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> { +        use c::DecorationElement::*; +        vec![match self { +            Header(e) => sub_res(*e, refs).into(), +            Footer(e) => sub_res(*e, refs).into(), +        }] +    }  }  impl ResolvableRefs for c::SubTopic { -	fn populate_targets(&self, refs: &mut TargetsCollected) { -		use c::SubTopic::*; -		match self { -			Title(e) => sub_pop(&**e, refs), -			BodyElement(e) => e.populate_targets(refs), -		} -	} -	fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> { -		use c::SubTopic::*; -		match self { -			Title(e) => vec![sub_res(*e, refs).into()], -			BodyElement(e) => e.resolve_refs(refs).drain(..).map(Into::into).collect(), -		} -	} +    fn populate_targets(&self, refs: &mut TargetsCollected) { +        use c::SubTopic::*; +        match self { +            Title(e) => sub_pop(&**e, refs), +            BodyElement(e) => e.populate_targets(refs), +        } +    } +    fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> { +        use c::SubTopic::*; +        match self { +            Title(e) => vec![sub_res(*e, refs).into()], +            BodyElement(e) => e.resolve_refs(refs).drain(..).map(Into::into).collect(), +        } +    }  }  impl ResolvableRefs for c::SubSidebar { -	fn populate_targets(&self, refs: &mut TargetsCollected) { -		use c::SubSidebar::*; -		match self { -			Topic(e) => sub_pop(&**e, refs), -			Title(e) => sub_pop(&**e, refs), -			Subtitle(e) => sub_pop(&**e, refs), -			BodyElement(e) => e.populate_targets(refs), -		} -	} -	fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> { -		use c::SubSidebar::*; -		vec![match self { -			Topic(e) => sub_res(*e, refs).into(), -			Title(e) => sub_res(*e, refs).into(), -			Subtitle(e) => sub_res(*e, refs).into(), -			BodyElement(e) => return e.resolve_refs(refs).drain(..).map(Into::into).collect(), -		}] -	} +    fn populate_targets(&self, refs: &mut TargetsCollected) { +        use c::SubSidebar::*; +        match self { +            Topic(e) => sub_pop(&**e, refs), +            Title(e) => sub_pop(&**e, refs), +            Subtitle(e) => sub_pop(&**e, refs), +            BodyElement(e) => e.populate_targets(refs), +        } +    } +    fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> { +        use c::SubSidebar::*; +        vec![match self { +            Topic(e) => sub_res(*e, refs).into(), +            Title(e) => sub_res(*e, refs).into(), +            Subtitle(e) => sub_res(*e, refs).into(), +            BodyElement(e) => return e.resolve_refs(refs).drain(..).map(Into::into).collect(), +        }] +    }  }  impl ResolvableRefs for c::SubDLItem { -	fn populate_targets(&self, refs: &mut TargetsCollected) { -		use c::SubDLItem::*; -		match self { -			Term(e) => sub_pop(&**e, refs), -			Classifier(e) => sub_pop(&**e, refs), -			Definition(e) => sub_pop(&**e, refs), -		} -	} -	fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> { -		use c::SubDLItem::*; -		vec![match self { -			Term(e) => sub_res(*e, refs).into(), -			Classifier(e) => sub_res(*e, refs).into(), -			Definition(e) => sub_res(*e, refs).into(), -		}] -	} +    fn populate_targets(&self, refs: &mut TargetsCollected) { +        use c::SubDLItem::*; +        match self { +            Term(e) => sub_pop(&**e, refs), +            Classifier(e) => sub_pop(&**e, refs), +            Definition(e) => sub_pop(&**e, refs), +        } +    } +    fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> { +        use c::SubDLItem::*; +        vec![match self { +            Term(e) => sub_res(*e, refs).into(), +            Classifier(e) => sub_res(*e, refs).into(), +            Definition(e) => sub_res(*e, refs).into(), +        }] +    }  }  impl ResolvableRefs for c::SubField { -	fn populate_targets(&self, refs: &mut TargetsCollected) { -		use c::SubField::*; -		match self { -			FieldName(e) => sub_pop(&**e, refs), -			FieldBody(e) => sub_pop(&**e, refs), -		} -	} -	fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> { -		use c::SubField::*; -		vec![match self { -			FieldName(e) => sub_res(*e, refs).into(), -			FieldBody(e) => sub_res(*e, refs).into(), -		}] -	} +    fn populate_targets(&self, refs: &mut TargetsCollected) { +        use c::SubField::*; +        match self { +            FieldName(e) => sub_pop(&**e, refs), +            FieldBody(e) => sub_pop(&**e, refs), +        } +    } +    fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> { +        use c::SubField::*; +        vec![match self { +            FieldName(e) => sub_res(*e, refs).into(), +            FieldBody(e) => sub_res(*e, refs).into(), +        }] +    }  }  impl ResolvableRefs for c::SubOptionListItem { -	fn populate_targets(&self, refs: &mut TargetsCollected) { -		use c::SubOptionListItem::*; -		match self { -			OptionGroup(e) => sub_sub_pop(&**e, refs), -			Description(e) => sub_pop(&**e, refs), -		} -	} -	fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> { -		use c::SubOptionListItem::*; -		vec![match self { -			OptionGroup(e) => sub_sub_res(*e, refs).into(), -			Description(e) => sub_res(*e, refs).into(), -		}] -	} +    fn populate_targets(&self, refs: &mut TargetsCollected) { +        use c::SubOptionListItem::*; +        match self { +            OptionGroup(e) => sub_sub_pop(&**e, refs), +            Description(e) => sub_pop(&**e, refs), +        } +    } +    fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> { +        use c::SubOptionListItem::*; +        vec![match self { +            OptionGroup(e) => sub_sub_res(*e, refs).into(), +            Description(e) => sub_res(*e, refs).into(), +        }] +    }  }  impl ResolvableRefs for c::SubOption { -	fn populate_targets(&self, _: &mut TargetsCollected) {} -	fn resolve_refs(self, _: &TargetsCollected) -> Vec<Self> { vec![self] } +    fn populate_targets(&self, _: &mut TargetsCollected) {} +    fn resolve_refs(self, _: &TargetsCollected) -> Vec<Self> { +        vec![self] +    }  }  impl ResolvableRefs for c::SubLineBlock { -	fn populate_targets(&self, refs: &mut TargetsCollected) { -		use c::SubLineBlock::*; -		match self { -			LineBlock(e) => sub_pop(&**e, refs), -			Line(e) => sub_pop(&**e, refs), -		} -	} -	fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> { -		use c::SubLineBlock::*; -		vec![match self { -			LineBlock(e) => sub_res(*e, refs).into(), -			Line(e) => sub_res(*e, refs).into(), -		}] -	} +    fn populate_targets(&self, refs: &mut TargetsCollected) { +        use c::SubLineBlock::*; +        match self { +            LineBlock(e) => sub_pop(&**e, refs), +            Line(e) => sub_pop(&**e, refs), +        } +    } +    fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> { +        use c::SubLineBlock::*; +        vec![match self { +            LineBlock(e) => sub_res(*e, refs).into(), +            Line(e) => sub_res(*e, refs).into(), +        }] +    }  }  impl ResolvableRefs for c::SubBlockQuote { -	fn populate_targets(&self, refs: &mut TargetsCollected) { -		use c::SubBlockQuote::*; -		match self { -			Attribution(e) => sub_pop(&**e, refs), -			BodyElement(e) => e.populate_targets(refs), -		} -	} -	fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> { -		use c::SubBlockQuote::*; -		match self { -			Attribution(e) => vec![sub_res(*e, refs).into()], -			BodyElement(e) => e.resolve_refs(refs).drain(..).map(Into::into).collect(), -		} -	} +    fn populate_targets(&self, refs: &mut TargetsCollected) { +        use c::SubBlockQuote::*; +        match self { +            Attribution(e) => sub_pop(&**e, refs), +            BodyElement(e) => e.populate_targets(refs), +        } +    } +    fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> { +        use c::SubBlockQuote::*; +        match self { +            Attribution(e) => vec![sub_res(*e, refs).into()], +            BodyElement(e) => e.resolve_refs(refs).drain(..).map(Into::into).collect(), +        } +    }  }  impl ResolvableRefs for c::SubFootnote { -	fn populate_targets(&self, refs: &mut TargetsCollected) { -		use c::SubFootnote::*; -		match self { -			Label(e) => sub_pop(&**e, refs), -			BodyElement(e) => e.populate_targets(refs), -		} -	} -	fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> { -		use c::SubFootnote::*; -		match self { -			Label(e) => vec![sub_res(*e, refs).into()], -			BodyElement(e) => e.resolve_refs(refs).drain(..).map(Into::into).collect(), -		} -	} +    fn populate_targets(&self, refs: &mut TargetsCollected) { +        use c::SubFootnote::*; +        match self { +            Label(e) => sub_pop(&**e, refs), +            BodyElement(e) => e.populate_targets(refs), +        } +    } +    fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> { +        use c::SubFootnote::*; +        match self { +            Label(e) => vec![sub_res(*e, refs).into()], +            BodyElement(e) => e.resolve_refs(refs).drain(..).map(Into::into).collect(), +        } +    }  }  impl ResolvableRefs for c::SubFigure { -	fn populate_targets(&self, refs: &mut TargetsCollected) { -		use c::SubFigure::*; -		match self { -			Caption(e) => sub_pop(&**e, refs), -			Legend(e) => sub_pop(&**e, refs), -			BodyElement(e) => e.populate_targets(refs), -		} -	} -	fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> { -		use c::SubFigure::*; -		vec![match self { -			Caption(e) => sub_res(*e, refs).into(), -			Legend(e) => sub_res(*e, refs).into(), -			BodyElement(e) => return e.resolve_refs(refs).drain(..).map(Into::into).collect(), -		}] -	} +    fn populate_targets(&self, refs: &mut TargetsCollected) { +        use c::SubFigure::*; +        match self { +            Caption(e) => sub_pop(&**e, refs), +            Legend(e) => sub_pop(&**e, refs), +            BodyElement(e) => e.populate_targets(refs), +        } +    } +    fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> { +        use c::SubFigure::*; +        vec![match self { +            Caption(e) => sub_res(*e, refs).into(), +            Legend(e) => sub_res(*e, refs).into(), +            BodyElement(e) => return e.resolve_refs(refs).drain(..).map(Into::into).collect(), +        }] +    }  }  impl ResolvableRefs for c::SubTable { -	fn populate_targets(&self, refs: &mut TargetsCollected) { -		use c::SubTable::*; -		match self { -			Title(e) => sub_pop(&**e, refs), -			TableGroup(e) => sub_pop(&**e, refs), -		} -	} -	fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> { -		use c::SubTable::*; -		vec![match self { -			Title(e) => sub_res(*e, refs).into(), -			TableGroup(e) => sub_res(*e, refs).into(), -		}] -	} +    fn populate_targets(&self, refs: &mut TargetsCollected) { +        use c::SubTable::*; +        match self { +            Title(e) => sub_pop(&**e, refs), +            TableGroup(e) => sub_pop(&**e, refs), +        } +    } +    fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> { +        use c::SubTable::*; +        vec![match self { +            Title(e) => sub_res(*e, refs).into(), +            TableGroup(e) => sub_res(*e, refs).into(), +        }] +    }  }  impl ResolvableRefs for c::SubTableGroup { -	fn populate_targets(&self, refs: &mut TargetsCollected) { -		use c::SubTableGroup::*; -		match self { -			TableColspec(_) => { -				unimplemented!(); -			}, -			TableHead(e) => { -				for c in e.children() { -					sub_sub_pop(c, refs); -				} -			}, -			TableBody(e) => { -				for c in e.children() { -					sub_sub_pop(c, refs); -				} -			}, -		} -	} -	fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> { -		use c::SubTableGroup::*; -		vec![match self { -			TableColspec(e) => TableColspec(e), -			TableHead(mut e) => { -				let new: Vec<_> = e.children_mut().drain(..).map(|c| sub_sub_res(c, refs)).collect(); -				e.children_mut().extend(new); -				TableHead(e) -			}, -			TableBody(mut e) => { -				let new: Vec<_> = e.children_mut().drain(..).map(|c| sub_sub_res(c, refs)).collect(); -				e.children_mut().extend(new); -				TableBody(e) -			}, -		}] -	} +    fn populate_targets(&self, refs: &mut TargetsCollected) { +        use c::SubTableGroup::*; +        match self { +            TableColspec(_) => { +                unimplemented!(); +            } +            TableHead(e) => { +                for c in e.children() { +                    sub_sub_pop(c, refs); +                } +            } +            TableBody(e) => { +                for c in e.children() { +                    sub_sub_pop(c, refs); +                } +            } +        } +    } +    fn resolve_refs(self, refs: &TargetsCollected) -> Vec<Self> { +        use c::SubTableGroup::*; +        vec![match self { +            TableColspec(e) => TableColspec(e), +            TableHead(mut e) => { +                let new: Vec<_> = e +                    .children_mut() +                    .drain(..) +                    .map(|c| sub_sub_res(c, refs)) +                    .collect(); +                e.children_mut().extend(new); +                TableHead(e) +            } +            TableBody(mut e) => { +                let new: Vec<_> = e +                    .children_mut() +                    .drain(..) +                    .map(|c| sub_sub_res(c, refs)) +                    .collect(); +                e.children_mut().extend(new); +                TableBody(e) +            } +        }] +    }  } diff --git a/parser/src/tests.rs b/parser/src/tests.rs index ffe0ee7..b5dfe6a 100644 --- a/parser/src/tests.rs +++ b/parser/src/tests.rs @@ -5,103 +5,103 @@ use crate::pest_rst::{RstParser, Rule};  #[test]  fn plain() { -	parses_to! { -		parser: RstParser, -		input: "line\n", -		rule: Rule::paragraph, -		tokens: [ -			paragraph(0, 4, [ -				str(0, 4) -			]) -		] -	}; +    parses_to! { +        parser: RstParser, +        input: "line\n", +        rule: Rule::paragraph, +        tokens: [ +            paragraph(0, 4, [ +                str(0, 4) +            ]) +        ] +    };  }  #[test]  fn emph_only() { -	parses_to! { -		parser: RstParser, -		input: "*emphasis*", -		rule: Rule::emph_outer, -		tokens: [ -			emph(1, 9, [str_nested(1, 9)]) -		] -	}; +    parses_to! { +        parser: RstParser, +        input: "*emphasis*", +        rule: Rule::emph_outer, +        tokens: [ +            emph(1, 9, [str_nested(1, 9)]) +        ] +    };  }  #[test]  fn emph() { -	parses_to! { -		parser: RstParser, -		input: "line *with markup*\n", -		rule: Rule::paragraph, -		tokens: [ -			paragraph(0, 18, [ -				str(0, 5), -				emph(6, 17, [str_nested(6, 17)]), -			]) -		] -	}; +    parses_to! { +        parser: RstParser, +        input: "line *with markup*\n", +        rule: Rule::paragraph, +        tokens: [ +            paragraph(0, 18, [ +                str(0, 5), +                emph(6, 17, [str_nested(6, 17)]), +            ]) +        ] +    };  }  #[test]  fn title() { -	parses_to! { -		parser: RstParser, -		input: "\ +    parses_to! { +        parser: RstParser, +        input: "\  Title  =====  ", -		rule: Rule::title, -		tokens: [ -			title(0, 12, [ title_single(0, 12, [ -				line(0, 6, [ str(0, 5) ]), -				adornments(6, 11), -			]) ]) -		] -	}; +        rule: Rule::title, +        tokens: [ +            title(0, 12, [ title_single(0, 12, [ +                line(0, 6, [ str(0, 5) ]), +                adornments(6, 11), +            ]) ]) +        ] +    };  }  #[test]  fn title_overline() { -	parses_to! { -		parser: RstParser, -		input: "\ +    parses_to! { +        parser: RstParser, +        input: "\  -----  Title  -----  ", -		rule: Rule::title, -		tokens: [ -			title(0, 17, [ title_double(0, 17, [ -				adornments(0, 5), -				line(6, 12, [ str(6, 11) ]), -			]) ]) -		] -	}; +        rule: Rule::title, +        tokens: [ +            title(0, 17, [ title_double(0, 17, [ +                adornments(0, 5), +                line(6, 12, [ str(6, 11) ]), +            ]) ]) +        ] +    };  }  #[allow(clippy::cognitive_complexity)]  #[test]  fn two_targets() { -	parses_to! { -		parser: RstParser, -		input: "\ +    parses_to! { +        parser: RstParser, +        input: "\  .. _a: http://example.com  .. _`b_`: https://example.org  ", -		rule: Rule::document, -		tokens: [ -			target(0, 26, [ -				target_name_uq(4, 5), -				link_target(7, 25), -			]), -			target(26, 56, [ -				target_name_qu(31, 33), -				link_target(36, 55), -			]), -		] -	}; +        rule: Rule::document, +        tokens: [ +            target(0, 26, [ +                target_name_uq(4, 5), +                link_target(7, 25), +            ]), +            target(26, 56, [ +                target_name_qu(31, 33), +                link_target(36, 55), +            ]), +        ] +    };  }  #[test] @@ -119,9 +119,9 @@ fn inline_code_literal_with_underscore() {  #[allow(clippy::cognitive_complexity)]  #[test]  fn admonitions() { -	parses_to! { -		parser: RstParser, -		input: "\ +    parses_to! { +        parser: RstParser, +        input: "\  .. note::     Just next line  .. admonition:: In line title @@ -130,30 +130,30 @@ fn admonitions() {  .. danger:: Just this line  ", -		rule: Rule::document, -		tokens: [ -			admonition_gen(0, 27, [ -				admonition_type(3, 7), -				paragraph(13, 27, [ str(13, 27) ]), -			]), -			admonition(28, 71, [ -				line(43, 58, [ str(43, 57) ]), -				paragraph(62, 71, [ str(62, 71) ]), -			]), -			admonition_gen(73, 100, [ -				admonition_type(76, 82), -				line(84, 100, [ str(84, 99) ]), -			]), -		] -	}; +        rule: Rule::document, +        tokens: [ +            admonition_gen(0, 27, [ +                admonition_type(3, 7), +                paragraph(13, 27, [ str(13, 27) ]), +            ]), +            admonition(28, 71, [ +                line(43, 58, [ str(43, 57) ]), +                paragraph(62, 71, [ str(62, 71) ]), +            ]), +            admonition_gen(73, 100, [ +                admonition_type(76, 82), +                line(84, 100, [ str(84, 99) ]), +            ]), +        ] +    };  }  #[allow(clippy::cognitive_complexity)]  #[test]  fn literal_block() { -	parses_to! { -		parser: RstParser, -		input: "\ +    parses_to! { +        parser: RstParser, +        input: "\  ::     print('x') @@ -162,26 +162,26 @@ fn literal_block() {  The end  ", -		rule: Rule::document, -		tokens: [ -			literal_block(0, 36, [ -				literal_lines(7, 36, [ -					literal_line(7, 18), -					literal_line_blank(18, 19), -					literal_line(22, 36), -				]), -			]), -			paragraph(37, 44, [ str(37, 44) ]), -		] -	}; +        rule: Rule::document, +        tokens: [ +            literal_block(0, 36, [ +                literal_lines(7, 36, [ +                    literal_line(7, 18), +                    literal_line_blank(18, 19), +                    literal_line(22, 36), +                ]), +            ]), +            paragraph(37, 44, [ str(37, 44) ]), +        ] +    };  }  #[allow(clippy::cognitive_complexity)]  #[test]  fn code_directive() { -	parses_to! { -		parser: RstParser, -		input: "\ +    parses_to! { +        parser: RstParser, +        input: "\  .. code::     Single line @@ -194,30 +194,30 @@ fn code_directive() {  The end  ", -		rule: Rule::document, -		tokens: [ -			code_directive(0, 26, [ -				literal_lines(14, 26, [ literal_line(14, 26) ]), -			]), -			code_directive(27, 83, [ -				source(43, 49), -				literal_lines(54, 83, [ -					literal_line(54, 65), -					literal_line_blank(65, 66), -					literal_line(69, 83), -				]), -			]), -			paragraph(84, 91, [ str(84, 91) ]), -		] -	}; +        rule: Rule::document, +        tokens: [ +            code_directive(0, 26, [ +                literal_lines(14, 26, [ literal_line(14, 26) ]), +            ]), +            code_directive(27, 83, [ +                source(43, 49), +                literal_lines(54, 83, [ +                    literal_line(54, 65), +                    literal_line_blank(65, 66), +                    literal_line(69, 83), +                ]), +            ]), +            paragraph(84, 91, [ str(84, 91) ]), +        ] +    };  }  #[allow(clippy::cognitive_complexity)]  #[test]  fn raw() { -	parses_to! { -		parser: RstParser, -		input: "\ +    parses_to! { +        parser: RstParser, +        input: "\  .. raw:: html     hello <span>world</span> @@ -230,33 +230,33 @@ fn raw() {  The end  ", -		rule: Rule::document, -		tokens: [ -			raw_directive(0, 43, [ -				raw_output_format(9, 13), -				raw_block(18, 43, [ -					raw_line(18, 43), -				]), -			]), -			raw_directive(44, 100, [ -				raw_output_format(53, 57), -				raw_block(62, 100, [ -					raw_line(62, 79), -					raw_line_blank(79, 80), -					raw_line(83, 100), -				]), -			]), -			paragraph(101, 108, [ str(101, 108) ]), -		] -	}; +        rule: Rule::document, +        tokens: [ +            raw_directive(0, 43, [ +                raw_output_format(9, 13), +                raw_block(18, 43, [ +                    raw_line(18, 43), +                ]), +            ]), +            raw_directive(44, 100, [ +                raw_output_format(53, 57), +                raw_block(62, 100, [ +                    raw_line(62, 79), +                    raw_line_blank(79, 80), +                    raw_line(83, 100), +                ]), +            ]), +            paragraph(101, 108, [ str(101, 108) ]), +        ] +    };  }  #[allow(clippy::cognitive_complexity)]  #[test]  fn comments() { -	parses_to! { -		parser: RstParser, -		input: "\ +    parses_to! { +        parser: RstParser, +        input: "\  .. This is a comment  .. @@ -280,118 +280,117 @@ fn comments() {  .. Comments can also be     run-in like this  ", -		rule: Rule::document, -		tokens: [ -			block_comment(0, 22, [ -				comment_line(3, 21), -			]), -			block_comment(22, 43, [ -				comment_line(28, 42), -			]), -			block_comment(43, 63, [ -				comment_line(49, 63), -			]), -			block_comment(63, 81, [ -				comment_line(69, 81), -			]), -			block_comment(81, 99, [ -				comment_line(87, 99), -			]), -			block_comment(99, 121, [ -				comment_line(105, 121), -			]), -			block_comment(121, 216, [ -				comment_line(124, 139), -				comment_line_blank(139, 140), -				comment_line(143, 163), -				comment_line(166, 195), -				comment_line_blank(195, 196), -				comment_line(199, 216), -			]), -			block_comment(216, 219), -			block_comment(219, 263, [ -				comment_line(222, 243), -				comment_line(246, 263), -			]), -		] -	}; +        rule: Rule::document, +        tokens: [ +            block_comment(0, 22, [ +                comment_line(3, 21), +            ]), +            block_comment(22, 43, [ +                comment_line(28, 42), +            ]), +            block_comment(43, 63, [ +                comment_line(49, 63), +            ]), +            block_comment(63, 81, [ +                comment_line(69, 81), +            ]), +            block_comment(81, 99, [ +                comment_line(87, 99), +            ]), +            block_comment(99, 121, [ +                comment_line(105, 121), +            ]), +            block_comment(121, 216, [ +                comment_line(124, 139), +                comment_line_blank(139, 140), +                comment_line(143, 163), +                comment_line(166, 195), +                comment_line_blank(195, 196), +                comment_line(199, 216), +            ]), +            block_comment(216, 219), +            block_comment(219, 263, [ +                comment_line(222, 243), +                comment_line(246, 263), +            ]), +        ] +    };  }  #[allow(clippy::cognitive_complexity)]  #[test]  fn substitutions() { -	parses_to! { -		parser: RstParser, -		input: "\ +    parses_to! { +        parser: RstParser, +        input: "\  A |subst| in-line  .. |subst| replace:: substitution  .. |subst2| replace:: it can also     be hanging  ", -		rule: Rule::document, -		tokens: [ -			paragraph(0, 17, [ -				str(0, 2), -				substitution_name(3, 8), -				str(9, 17), -			]), -			substitution_def(19, 52, [ -				substitution_name(23, 28), -				replace(30, 52, [ paragraph(40, 52, [str(40, 52)]) ]), -			]), -			substitution_def(53, 101, [ -				substitution_name(57, 63), -				replace(65, 101, [ paragraph(75, 101, [ -					str(75, 86), ws_newline(86, 87), -					str(88, 100), -				]) ]), -			]), -		] -	}; +        rule: Rule::document, +        tokens: [ +            paragraph(0, 17, [ +                str(0, 2), +                substitution_name(3, 8), +                str(9, 17), +            ]), +            substitution_def(19, 52, [ +                substitution_name(23, 28), +                replace(30, 52, [ paragraph(40, 52, [str(40, 52)]) ]), +            ]), +            substitution_def(53, 101, [ +                substitution_name(57, 63), +                replace(65, 101, [ paragraph(75, 101, [ +                    str(75, 86), ws_newline(86, 87), +                    str(88, 100), +                ]) ]), +            ]), +        ] +    };  }  #[test]  fn substitution_in_literal() { -	parses_to! { -		parser: RstParser, -		input: "Just ``|code|``, really ``*code* |only|``", -		rule: Rule::document, -		tokens: [ -			paragraph(0, 41, [ -				str(0, 5), -				literal(7, 13), -				str(15, 24), -				literal(26, 39), -			]), -		] -	}; +    parses_to! { +        parser: RstParser, +        input: "Just ``|code|``, really ``*code* |only|``", +        rule: Rule::document, +        tokens: [ +            paragraph(0, 41, [ +                str(0, 5), +                literal(7, 13), +                str(15, 24), +                literal(26, 39), +            ]), +        ] +    };  } -  #[allow(clippy::cognitive_complexity)]  #[test]  fn substitution_image() { -	parses_to! { -		parser: RstParser, -		input: "\ +    parses_to! { +        parser: RstParser, +        input: "\  .. |subst| image:: thing.png     :target: foo.html  ", -		rule: Rule::document, -		tokens: [ -			substitution_def(0, 50, [ -				substitution_name(4, 9), -				image(11, 50, [ -					line(18, 29, [ str(18, 28) ]), -					image_option(32, 50, [ -						image_opt_name(33, 39), -						line(40, 50, [ str(40, 49) ]), -					]), -				]), -			]), -		] -	}; +        rule: Rule::document, +        tokens: [ +            substitution_def(0, 50, [ +                substitution_name(4, 9), +                image(11, 50, [ +                    line(18, 29, [ str(18, 28) ]), +                    image_option(32, 50, [ +                        image_opt_name(33, 39), +                        line(40, 50, [ str(40, 49) ]), +                    ]), +                ]), +            ]), +        ] +    };  }  // TODO: test images @@ -399,9 +398,9 @@ fn substitution_image() {  #[allow(clippy::cognitive_complexity)]  #[test]  fn nested_lists() { -	parses_to! { -		parser: RstParser, -		input: "\ +    parses_to! { +        parser: RstParser, +        input: "\  paragraph  -  item 1 @@ -413,27 +412,27 @@ paragraph     - nested item 2     - nested item 3  ", -		rule: Rule::document, -		tokens: [ -			paragraph(0, 9, [ str(0, 9) ]), -			bullet_list(11, 131, [ -				bullet_item(11, 21, [ -					line(14, 21, [ str(14, 20) ]), -				]), -				bullet_item(21, 131, [ -					line(24, 31, [ str(24, 30) ]), -					paragraph(34, 74, [ -						str(34, 43), ws_newline(43, 44), -						str(47, 58), ws_newline(58, 59), -						str(62, 73), -					]), -					bullet_list(77, 131, [ -						bullet_item( 77,  93, [ line( 79,  93, [str( 79,  92)]) ]), -						bullet_item( 96, 112, [ line( 98, 112, [str( 98, 111)]) ]), -						bullet_item(115, 131, [ line(117, 131, [str(117, 130)]) ]), -					]), -				]), -			]), -		] -	} +        rule: Rule::document, +        tokens: [ +            paragraph(0, 9, [ str(0, 9) ]), +            bullet_list(11, 131, [ +                bullet_item(11, 21, [ +                    line(14, 21, [ str(14, 20) ]), +                ]), +                bullet_item(21, 131, [ +                    line(24, 31, [ str(24, 30) ]), +                    paragraph(34, 74, [ +                        str(34, 43), ws_newline(43, 44), +                        str(47, 58), ws_newline(58, 59), +                        str(62, 73), +                    ]), +                    bullet_list(77, 131, [ +                        bullet_item( 77,  93, [ line( 79,  93, [str( 79,  92)]) ]), +                        bullet_item( 96, 112, [ line( 98, 112, [str( 98, 111)]) ]), +                        bullet_item(115, 131, [ line(117, 131, [str(117, 130)]) ]), +                    ]), +                ]), +            ]), +        ] +    }  } diff --git a/parser/src/token.rs b/parser/src/token.rs index b3b7bac..1b1d49d 100644 --- a/parser/src/token.rs +++ b/parser/src/token.rs @@ -1,16 +1,68 @@  //http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#bullet-lists  // *, +, -, •, ‣, ⁃ -pub enum BulletListType { Ast, Plus, Minus, Bullet, TriBullet, HyphenBullet } +pub enum BulletListType { +    Ast, +    Plus, +    Minus, +    Bullet, +    TriBullet, +    HyphenBullet, +}  // 1, A, a, I, i -pub enum EnumListChar { Arabic, AlphaUpper, AlphaLower, RomanUpper, RomanLower, Auto } +pub enum EnumListChar { +    Arabic, +    AlphaUpper, +    AlphaLower, +    RomanUpper, +    RomanLower, +    Auto, +}  // 1., (1), 1) -pub enum EnumListType { Period, ParenEnclosed, Paren } +pub enum EnumListType { +    Period, +    ParenEnclosed, +    Paren, +}  // ! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~  pub enum AdornmentChar { -	Bang, DQuote, Hash, Dollar, Percent, Amp, SQuote, LParen, RParen, Ast, Plus, Comma, -	Minus, Period, Slash, Colon, Semicolon, Less, Eq, More, Question, At, LBrack, -	Backslash, RBrack, Caret, Underscore, Backtick, LBrace, Pipe, RBrace, Tilde, +    Bang, +    DQuote, +    Hash, +    Dollar, +    Percent, +    Amp, +    SQuote, +    LParen, +    RParen, +    Ast, +    Plus, +    Comma, +    Minus, +    Period, +    Slash, +    Colon, +    Semicolon, +    Less, +    Eq, +    More, +    Question, +    At, +    LBrack, +    Backslash, +    RBrack, +    Caret, +    Underscore, +    Backtick, +    LBrace, +    Pipe, +    RBrace, +    Tilde,  }  // [1], [#], [*], [#foo] -pub enum FootnoteType { Numbered(usize), AutoNumber, AutoSymbol, AutoNamed(String) } +pub enum FootnoteType { +    Numbered(usize), +    AutoNumber, +    AutoSymbol, +    AutoNamed(String), +} diff --git a/renderer/src/html.rs b/renderer/src/html.rs index 4244349..feef29d 100644 --- a/renderer/src/html.rs +++ b/renderer/src/html.rs @@ -7,406 +7,601 @@ use failure::Error;  // use crate::url::Url;  use document_tree::{ -	Document,Element,HasChildren,ExtraAttributes, -	elements as e, -	extra_attributes as a, -	element_categories as c, -	attribute_types as at, +    attribute_types as at, element_categories as c, elements as e, extra_attributes as a, Document, +    Element, ExtraAttributes, HasChildren,  }; -  // static FOOTNOTE_SYMBOLS: [char; 10] = ['*', '†', '‡', '§', '¶', '#', '♠', '♥', '♦', '♣']; -pub fn render_html<W>(document: &Document, stream: W, standalone: bool) -> Result<(), Error> where W: Write { -	let mut renderer = HTMLRenderer { stream, level: 0 }; -	if standalone { -		document.render_html(&mut renderer) -	} else { -		for c in document.children() { -			(*c).render_html(&mut renderer)?; -			writeln!(renderer.stream)?; -		} -		Ok(()) -	} +pub fn render_html<W>(document: &Document, stream: W, standalone: bool) -> Result<(), Error> +where +    W: Write, +{ +    let mut renderer = HTMLRenderer { stream, level: 0 }; +    if standalone { +        document.render_html(&mut renderer) +    } else { +        for c in document.children() { +            (*c).render_html(&mut renderer)?; +            writeln!(renderer.stream)?; +        } +        Ok(()) +    }  }  fn escape_html(text: &str) -> String { -	text.replace('&', "&") -		.replace('<', "<") -		.replace('>', ">") -		.replace('"', """) +    text.replace('&', "&") +        .replace('<', "<") +        .replace('>', ">") +        .replace('"', """)  } -struct HTMLRenderer<W> where W: Write { -	stream: W, -	level: u8, +struct HTMLRenderer<W> +where +    W: Write, +{ +    stream: W, +    level: u8,  }  trait HTMLRender { -	fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write; +    fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> +    where +        W: Write;  }  macro_rules! impl_html_render_cat {($cat:ident { $($member:ident),+ }) => { -	impl HTMLRender for c::$cat { -		fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { -			match self {$( -				c::$cat::$member(elem) => (**elem).render_html(renderer), -			)+} -		} -	} +    impl HTMLRender for c::$cat { +        fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { +            match self {$( +                c::$cat::$member(elem) => (**elem).render_html(renderer), +            )+} +        } +    }  }}  macro_rules! impl_html_render_simple { -	( -		$type1:ident => $tag1:ident $( [$($post1:tt)+] )?, -		$( $type:ident => $tag:ident $( [$($post:tt)+] )? ),+ -	) => { -		impl_html_render_simple!($type1 => $tag1 $([$($post1)+])?); -		$( impl_html_render_simple!($type => $tag $([$($post)+])?); )+ -	}; -	( $type:ident => $tag:ident ) => { -		impl_html_render_simple!($type => $tag[""]); -	}; -	( $type:ident => $tag:ident [ $post:expr ] ) => { -		impl HTMLRender for e::$type { -			fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { -				let multiple_children = self.children().len() > 1; -				write!(renderer.stream, "<{}", stringify!($tag))?; -				if self.classes().len() > 0 { -					write!(renderer.stream, " class=\"{}\"", self.classes().join(" "))?; -				} -				write!(renderer.stream, ">")?; -				if multiple_children { write!(renderer.stream, $post)?; } -				for c in self.children() { -					(*c).render_html(renderer)?; -					if multiple_children { write!(renderer.stream, $post)?; } -				} -				write!(renderer.stream, "</{}>", stringify!($tag))?; -				Ok(()) -			} -		} -	}; +    ( +        $type1:ident => $tag1:ident $( [$($post1:tt)+] )?, +        $( $type:ident => $tag:ident $( [$($post:tt)+] )? ),+ +    ) => { +        impl_html_render_simple!($type1 => $tag1 $([$($post1)+])?); +        $( impl_html_render_simple!($type => $tag $([$($post)+])?); )+ +    }; +    ( $type:ident => $tag:ident ) => { +        impl_html_render_simple!($type => $tag[""]); +    }; +    ( $type:ident => $tag:ident [ $post:expr ] ) => { +        impl HTMLRender for e::$type { +            fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { +                let multiple_children = self.children().len() > 1; +                write!(renderer.stream, "<{}", stringify!($tag))?; +                if self.classes().len() > 0 { +                    write!(renderer.stream, " class=\"{}\"", self.classes().join(" "))?; +                } +                write!(renderer.stream, ">")?; +                if multiple_children { write!(renderer.stream, $post)?; } +                for c in self.children() { +                    (*c).render_html(renderer)?; +                    if multiple_children { write!(renderer.stream, $post)?; } +                } +                write!(renderer.stream, "</{}>", stringify!($tag))?; +                Ok(()) +            } +        } +    };  }  macro_rules! impl_html_render_simple_nochildren {( $($type:ident => $tag:ident),+ ) => { $( -	impl HTMLRender for e::$type { -		fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { -			write!(renderer.stream, "<{0}></{0}>", stringify!($tag))?; -			Ok(()) -		} -	} +    impl HTMLRender for e::$type { +        fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { +            write!(renderer.stream, "<{0}></{0}>", stringify!($tag))?; +            Ok(()) +        } +    }  )+ }}  // Impl  impl HTMLRender for Document { -	fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { -		writeln!(renderer.stream, "<!doctype html><html>")?; -		for c in self.children() { -			(*c).render_html(renderer)?; -			writeln!(renderer.stream)?; -		} -		writeln!(renderer.stream, "</html>")?; -		Ok(()) -	} -} - -impl_html_render_cat!(StructuralSubElement { Title, Subtitle, Decoration, Docinfo, SubStructure }); +    fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> +    where +        W: Write, +    { +        writeln!(renderer.stream, "<!doctype html><html>")?; +        for c in self.children() { +            (*c).render_html(renderer)?; +            writeln!(renderer.stream)?; +        } +        writeln!(renderer.stream, "</html>")?; +        Ok(()) +    } +} + +impl_html_render_cat!(StructuralSubElement { +    Title, +    Subtitle, +    Decoration, +    Docinfo, +    SubStructure +});  impl_html_render_simple!(Subtitle => h2);  impl HTMLRender for e::Title { -	fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { -		let level = if renderer.level > 6 { 6 } else { renderer.level }; -		write!(renderer.stream, "<h{0}>", level)?; -		for c in self.children() { -			(*c).render_html(renderer)?; -		} -		write!(renderer.stream, "</h{0}>", level)?; -		Ok(()) -	} +    fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> +    where +        W: Write, +    { +        let level = if renderer.level > 6 { +            6 +        } else { +            renderer.level +        }; +        write!(renderer.stream, "<h{0}>", level)?; +        for c in self.children() { +            (*c).render_html(renderer)?; +        } +        write!(renderer.stream, "</h{0}>", level)?; +        Ok(()) +    }  }  impl HTMLRender for e::Docinfo { -	fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { -		// Like “YAML frontmatter” in Markdown -		unimplemented!(); -	} +    fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> +    where +        W: Write, +    { +        // Like “YAML frontmatter” in Markdown +        unimplemented!(); +    }  }  impl HTMLRender for e::Decoration { -	fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { -		// Header or footer -		unimplemented!(); -	} -} - -impl_html_render_cat!(SubStructure { Topic, Sidebar, Transition, Section, BodyElement }); +    fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> +    where +        W: Write, +    { +        // Header or footer +        unimplemented!(); +    } +} + +impl_html_render_cat!(SubStructure { +    Topic, +    Sidebar, +    Transition, +    Section, +    BodyElement +});  impl_html_render_simple!(Sidebar => aside);  impl HTMLRender for e::Section { -	fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { -		renderer.level += 1; -		writeln!(renderer.stream, "<section id=\"{0}\">", self.ids()[0].0)?; -		for c in self.children() { -			(*c).render_html(renderer)?; -			writeln!(renderer.stream)?; -		} -		write!(renderer.stream, "</section>")?; -		renderer.level -= 1; -		Ok(()) -	} +    fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> +    where +        W: Write, +    { +        renderer.level += 1; +        writeln!(renderer.stream, "<section id=\"{0}\">", self.ids()[0].0)?; +        for c in self.children() { +            (*c).render_html(renderer)?; +            writeln!(renderer.stream)?; +        } +        write!(renderer.stream, "</section>")?; +        renderer.level -= 1; +        Ok(()) +    }  }  impl HTMLRender for e::Transition { -	fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { -		write!(renderer.stream, "<hr/>")?; -		Ok(()) -	} +    fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> +    where +        W: Write, +    { +        write!(renderer.stream, "<hr/>")?; +        Ok(()) +    }  }  impl HTMLRender for e::Topic { -	fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { -		// A mini section with title -		unimplemented!(); -	} -} - -impl_html_render_cat!(BodyElement { Paragraph, LiteralBlock, DoctestBlock, MathBlock, Rubric, SubstitutionDefinition, Comment, Pending, Target, Raw, Image, Compound, Container, BulletList, EnumeratedList, DefinitionList, FieldList, OptionList, LineBlock, BlockQuote, Admonition, Attention, Hint, Note, Caution, Danger, Error, Important, Tip, Warning, Footnote, Citation, SystemMessage, Figure, Table }); +    fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> +    where +        W: Write, +    { +        // A mini section with title +        unimplemented!(); +    } +} + +impl_html_render_cat!(BodyElement { +    Paragraph, +    LiteralBlock, +    DoctestBlock, +    MathBlock, +    Rubric, +    SubstitutionDefinition, +    Comment, +    Pending, +    Target, +    Raw, +    Image, +    Compound, +    Container, +    BulletList, +    EnumeratedList, +    DefinitionList, +    FieldList, +    OptionList, +    LineBlock, +    BlockQuote, +    Admonition, +    Attention, +    Hint, +    Note, +    Caution, +    Danger, +    Error, +    Important, +    Tip, +    Warning, +    Footnote, +    Citation, +    SystemMessage, +    Figure, +    Table +});  impl_html_render_simple!(Paragraph => p, MathBlock => math, Rubric => a, Compound => p, Container => div, BulletList => ul["\n"], EnumeratedList => ol["\n"], DefinitionList => dl["\n"], FieldList => dl["\n"], OptionList => pre, LineBlock => div["\n"], BlockQuote => blockquote, Admonition => aside, Attention => aside, Hint => aside, Note => aside, Caution => aside, Danger => aside, Error => aside, Important => aside, Tip => aside, Warning => aside, Figure => figure); -impl_html_render_simple_nochildren!(Table => table);  //TODO: after implementing the table, move it to elems with children +impl_html_render_simple_nochildren!(Table => table); //TODO: after implementing the table, move it to elems with children  // circumvent E0119 -trait IMark {} impl IMark for e::Image {} impl IMark for e::ImageInline {} -impl<I> HTMLRender for I where I: e::Element + a::ExtraAttributes<a::Image> + IMark { -	fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { -		let extra = self.extra(); -		if let Some(ref target) = extra.target { -			write!(renderer.stream, "<a href=\"{}\">", escape_html(target.as_str()))?; -		} -		write!(renderer.stream, "<img")?; -		if let Some(ref alt) = extra.alt { -			write!(renderer.stream, " alt=\"{}\"", escape_html(alt))?; -		} -		// TODO: align: Option<AlignHV> -		// TODO: height: Option<Measure> -		// TODO: width: Option<Measure> -		// TODO: scale: Option<u8> -		write!(renderer.stream, " src=\"{}\" />", escape_html(extra.uri.as_str()))?; -		if extra.target.is_some() { -			write!(renderer.stream, "</a>")?; -		} -		Ok(()) -	} +trait IMark {} +impl IMark for e::Image {} +impl IMark for e::ImageInline {} +impl<I> HTMLRender for I +where +    I: e::Element + a::ExtraAttributes<a::Image> + IMark, +{ +    fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> +    where +        W: Write, +    { +        let extra = self.extra(); +        if let Some(ref target) = extra.target { +            write!( +                renderer.stream, +                "<a href=\"{}\">", +                escape_html(target.as_str()) +            )?; +        } +        write!(renderer.stream, "<img")?; +        if let Some(ref alt) = extra.alt { +            write!(renderer.stream, " alt=\"{}\"", escape_html(alt))?; +        } +        // TODO: align: Option<AlignHV> +        // TODO: height: Option<Measure> +        // TODO: width: Option<Measure> +        // TODO: scale: Option<u8> +        write!( +            renderer.stream, +            " src=\"{}\" />", +            escape_html(extra.uri.as_str()) +        )?; +        if extra.target.is_some() { +            write!(renderer.stream, "</a>")?; +        } +        Ok(()) +    }  }  impl HTMLRender for e::LiteralBlock { -	fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { -		let mut cls_iter = self.classes().iter(); -		let is_code = cls_iter.next() == Some(&"code".to_owned()); -		write!(renderer.stream, "<pre>")?; -		if is_code {  // TODO: support those classes not being at the start -			if let Some(lang) = cls_iter.next() { -				write!(renderer.stream, "<code class=\"language-{}\">", lang)?; -			} else { -				write!(renderer.stream, "<code>")?; -			} -		} -		for c in self.children() { -			(*c).render_html(renderer)?; -		} -		if is_code { write!(renderer.stream, "</code>")?; } -		write!(renderer.stream, "</pre>")?; -		Ok(()) -	} +    fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> +    where +        W: Write, +    { +        let mut cls_iter = self.classes().iter(); +        let is_code = cls_iter.next() == Some(&"code".to_owned()); +        write!(renderer.stream, "<pre>")?; +        if is_code { +            // TODO: support those classes not being at the start +            if let Some(lang) = cls_iter.next() { +                write!(renderer.stream, "<code class=\"language-{}\">", lang)?; +            } else { +                write!(renderer.stream, "<code>")?; +            } +        } +        for c in self.children() { +            (*c).render_html(renderer)?; +        } +        if is_code { +            write!(renderer.stream, "</code>")?; +        } +        write!(renderer.stream, "</pre>")?; +        Ok(()) +    }  }  impl HTMLRender for e::DoctestBlock { -	fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { -		// TODO -		unimplemented!(); -	} +    fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> +    where +        W: Write, +    { +        // TODO +        unimplemented!(); +    }  }  impl HTMLRender for e::SubstitutionDefinition { -	fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { -		// TODO: Should those be removed after resolving them -		Ok(()) -	} +    fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> +    where +        W: Write, +    { +        // TODO: Should those be removed after resolving them +        Ok(()) +    }  }  impl HTMLRender for e::Comment { -	fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { -		write!(renderer.stream, "<!--")?; -		for c in self.children() { -			(*c).render_html(renderer)?; -		} -		write!(renderer.stream, "-->")?; -		Ok(()) -	} +    fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> +    where +        W: Write, +    { +        write!(renderer.stream, "<!--")?; +        for c in self.children() { +            (*c).render_html(renderer)?; +        } +        write!(renderer.stream, "-->")?; +        Ok(()) +    }  }  impl HTMLRender for e::Pending { -	fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { -		// Will those be resolved by the time we get here? -		unimplemented!(); -	} +    fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> +    where +        W: Write, +    { +        // Will those be resolved by the time we get here? +        unimplemented!(); +    }  }  impl HTMLRender for e::Target { -	fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { -		// Should be resolved by now -		Ok(()) -	} +    fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> +    where +        W: Write, +    { +        // Should be resolved by now +        Ok(()) +    }  }  impl HTMLRender for e::Raw { -	fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { -		let extra = self.extra(); -		if extra.format.contains(&at::NameToken("html".to_owned())) { -			for c in self.children() { -				write!(renderer.stream, "{}", c)?; -			} -		} -		Ok(()) -	} +    fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> +    where +        W: Write, +    { +        let extra = self.extra(); +        if extra.format.contains(&at::NameToken("html".to_owned())) { +            for c in self.children() { +                write!(renderer.stream, "{}", c)?; +            } +        } +        Ok(()) +    }  }  impl HTMLRender for e::Footnote { -	fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { -		unimplemented!(); -	} +    fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> +    where +        W: Write, +    { +        unimplemented!(); +    }  }  impl HTMLRender for e::Citation { -	fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { -		unimplemented!(); -	} +    fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> +    where +        W: Write, +    { +        unimplemented!(); +    }  }  impl HTMLRender for e::SystemMessage { -	fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { -		write!(renderer.stream, "<figure><caption>System Message</caption>")?; -		for c in self.children() { -			(*c).render_html(renderer)?; -		} -		write!(renderer.stream, "</figure>")?; -		Ok(()) -	} -} - -impl_html_render_cat!(TextOrInlineElement { String, Emphasis, Strong, Literal, Reference, FootnoteReference, CitationReference, SubstitutionReference, TitleReference, Abbreviation, Acronym, Superscript, Subscript, Inline, Problematic, Generated, Math, TargetInline, RawInline, ImageInline }); +    fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> +    where +        W: Write, +    { +        write!(renderer.stream, "<figure><caption>System Message</caption>")?; +        for c in self.children() { +            (*c).render_html(renderer)?; +        } +        write!(renderer.stream, "</figure>")?; +        Ok(()) +    } +} + +impl_html_render_cat!(TextOrInlineElement { +    String, +    Emphasis, +    Strong, +    Literal, +    Reference, +    FootnoteReference, +    CitationReference, +    SubstitutionReference, +    TitleReference, +    Abbreviation, +    Acronym, +    Superscript, +    Subscript, +    Inline, +    Problematic, +    Generated, +    Math, +    TargetInline, +    RawInline, +    ImageInline +});  impl_html_render_simple!(Emphasis => em, Strong => strong, Literal => code, FootnoteReference => a, CitationReference => a, TitleReference => a, Abbreviation => abbr, Acronym => acronym, Superscript => sup, Subscript => sub, Inline => span, Math => math, TargetInline => a);  impl HTMLRender for String { -	fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { -		write!(renderer.stream, "{}", escape_html(self))?; -		Ok(()) -	} +    fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> +    where +        W: Write, +    { +        write!(renderer.stream, "{}", escape_html(self))?; +        Ok(()) +    }  }  impl HTMLRender for e::Reference { -	fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { -		let extra = self.extra(); -		write!(renderer.stream, "<a")?; -		if let Some(ref target) = extra.refuri { -			write!(renderer.stream, " href=\"{}\"", escape_html(target.as_str()))?; -		} -		/* -		if let Some(ref name) = extra.name { -			write!(renderer.stream, " title=\"{}\"", escape_html(&name.0))?; -		} -		*/ -		write!(renderer.stream, ">")?; -		for c in self.children() { -			(*c).render_html(renderer)?; -		} -		write!(renderer.stream, "</a>")?; -		Ok(()) -	} +    fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> +    where +        W: Write, +    { +        let extra = self.extra(); +        write!(renderer.stream, "<a")?; +        if let Some(ref target) = extra.refuri { +            write!( +                renderer.stream, +                " href=\"{}\"", +                escape_html(target.as_str()) +            )?; +        } +        /* +        if let Some(ref name) = extra.name { +            write!(renderer.stream, " title=\"{}\"", escape_html(&name.0))?; +        } +        */ +        write!(renderer.stream, ">")?; +        for c in self.children() { +            (*c).render_html(renderer)?; +        } +        write!(renderer.stream, "</a>")?; +        Ok(()) +    }  }  impl HTMLRender for e::SubstitutionReference { -	fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { -		// Will those be resolved by the time we get here? -		unimplemented!(); -	} +    fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> +    where +        W: Write, +    { +        // Will those be resolved by the time we get here? +        unimplemented!(); +    }  }  impl HTMLRender for e::Problematic { -	fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { -		// Broken inline markup leads to insertion of this in docutils -		unimplemented!(); -	} +    fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> +    where +        W: Write, +    { +        // Broken inline markup leads to insertion of this in docutils +        unimplemented!(); +    }  }  impl HTMLRender for e::Generated { -	fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { -		// Section numbers and so on -		unimplemented!(); -	} +    fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> +    where +        W: Write, +    { +        // Section numbers and so on +        unimplemented!(); +    }  }  impl HTMLRender for e::RawInline { -	fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { -		for c in self.children() { -			write!(renderer.stream, "{}", c)?; -		} -		Ok(()) -	} +    fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> +    where +        W: Write, +    { +        for c in self.children() { +            write!(renderer.stream, "{}", c)?; +        } +        Ok(()) +    }  } -  //--------------\\  //Content Models\\  //--------------\\  impl_html_render_cat!(SubTopic { Title, BodyElement }); -impl_html_render_cat!(SubSidebar { Topic, Title, Subtitle, BodyElement }); +impl_html_render_cat!(SubSidebar { +    Topic, +    Title, +    Subtitle, +    BodyElement +});  impl_html_render_simple!(ListItem => li);  impl HTMLRender for e::DefinitionListItem { -	fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { -		// Term→dt, Definition→dd, Classifier→??? -		unimplemented!(); -	} +    fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> +    where +        W: Write, +    { +        // Term→dt, Definition→dd, Classifier→??? +        unimplemented!(); +    }  }  impl HTMLRender for e::Field { -	fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { -		// FieldName→dt, FieldBody→dd -		unimplemented!(); -	} +    fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> +    where +        W: Write, +    { +        // FieldName→dt, FieldBody→dd +        unimplemented!(); +    }  }  impl HTMLRender for e::OptionListItem { -	fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { -		// OptionGroup→dt(s), Description→dd -		unimplemented!(); -	} +    fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> +    where +        W: Write, +    { +        // OptionGroup→dt(s), Description→dd +        unimplemented!(); +    }  }  impl_html_render_cat!(SubLineBlock { LineBlock, Line });  impl HTMLRender for e::Line { -	fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { -		for c in self.children() { -			(*c).render_html(renderer)?; -		} -		write!(renderer.stream, "<br>")?; -		Ok(()) -	} -} - -impl_html_render_cat!(SubBlockQuote { Attribution, BodyElement }); +    fn render_html<W>(&self, renderer: &mut HTMLRenderer<W>) -> Result<(), Error> +    where +        W: Write, +    { +        for c in self.children() { +            (*c).render_html(renderer)?; +        } +        write!(renderer.stream, "<br>")?; +        Ok(()) +    } +} + +impl_html_render_cat!(SubBlockQuote { +    Attribution, +    BodyElement +});  impl_html_render_simple!(Attribution => cite); //TODO: correct? -impl_html_render_cat!(SubFigure { Caption, Legend, BodyElement }); +impl_html_render_cat!(SubFigure { +    Caption, +    Legend, +    BodyElement +});  impl_html_render_simple!(Caption => caption);  impl HTMLRender for e::Legend { -	fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> where W: Write { -		unimplemented!(); -	} +    fn render_html<W>(&self, _renderer: &mut HTMLRenderer<W>) -> Result<(), Error> +    where +        W: Write, +    { +        unimplemented!(); +    }  }  //------------\\ diff --git a/renderer/src/html/tests.rs b/renderer/src/html/tests.rs index c29e9d8..4734d65 100644 --- a/renderer/src/html/tests.rs +++ b/renderer/src/html/tests.rs @@ -5,43 +5,40 @@ use rst_parser::parse;  use crate::html::render_html;  fn check_renders_to(rst: &str, expected: &str) { -	println!("Rendering:\n{}\n---", rst); -	let doc = parse(rst).expect("Cannot parse"); -	let mut result_data: Vec<u8> = vec![]; -	render_html(&doc, &mut result_data, false).expect("Render error"); -	let result = String::from_utf8(result_data).expect("Could not decode"); -	assert_eq!(result.as_str().trim(), expected); -	println!("{}", expected); +    println!("Rendering:\n{}\n---", rst); +    let doc = parse(rst).expect("Cannot parse"); +    let mut result_data: Vec<u8> = vec![]; +    render_html(&doc, &mut result_data, false).expect("Render error"); +    let result = String::from_utf8(result_data).expect("Could not decode"); +    assert_eq!(result.as_str().trim(), expected); +    println!("{}", expected);  }  #[test]  fn simple_string() { -	check_renders_to( -		"Simple String", -		"<p>Simple String</p>", -	); +    check_renders_to("Simple String", "<p>Simple String</p>");  }  #[test]  fn simple_string_with_markup() { -	check_renders_to( -		"Simple String with *emph* and **strong**", -		"<p>Simple String with <em>emph</em> and <strong>strong</strong></p>", -	); +    check_renders_to( +        "Simple String with *emph* and **strong**", +        "<p>Simple String with <em>emph</em> and <strong>strong</strong></p>", +    );  }  #[test]  fn inline_literal() { -	check_renders_to( -		"Simple String with an even simpler ``inline literal``", -		"<p>Simple String with an even simpler <code>inline literal</code></p>", -	); +    check_renders_to( +        "Simple String with an even simpler ``inline literal``", +        "<p>Simple String with an even simpler <code>inline literal</code></p>", +    );  }  /*  #[test]  fn test_reference_anonymous() { -	check_renders_to("\ +    check_renders_to("\  A simple `anonymous reference`__  __ http://www.test.com/test_url @@ -53,28 +50,31 @@ __ http://www.test.com/test_url  #[test]  fn two_paragraphs() { -	check_renders_to( -		"One paragraph.\n\nTwo paragraphs.", -		"<p>One paragraph.</p>\n<p>Two paragraphs.</p>", -	); +    check_renders_to( +        "One paragraph.\n\nTwo paragraphs.", +        "<p>One paragraph.</p>\n<p>Two paragraphs.</p>", +    );  }  #[test]  fn named_reference() { -	check_renders_to("\ +    check_renders_to( +        "\  A simple `named reference`_ with stuff in between the  reference and the target.  .. _`named reference`: http://www.test.com/test_url -", "\ +", +        "\  <p>A simple <a href=\"http://www.test.com/test_url\">named reference</a> with stuff in between the \  reference and the target.</p>\ -"); +", +    );  }  #[test]  fn standalone_hyperlinks() { -	check_renders_to("\ +    check_renders_to("\  Some http://url and a not_url_scheme:foo that is not supposed to be an url.  ", "\  <p>Some <a href=\"http://url/\">http://url</a> and a not_url_scheme:foo that is not supposed to be an url.</p>\ @@ -83,16 +83,20 @@ Some http://url and a not_url_scheme:foo that is not supposed to be an url.  #[test]  fn substitution() { -	check_renders_to("\ +    check_renders_to( +        "\  A |subst|.  .. |subst| replace:: text substitution -", "<p>A text substitution.</p>"); +", +        "<p>A text substitution.</p>", +    );  }  #[test]  fn not_substitution_literal() { -	check_renders_to("\ +    check_renders_to( +        "\  hello ``foo.map(|world| world + 42)``  .. |world| replace:: something different @@ -104,15 +108,17 @@ hello ``foo.map(|world| world + 42)``  ::      hay! |x| -", "<p>hello <code>foo.map(|world| world + 42)</code></p> +", +        "<p>hello <code>foo.map(|world| world + 42)</code></p>  <pre><code>foo.map(|world| world + 42)\n</code></pre> -<pre>hay! |x|\n</pre>"); +<pre>hay! |x|\n</pre>", +    );  }  /*  #[test]  fn test_section_hierarchy() { -	check_renders_to("\ +    check_renders_to("\  +++++  Title  +++++ @@ -146,7 +152,7 @@ And even more stuff  #[test]  fn test_docinfo_title() { -	check_renders_to("\ +    check_renders_to("\  +++++  Title  +++++ @@ -169,7 +175,8 @@ Some stuff  #[test]  fn section_hierarchy() { -	check_renders_to("\ +    check_renders_to( +        "\  +++++  Title  +++++ @@ -188,7 +195,8 @@ Another Section  ...............  And even more stuff -", "\ +", +        "\  <section id=\"title\">  <h1>Title</h1>  <section id=\"not-a-subtitle\"> @@ -204,12 +212,14 @@ And even more stuff  </section>  </section>  </section>\ -"); +", +    );  }  #[test]  fn many_sections() { -	check_renders_to("\ +    check_renders_to( +        "\  +++++++++  heading 1  +++++++++ @@ -233,7 +243,8 @@ heading 3a  ----------  Second detail -", "\ +", +        "\  <section id=\"heading-1\">  <h1>heading 1</h1>  <section id=\"heading-2\"> @@ -253,26 +264,30 @@ Second detail  </section>  </section>  </section>\ -"); +", +    );  }  #[test]  fn bullet_list() { -	check_renders_to("\ +    check_renders_to( +        "\  * bullet  * list -", "\ +", +        "\  <ul>  <li><p>bullet</p></li>  <li><p>list</p></li>  </ul>\ -"); +", +    );  }  /*  #[test]  fn test_table() { -	check_renders_to("\ +    check_renders_to("\  .. table::     :align: right @@ -302,25 +317,29 @@ fn test_table() {  #[test]  fn code() { -	check_renders_to("\ +    check_renders_to( +        "\  .. code:: python     def foo():         print('Hi!')         # comment -", "\ +", +        "\  <pre><code class=\"language-python\">def foo():      print('Hi!')      # comment  </code></pre>\ -"); +", +    );  }  #[test]  fn raw_html() { -	check_renders_to("\ +    check_renders_to( +        "\  .. raw:: html     hello <span>world</span> @@ -333,19 +352,22 @@ after  .. raw:: something_else     into a black hole this goes -", "\ +", +        "\  hello <span>world</span>  <p>paragraph  paragraph</p>  <p>after</p>\ -"); +", +    );  }  #[test]  fn comments() { -	check_renders_to("\ +    check_renders_to( +        "\  .. Run-in     comment @@ -354,7 +376,8 @@ fn comments() {      block-like      with blank lines -", "\ +", +        "\  <!--Run-in  comment  --> @@ -362,13 +385,14 @@ comment  with blank lines  -->\ -"); +", +    );  }  /*  #[test]  fn test_field_list() { -	check_renders_to("\ +    check_renders_to("\  Not a docinfo.  :This: .. _target: @@ -398,7 +422,7 @@ Not a docinfo.  /*  #[test]  fn test_field_list_long() { -	check_renders_to("\ +    check_renders_to("\  Not a docinfo.  :This is: a diff --git a/renderer/src/lib.rs b/renderer/src/lib.rs index 4d6bfdb..6e349be 100644 --- a/renderer/src/lib.rs +++ b/renderer/src/lib.rs @@ -1,21 +1,25 @@  mod html; -  use std::io::Write;  use failure::Error;  use document_tree::Document; - -pub fn render_json<W>(document: &Document, stream: W) -> Result<(), Error> where W: Write { -	serde_json::to_writer(stream, &document)?; -	Ok(()) +pub fn render_json<W>(document: &Document, stream: W) -> Result<(), Error> +where +    W: Write, +{ +    serde_json::to_writer(stream, &document)?; +    Ok(())  } -pub fn render_xml<W>(document: &Document, stream: W) -> Result<(), Error> where W: Write { -	serde_xml_rs::to_writer(stream, &document).map_err(failure::SyncFailure::new)?; -	Ok(()) +pub fn render_xml<W>(document: &Document, stream: W) -> Result<(), Error> +where +    W: Write, +{ +    serde_xml_rs::to_writer(stream, &document).map_err(failure::SyncFailure::new)?; +    Ok(())  }  pub use html::render_html; diff --git a/rst/src/main.rs b/rst/src/main.rs index cf16b16..0339ce1 100644 --- a/rst/src/main.rs +++ b/rst/src/main.rs @@ -1,48 +1,44 @@ -use structopt::StructOpt;  use clap::arg_enum;  use quicli::{ -	fs::read_file, -	prelude::{CliResult,Verbosity}, +    fs::read_file, +    prelude::{CliResult, Verbosity},  }; +use structopt::StructOpt;  use rst_parser::parse; -use rst_renderer::{ -	render_json, -	render_xml, -	render_html, -}; +use rst_renderer::{render_html, render_json, render_xml};  arg_enum! { -	#[derive(Debug)] -	#[allow(non_camel_case_types)] -	enum Format { json, xml, html } +    #[derive(Debug)] +    #[allow(non_camel_case_types)] +    enum Format { json, xml, html }  }  #[derive(Debug, StructOpt)]  #[structopt(raw(setting = "structopt::clap::AppSettings::ColoredHelp"))]  struct Cli { -	#[structopt( -		long = "format", short = "f", default_value = "html",  // xml is pretty defunct… -		raw(possible_values = "&Format::variants()", case_insensitive = "true"), -	)] -	format: Format, -	file: String, -	#[structopt(flatten)] -	verbosity: Verbosity, +    #[structopt( +        long = "format", short = "f", default_value = "html",  // xml is pretty defunct… +        raw(possible_values = "&Format::variants()", case_insensitive = "true"), +    )] +    format: Format, +    file: String, +    #[structopt(flatten)] +    verbosity: Verbosity,  }  fn main() -> CliResult { -	let args = Cli::from_args(); -	args.verbosity.setup_env_logger("rst")?; +    let args = Cli::from_args(); +    args.verbosity.setup_env_logger("rst")?; -	// TODO: somehow make it work without replacing tabs -	let content = read_file(args.file)?.replace('\t', " ".repeat(8).as_ref()); -	let document = parse(&content)?; -	let stdout = std::io::stdout(); -	match args.format { -		Format::json => render_json(&document, stdout)?, -		Format::xml  => render_xml (&document, stdout)?, -		Format::html => render_html(&document, stdout, true)?, -	} -	Ok(()) +    // TODO: somehow make it work without replacing tabs +    let content = read_file(args.file)?.replace('\t', " ".repeat(8).as_ref()); +    let document = parse(&content)?; +    let stdout = std::io::stdout(); +    match args.format { +        Format::json => render_json(&document, stdout)?, +        Format::xml => render_xml(&document, stdout)?, +        Format::html => render_html(&document, stdout, true)?, +    } +    Ok(())  } diff --git a/rust-rst.svg b/rust-rst.svg index a67350a..6ff77a3 100644 --- a/rust-rst.svg +++ b/rust-rst.svg @@ -1,57 +1,57 @@  <?xml version="1.0" encoding="UTF-8" standalone="no"?>  <svg xmlns="http://www.w3.org/2000/svg" height="104.49973" width="195.40294" version="1.1"> -	<style> -	/* colors */ -	:root { --rust-rst-lite: #ccc; --rust-rst-dark: #333 } -	.rust-rst-logo { fill: var(--rust-rst-dark); stroke: none; fill-rule: evenodd } -	.rust-rst-logo .stroked { stroke: var(--rust-rst-dark) } -	/* with a dark system theme, use: */ -	@media (prefers-color-scheme: dark) { -		.rust-rst-logo { fill: var(--rust-rst-lite) } -		.rust-rst-logo .stroked { stroke: var(--rust-rst-lite) } -	} -	/* If this SVG is embedded in a document, use the text color */ -	body .rust-rst-logo { fill: currentColor } -	body .rust-rst-logo .stroked { stroke: currentColor } +    <style> +    /* colors */ +    :root { --rust-rst-lite: #ccc; --rust-rst-dark: #333 } +    .rust-rst-logo { fill: var(--rust-rst-dark); stroke: none; fill-rule: evenodd } +    .rust-rst-logo .stroked { stroke: var(--rust-rst-dark) } +    /* with a dark system theme, use: */ +    @media (prefers-color-scheme: dark) { +        .rust-rst-logo { fill: var(--rust-rst-lite) } +        .rust-rst-logo .stroked { stroke: var(--rust-rst-lite) } +    } +    /* If this SVG is embedded in a document, use the text color */ +    body .rust-rst-logo { fill: currentColor } +    body .rust-rst-logo .stroked { stroke: currentColor } -	/* parts that use stroke */ -	.rust-rst-logo .stroked { -		stroke-width: 3; -		stroke-linecap: round; -		stroke-linejoin: round; -	} +    /* parts that use stroke */ +    .rust-rst-logo .stroked { +        stroke-width: 3; +        stroke-linecap: round; +        stroke-linejoin: round; +    } -	/* can be used to regenerate/change the logo */ -	.rust-rst-logo text { -		display: none; -		font-size: 90.66667175px; -		line-height: 1.25; -		font-family: "OCR-A", OCRA; -	} -	</style> -	<g class="rust-rst-logo"> -		<path -			d="M 52.25,4.9804688 C 26.19,4.9804688 4.9804688,26.2 4.9804688,52.25 4.9804688,78.3 26.2,99.519531 52.25,99.519531 78.3,99.519531 99.519531,78.3 99.519531,52.25 99.519531,26.2 78.3,4.9804688 52.25,4.9804688 Z m -0.07031,4.1992187 c 1.68076,0.043529 3.020091,1.4226295 3.019531,3.1093745 0,4.146665 -6.21875,4.146665 -6.21875,0 -7.34e-4,-1.753375 1.446577,-3.1601154 3.199219,-3.1093745 z m -7.029297,5.1210935 4.789062,5.019531 c 1.08,1.13 2.87,1.179844 4,0.08984 l 5.361328,-5.109375 C 70.351003,16.370525 79.931432,23.192597 85.5,32.958984 l -3.669922,8.28125 c -0.63,1.43 0.01945,3.11 1.439453,3.75 l 7.060547,3.128907 c 1.806609,8.8722 -3.653366,24.198569 -10.800779,30.611328 L 72.958984,77.320312 C 71.428984,76.990313 69.919844,77.97 69.589844,79.5 l -1.560547,7.279297 C 57.878136,91.377741 46.226622,91.32258 36.119141,86.628906 l -1.558594,-7.279297 c -0.33,-1.53 -1.831328,-2.509687 -3.361328,-2.179687 l -6.429688,1.380859 C 17.284085,70.513922 13.167601,57.997225 14.199219,47.789062 l 6.701172,-2.978515 c 1.43,-0.64 2.079453,-2.310235 1.439453,-3.740235 L 20.958984,37.958984 18.81585,33.087893 C 23.531492,24.265903 34.944722,15.885966 45.150391,14.300781 Z M 15.099609,37.320312 c 1.682359,0.04871 3.020237,1.426312 3.019532,3.109376 0,4.146664 -6.21875,4.146665 -6.21875,0 -7.34e-4,-1.753377 1.446577,-3.160117 3.199218,-3.109376 z M 89.25,37.458984 c 1.682359,0.04871 3.020236,1.428264 3.019531,3.111328 0,4.146665 -6.21875,4.146666 -6.21875,0 -7.34e-4,-1.753376 1.446578,-3.162069 3.199219,-3.111328 z M 29.150391,81.109375 c 1.682359,0.04871 3.020236,1.428264 3.019531,3.111328 0,4.146665 -6.220703,4.146665 -6.220703,0 -7.34e-4,-1.753376 1.448531,-3.162069 3.201172,-3.111328 z M 75.199219,81.25 c 1.682359,0.04871 3.022189,1.426311 3.021484,3.109375 0,4.146665 -6.220703,4.146665 -6.220703,0 -7.34e-4,-1.753376 1.446578,-3.160116 3.199219,-3.109375 z" -		/> -		<path class="stroked" -			d="m 96.879865,52.249865 a 44.63,44.63 0 0 1 -44.63,44.63 44.63,44.63 0 0 1 -44.6299999,-44.63 44.63,44.63 0 0 1 44.6299999,-44.6299999 44.63,44.63 0 0 1 44.63,44.6299999 z m -0.84,-4.31 6.960005,4.31 -6.960005,4.31 5.980005,5.59 -7.660005,2.87 4.78,6.65 -8.09,1.32 3.4,7.46 -8.19,-0.29 1.88,7.98 -7.98,-1.88 0.29,8.19 -7.46,-3.4 -1.32,8.09 -6.65,-4.78 -2.87,7.660005 -5.59,-5.980005 -4.31,6.960005 -4.31,-6.960005 -5.59,5.980005 -2.87,-7.660005 -6.65,4.78 -1.32,-8.09 -7.46,3.4 0.29,-8.19 -7.98,1.88 1.88,-7.98 -8.19,0.29 3.4,-7.46 -8.0899999,-1.32 4.7799999,-6.65 -7.6599999,-2.87 5.98,-5.59 -6.96,-4.31 6.96,-4.31 -5.98,-5.59 7.6599999,-2.87 -4.7799999,-6.65 8.0899999,-1.32 -3.4,-7.46 8.19,0.29 -1.88,-7.98 7.98,1.88 -0.29,-8.19 7.46,3.4 1.32,-8.0899999 6.65,4.7799999 2.87,-7.6599999 5.59,5.98 4.31,-6.96 4.31,6.96 5.59,-5.98 2.87,7.6599999 6.65,-4.7799999 1.32,8.0899999 7.46,-3.4 -0.29,8.19 7.98,-1.88 -1.88,7.98 8.19,-0.29 -3.4,7.46 8.09,1.32 -4.78,6.65 7.660005,2.87 z" -		/> -		<text x="21.311935" y="82.67424"> -			<tspan dx="0 7.5999999 -15.63">RST</tspan> -		</text> -		<g> -			<path -				style="font-size:90.66667175px;font-family:OCRA;word-spacing:0px" -				d="M 31.013269,47.132905 V 15.580903 H 46.51727 q 8.160001,0 11.877334,0.09067 3.717334,0 4.170667,0.09067 0.453333,0 1.36,0.272 3.898667,0.906667 6.618667,4.080001 2.72,3.082666 3.173334,7.162667 0.09067,0.544 0.09067,3.989333 -0.09067,3.626667 -0.453333,5.077334 -0.816,3.445333 -3.445334,5.984 -2.538666,2.538667 -5.984,3.445334 -1.36,0.362666 -5.077333,0.453333 h -3.173334 l 8.885334,15.141334 q 8.885334,15.232001 8.885334,15.413334 1.088,2.901334 -1.088,4.714667 -1.269334,1.269334 -3.082667,1.178667 -1.813334,-0.09067 -3.082667,-1.450667 -0.272,-0.272 -0.634667,-0.997333 L 55.402604,62.908906 45.610603,46.226238 h -2.992 -2.992 v 16.410668 q 0,16.501334 -0.09067,16.773334 -0.362666,0.997333 -0.816,1.632 -0.634666,0.816 -1.450666,1.178667 -1.813334,0.816 -3.445334,0.181333 -0.816,-0.272 -1.541333,-0.997333 -0.816,-0.906667 -0.997334,-1.541334 0,-0.09067 -0.09067,-0.09067 -0.181334,-0.544 -0.181334,-16.048001 0,-9.701334 0,-16.592001 z M 61.839938,24.37557 q -0.272,-0.09067 -11.242668,-0.09067 H 39.626603 v 6.618667 6.618667 H 50.59727 q 5.712001,0 8.341334,0 2.629334,-0.09067 2.901334,-0.09067 0.272,-0.09067 0.816,-0.362667 1.450666,-0.725333 1.994667,-2.085333 0.453333,-0.816 0.453333,-4.08 0,-3.264001 -0.453333,-4.170667 -0.816001,-1.632 -2.810667,-2.357334 z" -			/> -			<path -				style="font-size:90.66667175px;font-family:OCRA;word-spacing:0px" -				d="m 111.85777,15.67157 q 0.0907,0 0.816,0 0.72533,0 1.81333,0 1.17867,-0.09067 3.62667,-0.09067 2.53867,0 5.712,0 11.51467,0 12.05867,0.09067 4.08,0.544 6.98133,3.445333 2.81067,2.810667 3.264,6.981334 0.36267,2.72 -1.26933,4.261334 -1.26934,1.178666 -2.992,1.178666 -0.72534,0 -1.17867,-0.09067 -1.088,-0.362667 -1.904,-1.178667 -1.17867,-1.178667 -1.26933,-2.992 0,-0.544 -0.18134,-1.088 -0.45333,-1.269334 -1.81333,-1.722667 l -0.36267,-0.181333 h -11.152 -11.24267 l -0.18133,0.181333 q -0.72533,0.634667 -0.45333,1.36 0.0907,0.09067 16.13867,20.762668 16.13866,20.581334 16.32,20.944001 1.26933,1.994667 1.54133,4.170667 0.45333,3.626667 -1.632,6.709334 -2.08533,2.992 -5.62133,3.898666 -1.36,0.362667 -13.23734,0.362667 -11.424,0 -11.968,-0.09067 -4.08,-0.544 -6.98133,-3.445333 -2.81067,-2.992 -3.264,-6.981334 -0.272,-2.538667 1.17866,-4.08 0.816,-0.906667 1.99467,-1.178667 0.36267,-0.181333 1.088,-0.181333 1.81333,0 2.992,1.178667 1.26933,1.088 1.36,3.082666 l 0.18133,1.088001 q 0.544,1.269333 1.81334,1.722666 l 0.36267,0.181334 h 11.152 11.06133 l 0.272,-0.09067 q 1.088,-0.544 0.544,-1.36 0,-0.272 -16.13867,-20.853335 -16.048,-20.672001 -16.32,-20.944001 -1.54133,-2.448 -1.54133,-5.349333 0,-2.992001 1.632,-5.349334 2.448,-3.808 6.8,-4.352 z" -			/> -			<path -				style="font-size:90.66667175px;font-family:OCRA;word-spacing:0px" -				d="m 152.60827,21.564903 v -5.984 h 21.39734 21.39733 v 5.984 q 0,6.437334 -0.36267,7.253334 -0.544,1.722667 -2.35733,2.448 -0.63467,0.272 -1.632,0.272 -1.17867,0 -1.904,-0.362666 -1.45067,-0.816 -2.176,-2.357334 -0.272,-1.088 -0.272,-2.901333 v -1.632 h -4.17067 -4.26133 v 27.381334 q 0,27.472002 -0.0907,27.744002 -0.544,1.994667 -2.26666,2.810667 -1.81334,0.816 -3.44534,0.181333 -0.72533,-0.181333 -1.54133,-0.997333 -0.816,-0.906667 -0.99734,-1.541334 0,-0.09067 -0.0907,-0.09067 -0.18134,-0.544 -0.18134,-13.962668 0,-8.341334 0,-14.325334 V 24.284904 h -4.26133 -4.17067 v 1.632 q 0,2.176 -0.272,2.901333 -0.72533,1.722667 -2.448,2.448 -0.544,0.272 -1.632,0.272 -1.088,0 -1.81333,-0.362666 -1.632,-0.906667 -2.176,-2.357334 -0.272,-0.816 -0.272,-7.253334 z" -			/> -		</g> -	</g> +    /* can be used to regenerate/change the logo */ +    .rust-rst-logo text { +        display: none; +        font-size: 90.66667175px; +        line-height: 1.25; +        font-family: "OCR-A", OCRA; +    } +    </style> +    <g class="rust-rst-logo"> +        <path +            d="M 52.25,4.9804688 C 26.19,4.9804688 4.9804688,26.2 4.9804688,52.25 4.9804688,78.3 26.2,99.519531 52.25,99.519531 78.3,99.519531 99.519531,78.3 99.519531,52.25 99.519531,26.2 78.3,4.9804688 52.25,4.9804688 Z m -0.07031,4.1992187 c 1.68076,0.043529 3.020091,1.4226295 3.019531,3.1093745 0,4.146665 -6.21875,4.146665 -6.21875,0 -7.34e-4,-1.753375 1.446577,-3.1601154 3.199219,-3.1093745 z m -7.029297,5.1210935 4.789062,5.019531 c 1.08,1.13 2.87,1.179844 4,0.08984 l 5.361328,-5.109375 C 70.351003,16.370525 79.931432,23.192597 85.5,32.958984 l -3.669922,8.28125 c -0.63,1.43 0.01945,3.11 1.439453,3.75 l 7.060547,3.128907 c 1.806609,8.8722 -3.653366,24.198569 -10.800779,30.611328 L 72.958984,77.320312 C 71.428984,76.990313 69.919844,77.97 69.589844,79.5 l -1.560547,7.279297 C 57.878136,91.377741 46.226622,91.32258 36.119141,86.628906 l -1.558594,-7.279297 c -0.33,-1.53 -1.831328,-2.509687 -3.361328,-2.179687 l -6.429688,1.380859 C 17.284085,70.513922 13.167601,57.997225 14.199219,47.789062 l 6.701172,-2.978515 c 1.43,-0.64 2.079453,-2.310235 1.439453,-3.740235 L 20.958984,37.958984 18.81585,33.087893 C 23.531492,24.265903 34.944722,15.885966 45.150391,14.300781 Z M 15.099609,37.320312 c 1.682359,0.04871 3.020237,1.426312 3.019532,3.109376 0,4.146664 -6.21875,4.146665 -6.21875,0 -7.34e-4,-1.753377 1.446577,-3.160117 3.199218,-3.109376 z M 89.25,37.458984 c 1.682359,0.04871 3.020236,1.428264 3.019531,3.111328 0,4.146665 -6.21875,4.146666 -6.21875,0 -7.34e-4,-1.753376 1.446578,-3.162069 3.199219,-3.111328 z M 29.150391,81.109375 c 1.682359,0.04871 3.020236,1.428264 3.019531,3.111328 0,4.146665 -6.220703,4.146665 -6.220703,0 -7.34e-4,-1.753376 1.448531,-3.162069 3.201172,-3.111328 z M 75.199219,81.25 c 1.682359,0.04871 3.022189,1.426311 3.021484,3.109375 0,4.146665 -6.220703,4.146665 -6.220703,0 -7.34e-4,-1.753376 1.446578,-3.160116 3.199219,-3.109375 z" +        /> +        <path class="stroked" +            d="m 96.879865,52.249865 a 44.63,44.63 0 0 1 -44.63,44.63 44.63,44.63 0 0 1 -44.6299999,-44.63 44.63,44.63 0 0 1 44.6299999,-44.6299999 44.63,44.63 0 0 1 44.63,44.6299999 z m -0.84,-4.31 6.960005,4.31 -6.960005,4.31 5.980005,5.59 -7.660005,2.87 4.78,6.65 -8.09,1.32 3.4,7.46 -8.19,-0.29 1.88,7.98 -7.98,-1.88 0.29,8.19 -7.46,-3.4 -1.32,8.09 -6.65,-4.78 -2.87,7.660005 -5.59,-5.980005 -4.31,6.960005 -4.31,-6.960005 -5.59,5.980005 -2.87,-7.660005 -6.65,4.78 -1.32,-8.09 -7.46,3.4 0.29,-8.19 -7.98,1.88 1.88,-7.98 -8.19,0.29 3.4,-7.46 -8.0899999,-1.32 4.7799999,-6.65 -7.6599999,-2.87 5.98,-5.59 -6.96,-4.31 6.96,-4.31 -5.98,-5.59 7.6599999,-2.87 -4.7799999,-6.65 8.0899999,-1.32 -3.4,-7.46 8.19,0.29 -1.88,-7.98 7.98,1.88 -0.29,-8.19 7.46,3.4 1.32,-8.0899999 6.65,4.7799999 2.87,-7.6599999 5.59,5.98 4.31,-6.96 4.31,6.96 5.59,-5.98 2.87,7.6599999 6.65,-4.7799999 1.32,8.0899999 7.46,-3.4 -0.29,8.19 7.98,-1.88 -1.88,7.98 8.19,-0.29 -3.4,7.46 8.09,1.32 -4.78,6.65 7.660005,2.87 z" +        /> +        <text x="21.311935" y="82.67424"> +            <tspan dx="0 7.5999999 -15.63">RST</tspan> +        </text> +        <g> +            <path +                style="font-size:90.66667175px;font-family:OCRA;word-spacing:0px" +                d="M 31.013269,47.132905 V 15.580903 H 46.51727 q 8.160001,0 11.877334,0.09067 3.717334,0 4.170667,0.09067 0.453333,0 1.36,0.272 3.898667,0.906667 6.618667,4.080001 2.72,3.082666 3.173334,7.162667 0.09067,0.544 0.09067,3.989333 -0.09067,3.626667 -0.453333,5.077334 -0.816,3.445333 -3.445334,5.984 -2.538666,2.538667 -5.984,3.445334 -1.36,0.362666 -5.077333,0.453333 h -3.173334 l 8.885334,15.141334 q 8.885334,15.232001 8.885334,15.413334 1.088,2.901334 -1.088,4.714667 -1.269334,1.269334 -3.082667,1.178667 -1.813334,-0.09067 -3.082667,-1.450667 -0.272,-0.272 -0.634667,-0.997333 L 55.402604,62.908906 45.610603,46.226238 h -2.992 -2.992 v 16.410668 q 0,16.501334 -0.09067,16.773334 -0.362666,0.997333 -0.816,1.632 -0.634666,0.816 -1.450666,1.178667 -1.813334,0.816 -3.445334,0.181333 -0.816,-0.272 -1.541333,-0.997333 -0.816,-0.906667 -0.997334,-1.541334 0,-0.09067 -0.09067,-0.09067 -0.181334,-0.544 -0.181334,-16.048001 0,-9.701334 0,-16.592001 z M 61.839938,24.37557 q -0.272,-0.09067 -11.242668,-0.09067 H 39.626603 v 6.618667 6.618667 H 50.59727 q 5.712001,0 8.341334,0 2.629334,-0.09067 2.901334,-0.09067 0.272,-0.09067 0.816,-0.362667 1.450666,-0.725333 1.994667,-2.085333 0.453333,-0.816 0.453333,-4.08 0,-3.264001 -0.453333,-4.170667 -0.816001,-1.632 -2.810667,-2.357334 z" +            /> +            <path +                style="font-size:90.66667175px;font-family:OCRA;word-spacing:0px" +                d="m 111.85777,15.67157 q 0.0907,0 0.816,0 0.72533,0 1.81333,0 1.17867,-0.09067 3.62667,-0.09067 2.53867,0 5.712,0 11.51467,0 12.05867,0.09067 4.08,0.544 6.98133,3.445333 2.81067,2.810667 3.264,6.981334 0.36267,2.72 -1.26933,4.261334 -1.26934,1.178666 -2.992,1.178666 -0.72534,0 -1.17867,-0.09067 -1.088,-0.362667 -1.904,-1.178667 -1.17867,-1.178667 -1.26933,-2.992 0,-0.544 -0.18134,-1.088 -0.45333,-1.269334 -1.81333,-1.722667 l -0.36267,-0.181333 h -11.152 -11.24267 l -0.18133,0.181333 q -0.72533,0.634667 -0.45333,1.36 0.0907,0.09067 16.13867,20.762668 16.13866,20.581334 16.32,20.944001 1.26933,1.994667 1.54133,4.170667 0.45333,3.626667 -1.632,6.709334 -2.08533,2.992 -5.62133,3.898666 -1.36,0.362667 -13.23734,0.362667 -11.424,0 -11.968,-0.09067 -4.08,-0.544 -6.98133,-3.445333 -2.81067,-2.992 -3.264,-6.981334 -0.272,-2.538667 1.17866,-4.08 0.816,-0.906667 1.99467,-1.178667 0.36267,-0.181333 1.088,-0.181333 1.81333,0 2.992,1.178667 1.26933,1.088 1.36,3.082666 l 0.18133,1.088001 q 0.544,1.269333 1.81334,1.722666 l 0.36267,0.181334 h 11.152 11.06133 l 0.272,-0.09067 q 1.088,-0.544 0.544,-1.36 0,-0.272 -16.13867,-20.853335 -16.048,-20.672001 -16.32,-20.944001 -1.54133,-2.448 -1.54133,-5.349333 0,-2.992001 1.632,-5.349334 2.448,-3.808 6.8,-4.352 z" +            /> +            <path +                style="font-size:90.66667175px;font-family:OCRA;word-spacing:0px" +                d="m 152.60827,21.564903 v -5.984 h 21.39734 21.39733 v 5.984 q 0,6.437334 -0.36267,7.253334 -0.544,1.722667 -2.35733,2.448 -0.63467,0.272 -1.632,0.272 -1.17867,0 -1.904,-0.362666 -1.45067,-0.816 -2.176,-2.357334 -0.272,-1.088 -0.272,-2.901333 v -1.632 h -4.17067 -4.26133 v 27.381334 q 0,27.472002 -0.0907,27.744002 -0.544,1.994667 -2.26666,2.810667 -1.81334,0.816 -3.44534,0.181333 -0.72533,-0.181333 -1.54133,-0.997333 -0.816,-0.906667 -0.99734,-1.541334 0,-0.09067 -0.0907,-0.09067 -0.18134,-0.544 -0.18134,-13.962668 0,-8.341334 0,-14.325334 V 24.284904 h -4.26133 -4.17067 v 1.632 q 0,2.176 -0.272,2.901333 -0.72533,1.722667 -2.448,2.448 -0.544,0.272 -1.632,0.272 -1.088,0 -1.81333,-0.362666 -1.632,-0.906667 -2.176,-2.357334 -0.272,-0.816 -0.272,-7.253334 z" +            /> +        </g> +    </g>  </svg> | 
