diff --git a/src/xhtml.rs b/src/xhtml.rs index 43ea9e61..76610936 100644 --- a/src/xhtml.rs +++ b/src/xhtml.rs @@ -18,7 +18,7 @@ type Lang = String; #[derive(Debug, Clone)] pub struct XhtmlIm { /// Map of language to body element. - bodies: HashMap, + bodies: HashMap, } impl XhtmlIm { @@ -27,22 +27,41 @@ impl XhtmlIm { let mut html = Vec::new(); // TODO: use the best language instead. for (lang, body) in self.bodies { - if let Tag::Body { style: _, xml_lang, children } = body { - if lang.is_empty() { - assert!(xml_lang.is_none()); - } else { - assert_eq!(Some(lang), xml_lang); - } - for tag in children { - html.push(tag.to_html()); - } - break; + if lang.is_empty() { + assert!(body.xml_lang.is_none()); } else { - unreachable!(); + assert_eq!(Some(lang), body.xml_lang); } + for tag in body.children { + html.push(tag.to_html()); + } + break; } 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 {} @@ -62,7 +81,7 @@ impl TryFrom for XhtmlIm { Some(lang) => lang, None => "", }.to_string(); - let body = Tag::try_from(child)?; + let body = Body::try_from(child)?; match bodies.insert(lang, body) { None => (), Some(_) => return Err(Error::ParseError("Two identical language bodies found in XHTML-IM.")) @@ -81,16 +100,12 @@ impl From for Element { Element::builder("html") .ns(ns::XHTML_IM) .append(wrapper.bodies.into_iter().map(|(ref lang, ref body)| { - if let Tag::Body { style, xml_lang, children } = body { - assert_eq!(Some(lang), xml_lang.as_ref()); - Element::builder("body") - .ns(ns::XHTML_IM) - .attr("style", get_style_string(style.clone())) - .attr("xml:lang", xml_lang.clone()) - .append(children_to_nodes(children.clone())) - } else { - unreachable!(); - } + assert_eq!(Some(lang), body.xml_lang.as_ref()); + Element::builder("body") + .ns(ns::XHTML_IM) + .attr("style", get_style_string(body.style.clone())) + .attr("xml:lang", body.xml_lang.clone()) + .append(children_to_nodes(body.children.clone())) }).collect::>()) .build() } @@ -130,11 +145,45 @@ fn get_style_string(style: Css) -> Option { Some(result.join("; ")) } +#[derive(Debug, Clone)] +struct Body { + style: Css, + xml_lang: Option, + children: Vec, +} + +impl TryFrom for Body { + type Error = Error; + + fn try_from(elem: Element) -> Result { + 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 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)] enum Tag { A { href: Option, style: Css, type_: Option, children: Vec }, Blockquote { style: Css, children: Vec }, - Body { style: Css, xml_lang: Option, children: Vec }, Br, Cite { style: Css, children: Vec }, Em { children: Vec }, @@ -161,10 +210,6 @@ impl Tag { let style = write_attr(get_style_string(style), "style"); format!("{}", style, children_to_html(children)) }, - Tag::Body { style, xml_lang: _, children } => { - let style = write_attr(get_style_string(style), "style"); - format!("{}", style, children_to_html(children)) - }, Tag::Br => String::from("
"), Tag::Cite { style, children } => { let style = write_attr(get_style_string(style), "style"); @@ -218,7 +263,6 @@ impl TryFrom for Tag { 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 }, "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, "cite" => Tag::Cite { style: parse_css(elem.attr("style")), children }, "em" => Tag::Em { children }, @@ -254,16 +298,6 @@ impl From for Element { Some(style) => vec![("style", style)], None => vec![], }, 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::Cite { style, children } => ("cite", match get_style_string(style) { Some(style) => vec![("style", style)], @@ -405,26 +439,17 @@ mod tests { let elem: Element = "" .parse() .unwrap(); - let body = Tag::try_from(elem).unwrap(); - match body { - Tag::Body { style: _, xml_lang: _, children } => assert_eq!(children.len(), 0), - _ => panic!(), - } + let body = Body::try_from(elem).unwrap(); + assert_eq!(body.children.len(), 0); let elem: Element = "

Hello world!

" .parse() .unwrap(); - let body = Tag::try_from(elem).unwrap(); - let mut children = match body { - Tag::Body { style, xml_lang, children } => { - assert_eq!(style.len(), 0); - assert_eq!(xml_lang, None); - assert_eq!(children.len(), 1); - children - }, - _ => panic!(), - }; - let p = match children.pop() { + let mut body = Body::try_from(elem).unwrap(); + assert_eq!(body.style.len(), 0); + assert_eq!(body.xml_lang, None); + assert_eq!(body.children.len(), 1); + let p = match body.children.pop() { Some(Child::Tag(tag)) => tag, _ => panic!(), };