From 5d479ca112cde06df56e1f97554c2363a25998af Mon Sep 17 00:00:00 2001 From: peterrabbit <peterrabbit@msi.home> Date: Tue, 30 Aug 2022 18:49:19 +0200 Subject: [PATCH] wip build page body from template --- example.json | 8 ++- src/website/{html.rs => html_doc.rs} | 2 +- src/website/item.rs | 100 +++++++++++++++++++++------ src/website/mod.rs | 2 +- src/website/page.rs | 19 +++-- src/website/website.rs | 6 +- templates/new_website.json | 12 ++-- 7 files changed, 113 insertions(+), 36 deletions(-) rename src/website/{html.rs => html_doc.rs} (97%) diff --git a/example.json b/example.json index 09c95cf..c54e4b9 100644 --- a/example.json +++ b/example.json @@ -23,11 +23,15 @@ { "tag": "p", "text": "Hello Pijar<br />Oui oui oui.", - "class": "pijar_p_class" + "attrs": { + "class": "pijar_p_class" + } }, { "tag": "img", - "src": "/img/url.png" + "attrs": { + "src": "/img/url.png" + } } ] } diff --git a/src/website/html.rs b/src/website/html_doc.rs similarity index 97% rename from src/website/html.rs rename to src/website/html_doc.rs index 5bdb938..06a7d82 100644 --- a/src/website/html.rs +++ b/src/website/html_doc.rs @@ -43,7 +43,7 @@ impl HtmlDoc { HtmlDoc(html) } - pub fn empty() -> Self { + pub fn new() -> Self { HtmlDoc(String::new()) } } diff --git a/src/website/item.rs b/src/website/item.rs index 639e975..69a80e4 100644 --- a/src/website/item.rs +++ b/src/website/item.rs @@ -1,10 +1,11 @@ use super::css::StyleSheet; use serde::{Deserialize, Serialize}; +use std::collections::HashMap; #[derive(Debug, Serialize, Deserialize, Clone)] pub struct Item { - contents: Vec<ItemContent>, - layout: StyleSheet, + pub contents: Vec<HtmlElement>, + pub layout: StyleSheet, } impl std::fmt::Display for Item { @@ -14,7 +15,7 @@ impl std::fmt::Display for Item { "{}", self.contents .iter() - .map(|ic| ic.to_string()) + .map(|item_content| item_content.to_string()) .collect::<Vec<String>>() .join("") ) @@ -22,13 +23,42 @@ impl std::fmt::Display for Item { } #[derive(Debug, Serialize, Deserialize, Clone)] -pub struct ItemContent { - tag: String, - text: Option<String>, - contents: Option<Vec<ItemContent>>, +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 ItemContent { +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 @@ -36,14 +66,13 @@ impl std::fmt::Display for ItemContent { .map(|item| item.to_string()) .collect::<Vec<String>>() .join(""), - None => self - .text - .as_ref() - .expect("Either contents or text field must be provided") - .to_string(), + None => match &self.text { + Some(text) => text.to_owned(), + None => String::new(), + }, }; - write!(f, "<{}>{}</{}>", self.tag, body, self.tag) + write!(f, "<{}{}>{}</{}>", self.tag, self.attrs, body, self.tag) } } @@ -54,30 +83,59 @@ mod test_items { #[test] fn text_item_content_to_string() { - let item_content = ItemContent { + 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 = ItemContent { + let item_content = HtmlElement { tag: String::from("p"), text: None, + attrs: HtmlAttributes::new(), contents: Some(vec![ - ItemContent { + HtmlElement { tag: String::from("span"), text: Some(String::from("Hello ")), contents: None, + attrs: HtmlAttributes::new(), }, - ItemContent { + HtmlElement { tag: String::from("b"), text: Some(String::from("World")), contents: None, + attrs: HtmlAttributes::new(), }, ]), }; @@ -93,15 +151,17 @@ mod test_items { let item = Item { layout: StyleSheet(HashMap::new()), contents: vec![ - ItemContent { + HtmlElement { tag: String::from("span"), text: Some(String::from("Hello ")), contents: None, + attrs: HtmlAttributes::new(), }, - ItemContent { + HtmlElement { tag: String::from("b"), text: Some(String::from("World")), contents: None, + attrs: HtmlAttributes::new(), }, ], }; diff --git a/src/website/mod.rs b/src/website/mod.rs index 3f286a7..0fd4f5b 100644 --- a/src/website/mod.rs +++ b/src/website/mod.rs @@ -1,5 +1,5 @@ mod css; -mod html; +mod html_doc; mod item; mod page; mod website; diff --git a/src/website/page.rs b/src/website/page.rs index c38aa78..424f954 100644 --- a/src/website/page.rs +++ b/src/website/page.rs @@ -1,5 +1,5 @@ use super::css::StyleSheet; -use super::html::HtmlDoc; +use super::html_doc::HtmlDoc; use super::item::*; use serde::{Deserialize, Serialize}; @@ -10,7 +10,7 @@ pub struct Page { pub metadata: PageMetadata, #[serde(default = "Vec::new")] pub sub_pages: Vec<Page>, - #[serde(default = "HtmlDoc::empty")] + #[serde(default = "HtmlDoc::new")] pub html: HtmlDoc, pub template: Option<PageTemplate>, } @@ -19,7 +19,7 @@ pub struct Page { pub struct PageTemplate { pub layout: StyleSheet, pub name: String, - pub contents: Vec<ItemContent>, + pub contents: Vec<HtmlElement>, } #[derive(Debug, Serialize, Deserialize, Clone)] @@ -55,10 +55,17 @@ impl Page { self.html = HtmlDoc::from_page(self); } - pub fn build_body_template(&mut self, template: &PageTemplate) { - self.template = Some(template.clone()); + pub fn build_body_template(&mut self, template: PageTemplate) { + let mut body = template.clone(); + self.template = Some(template); + let err = "Couldn't find page-body placeholder"; + let body_container = body + .contents + .iter() + .find(|el| el.attrs.get("id").expect(err).eq("page-body")) + .expect(err); + // HINT use HashMap::extend() to insert many values // TODO concat template with page body (template should have a page body placeholder) - unimplemented!() } pub fn text_from_key(&self, key: String) -> String { diff --git a/src/website/website.rs b/src/website/website.rs index de26d59..2645eaa 100644 --- a/src/website/website.rs +++ b/src/website/website.rs @@ -32,7 +32,8 @@ impl WebSite { obj.templates .iter() .find(|t| t.name == obj.root_page.template_name) - .expect("Page template not found"), + .expect("Page template not found") + .clone(), ); for p in obj.root_page.sub_pages.iter_mut() { p.build_html(); @@ -40,7 +41,8 @@ impl WebSite { obj.templates .iter() .find(|t| t.name == p.template_name) - .expect("Page template not found"), + .expect("Page template not found") + .clone(), ); } obj diff --git a/templates/new_website.json b/templates/new_website.json index 749cbd9..58d9d3d 100644 --- a/templates/new_website.json +++ b/templates/new_website.json @@ -27,12 +27,16 @@ "layout": {}, "contents": [ { - "tag": "nav", - "contents": [] + "tag": "nav" }, { - "tag": "footer", - "contents": [] + "tag": "div", + "attrs": { + "id": "page-body" + } + }, + { + "tag": "footer" } ] } -- GitLab