diff options
| author | Philipp A | 2018-11-21 21:28:59 +0100 | 
|---|---|---|
| committer | Philipp A | 2018-11-21 21:28:59 +0100 | 
| commit | 37ee18111c4ad76076ab973a4f38544ec9974050 (patch) | |
| tree | 0fdfc2eb25d0e9a7e69d2257ed11daeee381e4e5 | |
| parent | 32d660c23ca7d44da44112187f2efb18fa4077ed (diff) | |
| download | rust-rst-37ee18111c4ad76076ab973a4f38544ec9974050.tar.bz2 | |
Automate serialization
| -rw-r--r-- | src/document_tree/elements.rs | 80 | ||||
| -rw-r--r-- | src/parser.rs | 117 | ||||
| -rw-r--r-- | src/parser/conversion.rs | 99 | ||||
| -rw-r--r-- | src/parser/pest_rst.rs | 5 | 
4 files changed, 149 insertions, 152 deletions
| diff --git a/src/document_tree/elements.rs b/src/document_tree/elements.rs index a5519ce..99772b2 100644 --- a/src/document_tree/elements.rs +++ b/src/document_tree/elements.rs @@ -1,14 +1,15 @@ -use url::Url; -use serde::{ -    Serialize, -    Serializer, -    ser::SerializeStruct, -};  use serde_derive::Serialize; +use url::Url;  use super::extra_attributes::{self,ExtraAttributes};  use super::element_categories::*; +fn serialize_opt_url<S>(url_opt: &Option<Url>, serializer: S) -> Result<S::Ok, S::Error> where S: serde::ser::Serializer { +	match url_opt { +		Some(ref url) => serializer.serialize_some(url.as_str()), +		None          => serializer.serialize_none(), +	} +}  //-----------------\\  //Element hierarchy\\ @@ -25,10 +26,11 @@ pub trait Element {  	fn classes_mut(&mut self) -> &mut Vec<String>;  } -#[derive(Default,Debug)] +#[derive(Debug,Default,Serialize)]  pub struct CommonAttributes {  	ids:     Vec<String>,  	names:   Vec<String>, +	#[serde(serialize_with = "serialize_opt_url")]  	source:  Option<Url>,  	classes: Vec<String>,  	//left out dupnames @@ -69,62 +71,56 @@ macro_rules! impl_extra { ($name:ident) => (  macro_rules! impl_new {(  	$(#[$attr:meta])* -	pub struct $name:ident { $( $field:ident : $typ:path ),* -}) => ( +	pub struct $name:ident { $( +		$(#[$fattr:meta])* +		$field:ident : $typ:path +	),* $(,)* } +) => (  	$(#[$attr])* -	pub struct $name { $( $field: $typ, )* } +	#[derive(Debug,Serialize)] +	pub struct $name { $(  +		$(#[$fattr])* $field: $typ, +	)* }  	impl $name {  		pub fn new( $( $field: $typ, )* ) -> $name { $name { $( $field: $field, )* } }  	}  )} -macro_rules! impl_serialize { -	($name: ident, $extra: ident, $children: ident) => { -		impl Serialize for $name { -			fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer { -				let mut state = serializer.serialize_struct(stringify!($name), 6)?; -				state.serialize_field("ids", self.ids())?; -				state.serialize_field("names", self.names())?; -				state.serialize_field("source", &self.source().as_ref().map(|uri| uri.to_string()))?; -				state.serialize_field("classes", self.classes())?; -				state.serialize_field("extra",    &impl_cond!($extra    ? self.extra()   ))?; -				state.serialize_field("children", &impl_cond!($children ? self.children()))?; -				state.end() -			} -		} -	}; -} - -macro_rules! impl_cond { -	(false ? $($b:tt)*) => { () }; -	(true  ? $($b:tt)*) => { $($b)* }; -} -  macro_rules! impl_elem {  	($name:ident) => { -		impl_new!(#[derive(Default,Debug)] pub struct $name { common: CommonAttributes }); +		impl_new!(#[derive(Default)] pub struct $name { +			#[serde(flatten)] common: CommonAttributes, +		});  		impl_element!($name); -		impl_serialize!($name, false, false);  	};  	($name:ident; +) => { -		impl_new!(#[derive(Default,Debug)] pub struct $name { common: CommonAttributes, extra: extra_attributes::$name }); +		impl_new!(#[derive(Default)] pub struct $name { +			#[serde(flatten)] common: CommonAttributes, +			#[serde(flatten)] extra: extra_attributes::$name, +		});  		impl_element!($name); impl_extra!($name); -		impl_serialize!($name, true, false);  	};  	($name:ident; *) => { //same as above with no default -		impl_new!(#[derive(Debug)] pub struct $name { common: CommonAttributes, extra: extra_attributes::$name }); +		impl_new!(pub struct $name { +			#[serde(flatten)] common: CommonAttributes, +			#[serde(flatten)] extra: extra_attributes::$name +		});  		impl_element!($name); impl_extra!($name); -		impl_serialize!($name, true, false);  	};  	($name:ident, $childtype:ident) => { -		impl_new!(#[derive(Default,Debug)] pub struct $name { common: CommonAttributes, children: Vec<$childtype> }); +		impl_new!(#[derive(Default)] pub struct $name { +			#[serde(flatten)] common: CommonAttributes, +			children: Vec<$childtype>, +		});  		impl_element!($name); impl_children!($name, $childtype); -		impl_serialize!($name, false, true);  	};  	($name:ident, $childtype:ident; +) => { -		impl_new!(#[derive(Default,Debug)] pub struct $name { common: CommonAttributes, extra: extra_attributes::$name, children: Vec<$childtype> }); +		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); impl_children!($name, $childtype); -		impl_serialize!($name, true, true);  	};  } diff --git a/src/parser.rs b/src/parser.rs index a75d2a1..893f635 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,127 +1,24 @@  pub mod token; +pub mod conversion; +mod pest_rst;  #[cfg(test)]  pub mod tests; -mod pest_rst { -    use pest_derive::Parser; -     -    #[derive(Parser)] -    #[grammar = "rst.pest"] -    pub struct RstParser; -} -use self::pest_rst::Rule; -  use std::io::Write; -use url::Url;  use failure::Error; -use failure_derive::Fail;  use pest::Parser; -use crate::document_tree::{ -    HasChildren, -    elements::{ -        Document, -        Title, -        Paragraph, -        Target, -        Attention, Hint, Note, Caution, Danger, Error as ErrorEl, Important, Tip, Warning -    }, -    element_categories::{ -        StructuralSubElement, -        SubStructure, -        BodyElement, -    }, -    attribute_types::ID, -    extra_attributes, -}; - - -#[derive(Debug, Fail)] -enum ConversionError { -    #[fail(display = "unknown rule: {:?}", rule)] -    UnknownRuleError { -        rule: Rule, -    }, -} - - -fn convert_ssubel(pair: pest::iterators::Pair<Rule>) -> Result<StructuralSubElement, Error> { -    // TODO: This is just a proof of concep. Keep closely to DTD in final version! -    match pair.as_rule() { -        Rule::title => Ok(convert_title(pair).into()), -        Rule::paragraph => Ok(to_ssub(Paragraph::with_children(vec![pair.as_str().into()]))), -        Rule::target => Ok(to_ssub(convert_target(pair)?)), -        Rule::admonition_gen => Ok(to_ssub(convert_admonition_gen(pair)?)), -        rule => Err(ConversionError::UnknownRuleError { rule }.into()), -    } -} - +use crate::document_tree::elements::Document; -fn to_ssub<E>(elem: E) -> StructuralSubElement where E: Into<BodyElement> { -    let belm: BodyElement = elem.into(); -    let sstruc: SubStructure = belm.into(); -    sstruc.into() -} - - -fn convert_title(pair: pest::iterators::Pair<pest_rst::Rule>) -> Title { -    let mut title: Option<&str> = None; -    let mut _adornment_char: Option<char> = None; -    for p in pair.into_inner() { -        match p.as_rule() { -            Rule::line => title = Some(p.as_str()), -            Rule::adornments => _adornment_char = Some(p.as_str().chars().next().expect("Empty adornment?")), -            rule => panic!("Unexpected rule in title: {:?}", rule), -        }; -    } -    // TODO adornment char -    Title::with_children(vec![ -        title.expect("No text in title").into() -    ]) -} - -fn convert_target(pair: pest::iterators::Pair<pest_rst::Rule>) -> Result<Target, Error> { -    let mut attrs = extra_attributes::Target { -        anonymous: false, -        ..Default::default() -    }; -    for p in pair.into_inner() { -        match p.as_rule() { -            // TODO: or is it refnames? -            Rule::target_name_uq | Rule::target_name_qu => attrs.refid = Some(ID(p.as_str().to_owned())), -            Rule::link_target => attrs.refuri = Some(Url::parse(p.as_str())?), -            rule => panic!("Unexpected rule in target: {:?}", rule), -        } -    } -    Ok(Target::new(Default::default(), attrs)) -} - -fn convert_admonition_gen(pair: pest::iterators::Pair<pest_rst::Rule>) -> Result<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<BodyElement> = iter.map(|p| Paragraph::with_children(vec![p.as_str().into()]).into()).collect(); -    Ok(match typ { -        "attention" => Attention::with_children(children).into(), -        "hint"      =>      Hint::with_children(children).into(), -        "note"      =>      Note::with_children(children).into(), -        "caution"   =>   Caution::with_children(children).into(), -        "danger"    =>    Danger::with_children(children).into(), -        "error"     =>   ErrorEl::with_children(children).into(), -        "important" => Important::with_children(children).into(), -        "tip"       =>       Tip::with_children(children).into(), -        "warning"   =>   Warning::with_children(children).into(), -        typ         => panic!("Unknown admontion type {}!", typ), -    }) -} +use self::pest_rst::{RstParser,Rule}; +use self::conversion::convert_document;  /// tokens to Document tree. resolves sections, but not references  pub fn parse(source: &str) -> Result<Document, Error> { -    let pairs = pest_rst::RstParser::parse(pest_rst::Rule::document, source)?; -    let structural_elems = pairs.map(convert_ssubel).collect::<Result<_, _>>()?; -    Ok(Document::with_children(structural_elems)) +    let pairs = RstParser::parse(Rule::document, source)?; +    convert_document(pairs)  } diff --git a/src/parser/conversion.rs b/src/parser/conversion.rs new file mode 100644 index 0000000..5fc9605 --- /dev/null +++ b/src/parser/conversion.rs @@ -0,0 +1,99 @@ +use url::Url; +use failure::Error; +use failure_derive::Fail; +use pest::iterators::{Pairs,Pair}; + +use crate::document_tree::{ +    HasChildren, +    elements as e, +    element_categories as c, +    attribute_types::ID, +    extra_attributes, +}; + +use super::pest_rst::Rule; + +#[derive(Debug, Fail)] +enum ConversionError { +    #[fail(display = "unknown rule: {:?}", rule)] +    UnknownRuleError { +        rule: Rule, +    }, +} + + +pub fn convert_document(pairs: Pairs<Rule>) -> Result<e::Document, Error> { +    let structural_elems = pairs.map(convert_ssubel).collect::<Result<_,_>>()?; +    Ok(e::Document::with_children(structural_elems)) +} + + +fn convert_ssubel(pair: Pair<Rule>) -> Result<c::StructuralSubElement, Error> { +    // TODO: This is just a proof of concep. Keep closely to DTD in final version! +    match pair.as_rule() { +        Rule::title => Ok(convert_title(pair).into()), +        Rule::paragraph => Ok(to_ssub(e::Paragraph::with_children(vec![pair.as_str().into()]))), +        Rule::target => Ok(to_ssub(convert_target(pair)?)), +        Rule::admonition_gen => Ok(to_ssub(convert_admonition_gen(pair)?)), +        rule => Err(ConversionError::UnknownRuleError { rule }.into()), +    } +} + + +fn to_ssub<E>(elem: E) -> c::StructuralSubElement where E: Into<c::BodyElement> { +    let belm: c::BodyElement = elem.into(); +    let sstruc: c::SubStructure = belm.into(); +    sstruc.into() +} + + +fn convert_title(pair: Pair<Rule>) -> e::Title { +    let mut title: Option<&str> = None; +    let mut _adornment_char: Option<char> = None; +    for p in pair.into_inner() { +        match p.as_rule() { +            Rule::line => title = Some(p.as_str()), +            Rule::adornments => _adornment_char = Some(p.as_str().chars().next().expect("Empty adornment?")), +            rule => panic!("Unexpected rule in title: {:?}", rule), +        }; +    } +    // TODO adornment char +    e::Title::with_children(vec![ +        title.expect("No text in title").into() +    ]) +} + +fn convert_target(pair: Pair<Rule>) -> Result<e::Target, Error> { +    let mut attrs = extra_attributes::Target { +        anonymous: false, +        ..Default::default() +    }; +    for p in pair.into_inner() { +        match p.as_rule() { +            // TODO: or is it refnames? +            Rule::target_name_uq | Rule::target_name_qu => attrs.refid = Some(ID(p.as_str().to_owned())), +            Rule::link_target => attrs.refuri = Some(Url::parse(p.as_str())?), +            rule => panic!("Unexpected rule in target: {:?}", rule), +        } +    } +    Ok(e::Target::new(Default::default(), attrs)) +} + +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), +    }) +} diff --git a/src/parser/pest_rst.rs b/src/parser/pest_rst.rs new file mode 100644 index 0000000..8c36fc1 --- /dev/null +++ b/src/parser/pest_rst.rs @@ -0,0 +1,5 @@ +use pest_derive::Parser; + +#[derive(Parser)] +#[grammar = "rst.pest"] +pub struct RstParser; | 
