aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPhilipp A2018-11-27 00:45:22 +0100
committerPhilipp A2018-11-27 00:45:22 +0100
commit71d26fbdc4825b4c2a2db125dde388dc5c5a196c (patch)
tree15e485c6324bf2a5e6997a0a766345d44d0c4d50 /src
parent70667b529fcf24815819636b9a105d17ae38e796 (diff)
downloadrust-rst-71d26fbdc4825b4c2a2db125dde388dc5c5a196c.tar.bz2
Support inline Images
Diffstat (limited to 'src')
-rw-r--r--src/document_tree.rs1
-rw-r--r--src/document_tree/attribute_types.rs68
-rw-r--r--src/document_tree/element_categories.rs8
-rw-r--r--src/document_tree/elements.rs15
-rw-r--r--src/document_tree/extra_attributes.rs92
-rw-r--r--src/document_tree/serde_util.rs12
-rw-r--r--src/parser/conversion.rs57
-rw-r--r--src/parser/tests.rs3
8 files changed, 193 insertions, 63 deletions
diff --git a/src/document_tree.rs b/src/document_tree.rs
index 3d983da..749654c 100644
--- a/src/document_tree.rs
+++ b/src/document_tree.rs
@@ -3,6 +3,7 @@
#[macro_use]
mod macro_util;
+mod serde_util;
pub mod elements;
pub mod element_categories;
diff --git a/src/document_tree/attribute_types.rs b/src/document_tree/attribute_types.rs
index 9ad66be..cd766f2 100644
--- a/src/document_tree/attribute_types.rs
+++ b/src/document_tree/attribute_types.rs
@@ -1,4 +1,8 @@
+use std::str::FromStr;
+
+use failure::{Error,bail,format_err};
use serde_derive::Serialize;
+use regex::Regex;
#[derive(Debug,Serialize)]
pub enum EnumeratedListType {
@@ -20,8 +24,64 @@ impl Default for FixedSpace { fn default() -> FixedSpace { FixedSpace::Preserve
#[derive(Debug,Serialize)] pub struct NameToken(pub String);
#[derive(Debug,Serialize)]
-pub enum Measure {
- Pixel(usize),
- Em(usize),
- //TODO
+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),
+ })
+ }
+}
+
+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!(),
+ })
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ fn test_parse_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();
+ }
+
+ // TODO: Test parsing measures
}
diff --git a/src/document_tree/element_categories.rs b/src/document_tree/element_categories.rs
index df291cf..b4d63a2 100644
--- a/src/document_tree/element_categories.rs
+++ b/src/document_tree/element_categories.rs
@@ -117,7 +117,13 @@ mod test {
}
#[test]
+ fn test_convert_even_more() {
+ let _: StructuralSubElement = Paragraph::default().into();
+ }
+
+ #[test]
fn test_convert_super() {
- let _: StructuralSubElement = BodyElement::Paragraph(Paragraph::default()).into();
+ let be: BodyElement = Paragraph::default().into();
+ let _: StructuralSubElement = be.into();
}
}
diff --git a/src/document_tree/elements.rs b/src/document_tree/elements.rs
index 99772b2..2b99d97 100644
--- a/src/document_tree/elements.rs
+++ b/src/document_tree/elements.rs
@@ -1,15 +1,10 @@
use serde_derive::Serialize;
use url::Url;
+use super::serde_util::serialize_opt_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\\
@@ -61,9 +56,9 @@ macro_rules! impl_children { ($name:ident, $childtype:ident) => (
}
)}
-macro_rules! impl_extra { ($name:ident) => (
+macro_rules! impl_extra { ($name:ident $($more:tt)*) => (
impl ExtraAttributes<extra_attributes::$name> for $name {
-// fn with_extra(extra: extra_attributes::$name) -> $name { $name { extra: extra, ..Default::default() } }
+ fn with_extra(extra: extra_attributes::$name) -> $name { $name { common: Default::default(), extra: extra $($more)* } }
fn extra (& self) -> & extra_attributes::$name { & self.extra }
fn extra_mut(&mut self) -> &mut extra_attributes::$name { &mut self.extra }
}
@@ -98,7 +93,7 @@ macro_rules! impl_elem {
#[serde(flatten)] common: CommonAttributes,
#[serde(flatten)] extra: extra_attributes::$name,
});
- impl_element!($name); impl_extra!($name);
+ impl_element!($name); impl_extra!($name, ..Default::default());
};
($name:ident; *) => { //same as above with no default
impl_new!(pub struct $name {
@@ -120,7 +115,7 @@ macro_rules! impl_elem {
#[serde(flatten)] extra: extra_attributes::$name,
children: Vec<$childtype>,
});
- impl_element!($name); impl_extra!($name); impl_children!($name, $childtype);
+ impl_element!($name); impl_extra!($name, ..Default::default()); impl_children!($name, $childtype);
};
}
diff --git a/src/document_tree/extra_attributes.rs b/src/document_tree/extra_attributes.rs
index 80fe045..916ba41 100644
--- a/src/document_tree/extra_attributes.rs
+++ b/src/document_tree/extra_attributes.rs
@@ -1,50 +1,27 @@
use url::Url;
-use serde::{
- Serialize,
- Serializer,
- ser::SerializeStruct,
-};
+use serde_derive::Serialize;
+use super::serde_util::{serialize_url,serialize_opt_url};
use super::attribute_types::{FixedSpace,ID,NameToken,AlignHV,AlignH,Measure,EnumeratedListType};
pub trait ExtraAttributes<A> {
-// fn with_extra(extra: A) -> Self;
+ fn with_extra(extra: A) -> Self;
fn extra (& self) -> & A;
fn extra_mut(&mut self) -> &mut A;
}
-macro_rules! count {
- () => (0usize);
- ( $x:tt $($xs:tt)* ) => (1usize + count!($($xs)*));
-}
-
-macro_rules! ser_url {
- ($self:ident, refuri ) => { $self.refuri.as_ref().map(|uri| uri.to_string()) };
- ($self:ident, uri ) => { $self.uri.to_string() };
- ($self:ident, $param:ident) => { $self.$param };
-}
-
macro_rules! impl_extra {
- ( $name:ident { $( $param:ident : $type:ty ),* $(,)* } ) => (
+ ( $name:ident { $( $(#[$pattr:meta])* $param:ident : $type:ty ),* $(,)* } ) => (
impl_extra!(
- #[derive(Default,Debug)]
- $name { $( $param : $type, )* }
+ #[derive(Default,Debug,Serialize)]
+ $name { $( $(#[$pattr])* $param : $type, )* }
);
);
- ( $(#[$attr:meta])+ $name:ident { $( $param:ident : $type:ty ),* $(,)* } ) => (
+ ( $(#[$attr:meta])+ $name:ident { $( $(#[$pattr:meta])* $param:ident : $type:ty ),* $(,)* } ) => (
$(#[$attr])+
pub struct $name {
- $( pub $param : $type, )*
- }
-
- impl Serialize for $name {
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
- #[allow(unused_mut)]
- let mut state = serializer.serialize_struct(stringify!($name), count!($($param)*))?;
- $( state.serialize_field(stringify!($param), &ser_url!(self, $param))?; )*
- state.end()
- }
+ $( $(#[$pattr])* pub $param : $type, )*
}
);
}
@@ -54,15 +31,24 @@ impl_extra!(LiteralBlock { space: FixedSpace });
impl_extra!(DoctestBlock { space: FixedSpace });
impl_extra!(SubstitutionDefinition { ltrim: Option<bool>, rtrim: Option<bool> });
impl_extra!(Comment { space: FixedSpace });
-impl_extra!(Target { refuri: Option<Url>, refid: Option<ID>, refname: Vec<NameToken>, anonymous: bool });
+impl_extra!(Target {
+ #[serde(serialize_with = "serialize_opt_url")]
+ refuri: Option<Url>,
+ refid: Option<ID>,
+ refname: Vec<NameToken>,
+ anonymous: bool,
+});
impl_extra!(Raw { space: FixedSpace, format: Vec<NameToken> });
-impl_extra!(#[derive(Debug)] Image {
- align: Option<AlignHV>,
+impl_extra!(#[derive(Debug,Serialize)] Image {
+ #[serde(serialize_with = "serialize_url")]
uri: Url,
+ align: Option<AlignHV>,
alt: Option<String>,
height: Option<Measure>,
width: Option<Measure>,
- scale: Option<f64>,
+ scale: Option<u8>,
+ #[serde(serialize_with = "serialize_opt_url")]
+ 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
@@ -78,20 +64,46 @@ impl_extra!(Table {}); //TODO
impl_extra!(OptionArgument { delimiter: Option<String> });
-impl_extra!(Reference { name: Option<String>, refuri: Option<Url>, refid: Option<ID>, refname: Vec<NameToken> });
+impl_extra!(Reference {
+ name: Option<String>,
+ #[serde(serialize_with = "serialize_opt_url")]
+ refuri: Option<Url>,
+ refid: Option<ID>,
+ refname: Vec<NameToken>,
+});
impl_extra!(FootnoteReference { refid: Option<ID>, refname: Vec<NameToken>, auto: Option<bool> });
impl_extra!(CitationReference { refid: Option<ID>, refname: Vec<NameToken> });
impl_extra!(SubstitutionReference { refname: Vec<NameToken> });
impl_extra!(Problematic { refid: Option<ID> });
//also have non-inline versions. Inline image is no figure child, inline target has content
-impl_extra!(TargetInline { refuri: Option<Url>, refid: Option<ID>, refname: Vec<NameToken>, anonymous: bool });
+impl_extra!(TargetInline {
+ #[serde(serialize_with = "serialize_opt_url")]
+ refuri: Option<Url>,
+ refid: Option<ID>,
+ refname: Vec<NameToken>,
+ anonymous: bool,
+});
impl_extra!(RawInline { space: FixedSpace, format: Vec<NameToken> });
-impl_extra!(#[derive(Debug)] ImageInline {
- align: Option<AlignHV>,
+impl_extra!(#[derive(Debug,Serialize)] ImageInline {
+ #[serde(serialize_with = "serialize_url")]
uri: Url,
+ align: Option<AlignHV>,
alt: Option<String>,
height: Option<Measure>,
width: Option<Measure>,
- scale: Option<f64>,
+ scale: Option<u8>,
+ #[serde(serialize_with = "serialize_opt_url")]
+ target: Option<Url>, // Not part of the DTD but a valid argument
});
+impl ImageInline {
+ pub fn new(uri: Url) -> ImageInline { ImageInline {
+ uri: uri,
+ align: None,
+ alt: None,
+ height: None,
+ width: None,
+ scale: None,
+ target: None,
+ } }
+}
diff --git a/src/document_tree/serde_util.rs b/src/document_tree/serde_util.rs
new file mode 100644
index 0000000..2e24309
--- /dev/null
+++ b/src/document_tree/serde_util.rs
@@ -0,0 +1,12 @@
+use url::Url;
+
+pub fn serialize_url<S>(url: &Url, serializer: S) -> Result<S::Ok, S::Error> where S: serde::ser::Serializer {
+ serializer.serialize_str(url.as_str())
+}
+
+pub 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(),
+ }
+}
diff --git a/src/parser/conversion.rs b/src/parser/conversion.rs
index 22a6ebe..e59098e 100644
--- a/src/parser/conversion.rs
+++ b/src/parser/conversion.rs
@@ -1,14 +1,14 @@
use url::Url;
-use failure::Error;
+use failure::{Error,bail};
use failure_derive::Fail;
use pest::iterators::{Pairs,Pair};
use crate::document_tree::{
- HasChildren,
+ Element,HasChildren,ExtraAttributes,
elements as e,
element_categories as c,
attribute_types::ID,
- extra_attributes,
+ extra_attributes as a,
};
use super::pest_rst::Rule;
@@ -31,10 +31,11 @@ pub fn convert_document(pairs: Pairs<Rule>) -> Result<e::Document, Error> {
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(e::Paragraph::with_children(vec![pair.as_str().into()]).into()),
- Rule::target => Ok(convert_target(pair)?.into()),
- Rule::admonition_gen => Ok(convert_admonition_gen(pair)?.into()),
+ Rule::title => Ok(convert_title(pair).into()),
+ Rule::paragraph => Ok(e::Paragraph::with_children(vec![pair.as_str().into()]).into()),
+ Rule::target => Ok(convert_target(pair)?.into()),
+ Rule::substitution_def => Ok(convert_substitution_def(pair)?.into()),
+ Rule::admonition_gen => Ok(convert_admonition_gen(pair)?.into()),
rule => Err(ConversionError::UnknownRuleError { rule }.into()),
}
}
@@ -57,7 +58,7 @@ fn convert_title(pair: Pair<Rule>) -> e::Title {
}
fn convert_target(pair: Pair<Rule>) -> Result<e::Target, Error> {
- let mut attrs = extra_attributes::Target {
+ let mut attrs = a::Target {
anonymous: false,
..Default::default()
};
@@ -72,6 +73,46 @@ fn convert_target(pair: Pair<Rule>) -> Result<e::Target, Error> {
Ok(e::Target::new(Default::default(), attrs))
}
+fn convert_substitution_def(pair: Pair<Rule>) -> Result<e::SubstitutionDefinition, Error> {
+ let mut pairs = pair.into_inner();
+ let name = pairs.next().unwrap().as_str(); // Rule::substitution_name
+ let inner_pair = pairs.next().unwrap();
+ let inner: c::TextOrInlineElement = match inner_pair.as_rule() {
+ Rule::image => convert_image_inline(inner_pair)?.into(),
+ rule => panic!("Unknown substitution rule {:?}", rule),
+ };
+ let mut subst_def = e::SubstitutionDefinition::with_children(vec![inner.into()]);
+ subst_def.names_mut().push(name.to_owned());
+ Ok(subst_def)
+}
+
+fn convert_image_inline(pair: Pair<Rule>) -> Result<e::ImageInline, Error> {
+ let mut pairs = pair.into_inner();
+ let mut image = e::ImageInline::with_extra(a::ImageInline::new(
+ pairs.next().unwrap().as_str().parse()?, // line
+ ));
+ if let Some(opt_block) = pairs.next() { // image_opt_block
+ let options = opt_block.into_inner();
+ for opt in options {
+ let mut opt_iter = opt.into_inner();
+ let opt_name = opt_iter.next().unwrap();
+ let opt_val = opt_iter.next().unwrap().as_str();
+ match opt_name.as_str() {
+ "class" => image.classes_mut().push(opt_val.to_owned()),
+ "name" => image.names_mut().push(opt_val.to_owned()),
+ "alt" => image.extra_mut().alt = Some(opt_val.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(opt_val.parse()?), // TODO: can end with %
+ "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_admonition_gen(pair: Pair<Rule>) -> Result<c::BodyElement, Error> {
let mut iter = pair.into_inner();
let typ = iter.next().unwrap().as_str();
diff --git a/src/parser/tests.rs b/src/parser/tests.rs
index 74d741d..7c5c114 100644
--- a/src/parser/tests.rs
+++ b/src/parser/tests.rs
@@ -106,6 +106,9 @@ fn admonitions() {
};
}
+// TODO: substitutions
+// TODO: images
+
#[test]
fn nested_lists() {
parses_to! {