Pour tout problème contactez-nous par mail : support@froggit.fr | La FAQ :grey_question: | Rejoignez-nous sur le Chat :speech_balloon:

Skip to content
Snippets Groups Projects
page.rs 11.7 KiB
Newer Older
peterrabbit's avatar
peterrabbit committed
use super::css::StyleSheet;
Pierre Jarriges's avatar
Pierre Jarriges committed
use super::html::{
    replace_placeholders, HtmlDoc, HtmlElement, CSS_LINK_FRAGMENT, FAVICON_LINK_FRAGMENT,
    IMAGE_LINKS_FRAGMENT, SCRIPT_FRAGMENT,
};
peterrabbit's avatar
peterrabbit committed
use serde::{Deserialize, Serialize};
peterrabbit's avatar
peterrabbit committed
use std::collections::HashMap;
peterrabbit's avatar
peterrabbit committed

#[derive(Debug, Serialize, Deserialize, Clone)]
peterrabbit's avatar
peterrabbit committed
pub struct Page {
peterrabbit's avatar
peterrabbit committed
    pub template_name: String,
    pub body: PageBody,
peterrabbit's avatar
peterrabbit committed
    pub metadata: PageMetadata,
peterrabbit's avatar
peterrabbit committed
    #[serde(default = "Vec::new")]
peterrabbit's avatar
peterrabbit committed
    pub sub_pages: Vec<Page>,
    #[serde(default = "HtmlDoc::new")]
peterrabbit's avatar
peterrabbit committed
    pub html: HtmlDoc,
peterrabbit's avatar
peterrabbit committed
    pub template: Option<PageTemplate>,
peterrabbit's avatar
peterrabbit committed
}

#[derive(Debug, Serialize, Deserialize, Clone)]
peterrabbit's avatar
peterrabbit committed
pub struct PageTemplate {
peterrabbit's avatar
peterrabbit committed
    pub layout: StyleSheet,
    pub name: String,
    pub contents: Vec<HtmlElement>,
impl PageTemplate {
    pub fn get_page_body_placeholder<'a>(
        elements: &'a mut Vec<HtmlElement>,
    ) -> Result<&'a mut HtmlElement, ()> {
        for el in elements.iter_mut() {
            if el.attrs.get("id").unwrap_or(&String::new()).eq("page-body") {
                return Ok(el);
            } else if let Some(children) = &mut el.contents {
                if let Ok(found) = Self::get_page_body_placeholder(children) {
                    return Ok(found);
                }
            }
        }

        return Err(());
    }
}

peterrabbit's avatar
peterrabbit committed
#[derive(Debug, Serialize, Deserialize, Clone)]
peterrabbit's avatar
peterrabbit committed
pub struct PageBody(Vec<HtmlElement>);
peterrabbit's avatar
peterrabbit committed

impl std::fmt::Display for PageBody {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(
            f,
            "{}",
            &self
                .0
                .iter()
                .map(|i| i.to_string())
                .collect::<Vec<String>>()
                .join("")
        )
    }
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct PageMetadata {
    pub title: String,
    pub lang: String,
    pub description: String,
    pub url_slug: String,
peterrabbit's avatar
peterrabbit committed
    #[serde(default = "CSSLinks::new")]
    pub css: CSSLinks,
    #[serde(default = "JSLinks::new")]
    pub js: JSLinks,
Pierre Jarriges's avatar
Pierre Jarriges committed
    #[serde(default = "FaviconLink::new")]
    pub favicon: FaviconLink,
    #[serde(default = "String::new")]
    pub author: String,
    #[serde(default = "ImageLinks::new")]
    pub image: ImageLinks,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct FaviconLink(String);

impl FaviconLink {
    pub fn new() -> Self {
        FaviconLink(String::from("/static/default/favicon.ico"))
    }
}

impl std::fmt::Display for FaviconLink {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(
            f,
            "{}",
            replace_placeholders(FAVICON_LINK_FRAGMENT, {
                let mut map = HashMap::new();
                map.insert("url".to_owned(), self.0.to_owned());
                map
            })
        )
    }
peterrabbit's avatar
peterrabbit committed
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct CSSLinks(Vec<String>);

impl CSSLinks {
    pub fn new() -> Self {
        CSSLinks(vec!["/static/default/style.css".to_owned()])
    }
}

impl std::fmt::Display for CSSLinks {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(
            f,
            "{}",
            &self
                .0
                .iter()
                .map(|url| replace_placeholders(CSS_LINK_FRAGMENT, {
                    let mut map = HashMap::new();
                    map.insert("url".to_string(), url.to_owned());
                    map
                }))
                .collect::<Vec<String>>()
Pierre Jarriges's avatar
Pierre Jarriges committed
                .join("\n")
peterrabbit's avatar
peterrabbit committed
        )
    }
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct JSLinks(Vec<String>);

impl JSLinks {
    pub fn new() -> Self {
        JSLinks(vec!["/static/default/script.js".to_owned()])
    }
}

impl std::fmt::Display for JSLinks {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(
            f,
            "{}",
            &self
                .0
                .iter()
                .map(|url| replace_placeholders(SCRIPT_FRAGMENT, {
                    let mut map = HashMap::new();
                    map.insert("url".to_string(), url.to_owned());
                    map
                }))
                .collect::<Vec<String>>()
Pierre Jarriges's avatar
Pierre Jarriges committed
                .join("\n")
        )
    }
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct ImageLinks(Vec<String>);

impl ImageLinks {
    pub fn new() -> Self {
        ImageLinks(vec![])
    }
}

impl std::fmt::Display for ImageLinks {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(
            f,
            "{}",
            &self
                .0
                .iter()
                .map(|url| replace_placeholders(IMAGE_LINKS_FRAGMENT, {
                    let mut map = HashMap::new();
                    map.insert("url".to_string(), url.to_owned());
                    map
                }))
                .collect::<Vec<String>>()
                .join("\n")
peterrabbit's avatar
peterrabbit committed
        )
    }
peterrabbit's avatar
peterrabbit committed
}

impl Page {
    pub fn build_html(&mut self) {
        self.html = HtmlDoc::from_page(self);
Pierre Jarriges's avatar
Pierre Jarriges committed
    pub fn build_with_template(&mut self, template: PageTemplate) {
peterrabbit's avatar
peterrabbit committed
        let mut complete_body = template.clone();
        self.template = Some(template);

        PageTemplate::get_page_body_placeholder(&mut complete_body.contents)
            .expect("Couldn't find page body container in template")
peterrabbit's avatar
peterrabbit committed
            .set_contents(self.body.0.clone());
peterrabbit's avatar
peterrabbit committed
        self.body.0 = complete_body.contents;
peterrabbit's avatar
peterrabbit committed
    pub fn to_map(&self) -> HashMap<String, String> {
        let mut map = HashMap::new();
        map.insert("title".to_string(), self.metadata.title.to_owned());
        map.insert("lang".to_string(), self.metadata.lang.to_owned());
        map.insert(
            "description".to_string(),
            self.metadata.description.to_owned(),
        );
        map.insert("slug".to_string(), self.metadata.url_slug.to_owned());
        map.insert("body".to_string(), self.body.to_string());
        map.insert("css".to_string(), self.metadata.css.to_string());
        map.insert("js".to_string(), self.metadata.js.to_string());
Pierre Jarriges's avatar
Pierre Jarriges committed
        map.insert("author".to_string(), self.metadata.author.to_string());
        map.insert("image".to_string(), self.metadata.image.to_string());
peterrabbit's avatar
peterrabbit committed

        map
Pierre Jarriges's avatar
Pierre Jarriges committed

#[cfg(test)]
mod test_pages {
    use super::*;
    use crate::website::html::{HtmlAttributes, HtmlElement};

    fn test_page_metadata() -> PageMetadata {
        PageMetadata {
            title: String::from("Test"),
            lang: String::from("en"),
            description: String::from("test descr"),
            url_slug: String::from("test-page"),
            css: CSSLinks(vec![
                "/static/source_code/mystyle.css".to_string(),
                "/static/source_code/mystyle2.css".to_string(),
            ]),
            js: JSLinks(vec![
                "/static/source_code/myscript.js".to_string(),
                "/static/source_code/myscript2.js".to_string(),
            ]),
            favicon: FaviconLink(String::from("/static/images/testicon.ico")),
            author: String::from("test author"),
            image: ImageLinks(vec![
                "/static/images/testimage.png".to_string(),
                "/static/images/testimage2.png".to_string(),
            ]),
        }
    }

    fn test_page_template() -> PageTemplate {
        PageTemplate {
            layout: StyleSheet(HashMap::new()),
            name: String::from("test template"),
            contents: vec![HtmlElement {
                tag: String::from("div"),
                attrs: {
                    let mut attrs = HtmlAttributes::new();
                    attrs
                        .0
                        .insert(String::from("id"), String::from("test-template"));
                    attrs
                        .0
                        .insert(String::from("class"), String::from("page-template"));
                    attrs
                contents: Some(vec![
                    HtmlElement {
                        tag: String::from("nav"),
                        attrs: HtmlAttributes::new(),
                        contents: None,
                        text: Some(String::from("NAV")),
                    HtmlElement {
                        tag: String::from("div"),
                        attrs: {
                            let mut attrs = HtmlAttributes::new();
                            attrs
                                .0
                                .insert(String::from("id"), String::from("page-body"));
                            attrs
                        },
                        contents: None,
                        text: None,
                    },
                    HtmlElement {
                        tag: String::from("footer"),
                        attrs: HtmlAttributes::new(),
                        contents: None,
                        text: Some(String::from("FOOTER")),
                    },
                ]),
                text: None,
            }],
Pierre Jarriges's avatar
Pierre Jarriges committed
        }
    }

    fn test_page() -> Page {
        Page {
            template_name: String::from("test template"),
            body: PageBody(vec![HtmlElement {
                tag: "span".to_string(),
                text: Some("TEST".to_string()),
                contents: None,
                attrs: HtmlAttributes::new(),
            }]),
            metadata: test_page_metadata(),
            sub_pages: Vec::new(),
            html: HtmlDoc::new(),
            template: None,
        }
    }

    #[test]
    fn page_body_to_string() {
        let body = PageBody(vec![
            HtmlElement {
                tag: "span".to_string(),
                text: Some("Hello ".to_string()),
                contents: None,
                attrs: HtmlAttributes::new(),
            },
            HtmlElement {
                tag: "span".to_string(),
                text: Some("World!".to_string()),
                contents: None,
                attrs: HtmlAttributes::new(),
            },
        ]);

        assert_eq!(body.to_string(), "<span>Hello </span><span>World!</span>");
    }

    #[test]
    fn favicon_link_to_string() {
        let pmd = test_page_metadata();
        assert_eq!(
            pmd.favicon.to_string(),
            "<link rel='icon' type='image/*' href='/static/images/testicon.ico'/>"
        )
    }

    #[test]
    fn css_links_to_string() {
        let pmd = test_page_metadata();
        assert_eq!(
            pmd.css.to_string(),
            "<link rel='stylesheet' href='/static/source_code/mystyle.css'>
<link rel='stylesheet' href='/static/source_code/mystyle2.css'>"
        )
    }

    #[test]
    fn js_links_to_string() {
        let pmd = test_page_metadata();
        assert_eq!(
            pmd.js.to_string(),
            "<script src='/static/source_code/myscript.js'></script>
<script src='/static/source_code/myscript2.js'></script>"
        )
    }

    #[test]
    fn images_links_to_string() {
        let pmd = test_page_metadata();
        assert_eq!(
            pmd.image.to_string(),
            "<meta name='image' content='/static/images/testimage.png'/>
<meta property='og:image' content='/static/images/testimage.png'/>
<meta property='twitter:image' content='/static/images/testimage.png'/>
<meta name='image' content='/static/images/testimage2.png'/>
<meta property='og:image' content='/static/images/testimage2.png'/>
<meta property='twitter:image' content='/static/images/testimage2.png'/>"
        )
    }

    #[test]
    fn build_body_with_template() {
        let mut page = test_page();
        page.build_with_template(test_page_template());
        assert_eq!(
            page.body.to_string(),
            "<div id=\"test-template\" class=\"page-template\"><nav>NAV</nav><div id=\"page-body\"><span>TEST</span></div><footer>FOOTER</footer></div>"