xhtml: Move Body out of Tag, because it is the only top-level element.
This commit is contained in:
parent
2f45d586b5
commit
63d0265284
1 changed files with 80 additions and 55 deletions
135
src/xhtml.rs
135
src/xhtml.rs
|
|
@ -18,7 +18,7 @@ type Lang = String;
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct XhtmlIm {
|
pub struct XhtmlIm {
|
||||||
/// Map of language to body element.
|
/// Map of language to body element.
|
||||||
bodies: HashMap<Lang, Tag>,
|
bodies: HashMap<Lang, Body>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl XhtmlIm {
|
impl XhtmlIm {
|
||||||
|
|
@ -27,22 +27,41 @@ impl XhtmlIm {
|
||||||
let mut html = Vec::new();
|
let mut html = Vec::new();
|
||||||
// TODO: use the best language instead.
|
// TODO: use the best language instead.
|
||||||
for (lang, body) in self.bodies {
|
for (lang, body) in self.bodies {
|
||||||
if let Tag::Body { style: _, xml_lang, children } = body {
|
if lang.is_empty() {
|
||||||
if lang.is_empty() {
|
assert!(body.xml_lang.is_none());
|
||||||
assert!(xml_lang.is_none());
|
|
||||||
} else {
|
|
||||||
assert_eq!(Some(lang), xml_lang);
|
|
||||||
}
|
|
||||||
for tag in children {
|
|
||||||
html.push(tag.to_html());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
} else {
|
} else {
|
||||||
unreachable!();
|
assert_eq!(Some(lang), body.xml_lang);
|
||||||
}
|
}
|
||||||
|
for tag in body.children {
|
||||||
|
html.push(tag.to_html());
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
html.concat()
|
html.concat()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Removes all unknown elements.
|
||||||
|
pub fn flatten(self) -> XhtmlIm {
|
||||||
|
let mut bodies = HashMap::new();
|
||||||
|
for (lang, body) in self.bodies {
|
||||||
|
let children = body.children.into_iter().fold(vec![], |mut acc, child| {
|
||||||
|
match child {
|
||||||
|
Child::Tag(Tag::Unknown(children)) => acc.extend(children),
|
||||||
|
any => acc.push(any),
|
||||||
|
}
|
||||||
|
acc
|
||||||
|
});
|
||||||
|
let body = Body {
|
||||||
|
style: body.style,
|
||||||
|
xml_lang: body.xml_lang,
|
||||||
|
children,
|
||||||
|
};
|
||||||
|
bodies.insert(lang, body);
|
||||||
|
}
|
||||||
|
XhtmlIm {
|
||||||
|
bodies,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MessagePayload for XhtmlIm {}
|
impl MessagePayload for XhtmlIm {}
|
||||||
|
|
@ -62,7 +81,7 @@ impl TryFrom<Element> for XhtmlIm {
|
||||||
Some(lang) => lang,
|
Some(lang) => lang,
|
||||||
None => "",
|
None => "",
|
||||||
}.to_string();
|
}.to_string();
|
||||||
let body = Tag::try_from(child)?;
|
let body = Body::try_from(child)?;
|
||||||
match bodies.insert(lang, body) {
|
match bodies.insert(lang, body) {
|
||||||
None => (),
|
None => (),
|
||||||
Some(_) => return Err(Error::ParseError("Two identical language bodies found in XHTML-IM."))
|
Some(_) => return Err(Error::ParseError("Two identical language bodies found in XHTML-IM."))
|
||||||
|
|
@ -81,16 +100,12 @@ impl From<XhtmlIm> for Element {
|
||||||
Element::builder("html")
|
Element::builder("html")
|
||||||
.ns(ns::XHTML_IM)
|
.ns(ns::XHTML_IM)
|
||||||
.append(wrapper.bodies.into_iter().map(|(ref lang, ref body)| {
|
.append(wrapper.bodies.into_iter().map(|(ref lang, ref body)| {
|
||||||
if let Tag::Body { style, xml_lang, children } = body {
|
assert_eq!(Some(lang), body.xml_lang.as_ref());
|
||||||
assert_eq!(Some(lang), xml_lang.as_ref());
|
Element::builder("body")
|
||||||
Element::builder("body")
|
.ns(ns::XHTML_IM)
|
||||||
.ns(ns::XHTML_IM)
|
.attr("style", get_style_string(body.style.clone()))
|
||||||
.attr("style", get_style_string(style.clone()))
|
.attr("xml:lang", body.xml_lang.clone())
|
||||||
.attr("xml:lang", xml_lang.clone())
|
.append(children_to_nodes(body.children.clone()))
|
||||||
.append(children_to_nodes(children.clone()))
|
|
||||||
} else {
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
}).collect::<Vec<_>>())
|
}).collect::<Vec<_>>())
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
@ -130,11 +145,45 @@ fn get_style_string(style: Css) -> Option<String> {
|
||||||
Some(result.join("; "))
|
Some(result.join("; "))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct Body {
|
||||||
|
style: Css,
|
||||||
|
xml_lang: Option<String>,
|
||||||
|
children: Vec<Child>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<Element> for Body {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn try_from(elem: Element) -> Result<Body, Error> {
|
||||||
|
let mut children = vec![];
|
||||||
|
for child in elem.nodes() {
|
||||||
|
match child {
|
||||||
|
Node::Element(child) => children.push(Child::Tag(Tag::try_from(child.clone())?)),
|
||||||
|
Node::Text(text) => children.push(Child::Text(text.clone())),
|
||||||
|
Node::Comment(_) => unimplemented!() // XXX: remove!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Body { style: parse_css(elem.attr("style")), xml_lang: elem.attr("xml:lang").map(|xml_lang| xml_lang.to_string()), children })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Body> for Element {
|
||||||
|
fn from(body: Body) -> Element {
|
||||||
|
Element::builder("body")
|
||||||
|
.ns(ns::XHTML)
|
||||||
|
.attr("style", get_style_string(body.style))
|
||||||
|
.attr("xml:lang", body.xml_lang)
|
||||||
|
.append(children_to_nodes(body.children))
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
enum Tag {
|
enum Tag {
|
||||||
A { href: Option<String>, style: Css, type_: Option<String>, children: Vec<Child> },
|
A { href: Option<String>, style: Css, type_: Option<String>, children: Vec<Child> },
|
||||||
Blockquote { style: Css, children: Vec<Child> },
|
Blockquote { style: Css, children: Vec<Child> },
|
||||||
Body { style: Css, xml_lang: Option<String>, children: Vec<Child> },
|
|
||||||
Br,
|
Br,
|
||||||
Cite { style: Css, children: Vec<Child> },
|
Cite { style: Css, children: Vec<Child> },
|
||||||
Em { children: Vec<Child> },
|
Em { children: Vec<Child> },
|
||||||
|
|
@ -161,10 +210,6 @@ impl Tag {
|
||||||
let style = write_attr(get_style_string(style), "style");
|
let style = write_attr(get_style_string(style), "style");
|
||||||
format!("<blockquote{}>{}</blockquote>", style, children_to_html(children))
|
format!("<blockquote{}>{}</blockquote>", style, children_to_html(children))
|
||||||
},
|
},
|
||||||
Tag::Body { style, xml_lang: _, children } => {
|
|
||||||
let style = write_attr(get_style_string(style), "style");
|
|
||||||
format!("<body{}>{}</body>", style, children_to_html(children))
|
|
||||||
},
|
|
||||||
Tag::Br => String::from("<br>"),
|
Tag::Br => String::from("<br>"),
|
||||||
Tag::Cite { style, children } => {
|
Tag::Cite { style, children } => {
|
||||||
let style = write_attr(get_style_string(style), "style");
|
let style = write_attr(get_style_string(style), "style");
|
||||||
|
|
@ -218,7 +263,6 @@ impl TryFrom<Element> for Tag {
|
||||||
Ok(match elem.name() {
|
Ok(match elem.name() {
|
||||||
"a" => Tag::A { href: elem.attr("href").map(|href| href.to_string()), style: parse_css(elem.attr("style")), type_: elem.attr("type").map(|type_| type_.to_string()), children },
|
"a" => Tag::A { href: elem.attr("href").map(|href| href.to_string()), style: parse_css(elem.attr("style")), type_: elem.attr("type").map(|type_| type_.to_string()), children },
|
||||||
"blockquote" => Tag::Blockquote { style: parse_css(elem.attr("style")), children },
|
"blockquote" => Tag::Blockquote { style: parse_css(elem.attr("style")), children },
|
||||||
"body" => Tag::Body { style: parse_css(elem.attr("style")), xml_lang: elem.attr("xml:lang").map(|xml_lang| xml_lang.to_string()), children },
|
|
||||||
"br" => Tag::Br,
|
"br" => Tag::Br,
|
||||||
"cite" => Tag::Cite { style: parse_css(elem.attr("style")), children },
|
"cite" => Tag::Cite { style: parse_css(elem.attr("style")), children },
|
||||||
"em" => Tag::Em { children },
|
"em" => Tag::Em { children },
|
||||||
|
|
@ -254,16 +298,6 @@ impl From<Tag> for Element {
|
||||||
Some(style) => vec![("style", style)],
|
Some(style) => vec![("style", style)],
|
||||||
None => vec![],
|
None => vec![],
|
||||||
}, children),
|
}, children),
|
||||||
Tag::Body { style, xml_lang, children } => ("body", {
|
|
||||||
let mut attrs = vec![];
|
|
||||||
if let Some(style) = get_style_string(style) {
|
|
||||||
attrs.push(("style", style));
|
|
||||||
}
|
|
||||||
if let Some(xml_lang) = xml_lang {
|
|
||||||
attrs.push(("xml:lang", xml_lang));
|
|
||||||
}
|
|
||||||
attrs
|
|
||||||
}, children),
|
|
||||||
Tag::Br => ("br", vec![], vec![]),
|
Tag::Br => ("br", vec![], vec![]),
|
||||||
Tag::Cite { style, children } => ("cite", match get_style_string(style) {
|
Tag::Cite { style, children } => ("cite", match get_style_string(style) {
|
||||||
Some(style) => vec![("style", style)],
|
Some(style) => vec![("style", style)],
|
||||||
|
|
@ -405,26 +439,17 @@ mod tests {
|
||||||
let elem: Element = "<body xmlns='http://www.w3.org/1999/xhtml'/>"
|
let elem: Element = "<body xmlns='http://www.w3.org/1999/xhtml'/>"
|
||||||
.parse()
|
.parse()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let body = Tag::try_from(elem).unwrap();
|
let body = Body::try_from(elem).unwrap();
|
||||||
match body {
|
assert_eq!(body.children.len(), 0);
|
||||||
Tag::Body { style: _, xml_lang: _, children } => assert_eq!(children.len(), 0),
|
|
||||||
_ => panic!(),
|
|
||||||
}
|
|
||||||
|
|
||||||
let elem: Element = "<body xmlns='http://www.w3.org/1999/xhtml'><p>Hello world!</p></body>"
|
let elem: Element = "<body xmlns='http://www.w3.org/1999/xhtml'><p>Hello world!</p></body>"
|
||||||
.parse()
|
.parse()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let body = Tag::try_from(elem).unwrap();
|
let mut body = Body::try_from(elem).unwrap();
|
||||||
let mut children = match body {
|
assert_eq!(body.style.len(), 0);
|
||||||
Tag::Body { style, xml_lang, children } => {
|
assert_eq!(body.xml_lang, None);
|
||||||
assert_eq!(style.len(), 0);
|
assert_eq!(body.children.len(), 1);
|
||||||
assert_eq!(xml_lang, None);
|
let p = match body.children.pop() {
|
||||||
assert_eq!(children.len(), 1);
|
|
||||||
children
|
|
||||||
},
|
|
||||||
_ => panic!(),
|
|
||||||
};
|
|
||||||
let p = match children.pop() {
|
|
||||||
Some(Child::Tag(tag)) => tag,
|
Some(Child::Tag(tag)) => tag,
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue