use super::css::StyleSheet; use serde::{Deserialize, Serialize}; use std::collections::HashMap; #[derive(Debug, Serialize, Deserialize, Clone)] pub struct Item { pub contents: Vec<HtmlElement>, pub layout: StyleSheet, } impl std::fmt::Display for Item { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!( f, "{}", self.contents .iter() .map(|item_content| item_content.to_string()) .collect::<Vec<String>>() .join("") ) } } #[derive(Debug, Serialize, Deserialize, Clone)] pub struct HtmlAttributes(pub HashMap<String, String>); impl HtmlAttributes { pub fn new() -> Self { HtmlAttributes(HashMap::new()) } pub fn get<S: Into<String>>(&self, key: S) -> Option<&String> { self.0.get(&key.into()) } } impl std::fmt::Display for HtmlAttributes { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!( f, "{}", self.0 .iter() .map(|(key, value)| format!(" {}=\"{}\"", key, value)) .collect::<Vec<String>>() .join("") ) } } #[derive(Debug, Serialize, Deserialize, Clone)] pub struct HtmlElement { pub tag: String, pub text: Option<String>, pub contents: Option<Vec<HtmlElement>>, #[serde(default = "HtmlAttributes::new")] pub attrs: HtmlAttributes, } impl std::fmt::Display for HtmlElement { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { let body = match &self.contents { Some(contents) => contents .iter() .map(|item| item.to_string()) .collect::<Vec<String>>() .join(""), None => match &self.text { Some(text) => text.to_owned(), None => String::new(), }, }; write!(f, "<{}{}>{}</{}>", self.tag, self.attrs, body, self.tag) } } #[cfg(test)] mod test_items { use super::*; use std::collections::HashMap; #[test] fn text_item_content_to_string() { let item_content = HtmlElement { tag: String::from("p"), text: Some(String::from("Hello")), contents: None, attrs: HtmlAttributes::new(), }; assert_eq!(item_content.to_string(), "<p>Hello</p>") } #[test] fn item_content_with_attrs_to_string() { let mut attrs = HtmlAttributes::new(); attrs.0.insert(String::from("id"), String::from("some-id")); let mut item_content = HtmlElement { tag: String::from("p"), text: Some(String::from("Hello")), contents: None, attrs: attrs.clone(), }; assert_eq!(item_content.to_string(), "<p id=\"some-id\">Hello</p>"); item_content .attrs .0 .insert(String::from("class"), String::from("some-class")); assert_eq!( item_content.to_string(), "<p id=\"some-id\" class=\"some-class\">Hello</p>" ); } #[test] fn complex_item_content_to_string() { let item_content = HtmlElement { tag: String::from("p"), text: None, attrs: HtmlAttributes::new(), contents: Some(vec![ HtmlElement { tag: String::from("span"), text: Some(String::from("Hello ")), contents: None, attrs: HtmlAttributes::new(), }, HtmlElement { tag: String::from("b"), text: Some(String::from("World")), contents: None, attrs: HtmlAttributes::new(), }, ]), }; assert_eq!( item_content.to_string(), "<p><span>Hello </span><b>World</b></p>" ) } #[test] fn item_to_string() { let item = Item { layout: StyleSheet(HashMap::new()), contents: vec![ HtmlElement { tag: String::from("span"), text: Some(String::from("Hello ")), contents: None, attrs: HtmlAttributes::new(), }, HtmlElement { tag: String::from("b"), text: Some(String::from("World")), contents: None, attrs: HtmlAttributes::new(), }, ], }; assert_eq!(item.to_string(), "<span>Hello </span><b>World</b>") } }