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
website.rs 9.08 KiB
Newer Older
  • Learn to ignore specific revisions
  • use super::page::{Page, PageTemplate};
    
    peterrabbit's avatar
    peterrabbit committed
    use crate::app::AppConfig;
    
    use crate::static_files::StaticFilesManager;
    
    use serde::ser::{SerializeStruct, Serializer};
    
    peterrabbit's avatar
    peterrabbit committed
    use serde::{Deserialize, Serialize};
    
    use std::io::prelude::*;
    
    peterrabbit's avatar
    peterrabbit committed
    use std::path::PathBuf;
    
    peterrabbit's avatar
    peterrabbit committed
    
    #[derive(Debug, Serialize, Deserialize, Clone)]
    
    Pierre Jarriges's avatar
    Pierre Jarriges committed
    pub struct WebSiteData {
    
        root_page: Page,
        #[serde(default = "Vec::new")]
        assets_index: Vec<String>,
        templates: Vec<PageTemplate>,
    }
    
    
    Pierre Jarriges's avatar
    Pierre Jarriges committed
    pub struct WebSiteBuilder(WebSiteData);
    
    
    peterrabbit's avatar
    peterrabbit committed
    pub struct WebSite {
    
    Pierre Jarriges's avatar
    Pierre Jarriges committed
        last_generated_page_id: usize,
    
    peterrabbit's avatar
    peterrabbit committed
        root_page: Page,
    
        pub static_files_manager: StaticFilesManager,
    
    peterrabbit's avatar
    peterrabbit committed
        templates: Vec<PageTemplate>,
    
    peterrabbit's avatar
    peterrabbit committed
    }
    
    
    impl Serialize for WebSite {
        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where
            S: Serializer,
        {
            let mut state = serializer.serialize_struct("WebSite", 3)?;
            state.serialize_field("root_page", &self.root_page)?;
            state.serialize_field("templates", &self.templates)?;
            state.serialize_field("assets_index", &self.static_files_manager.index)?;
            state.end()
        }
    }
    
    
    impl WebSiteBuilder {
        pub fn with_static_files_manager(
            &mut self,
            static_files_manager: StaticFilesManager,
        ) -> WebSite {
            WebSite {
    
    Pierre Jarriges's avatar
    Pierre Jarriges committed
                last_generated_page_id: 0,
                root_page: self.0.root_page.clone(),
    
                static_files_manager: {
                    let mut static_files_manager = static_files_manager;
    
    Pierre Jarriges's avatar
    Pierre Jarriges committed
                    static_files_manager.add_assets_pathes(&self.0.assets_index);
    
    Pierre Jarriges's avatar
    Pierre Jarriges committed
                templates: self.0.templates.clone(),
    
    peterrabbit's avatar
    peterrabbit committed
            }
        }
    
    Pierre Jarriges's avatar
    Pierre Jarriges committed
        pub fn from_json(json: &str) -> Self {
            WebSiteBuilder(serde_json::from_str(json).unwrap())
        }
    
    
        #[cfg(test)]
        pub fn testing(test_dir: &PathBuf) -> WebSite {
            Self::from_json(crate::testing::TEST_JSON_WEBSITE)
                .with_static_files_manager(StaticFilesManager::testing_new(test_dir).unwrap())
        }
    
    
        fn blank_website_template() -> PathBuf {
            std::env::current_dir()
                .unwrap()
                .join("templates")
                .join("new_website.json")
        }
    
        pub fn load(config: &AppConfig) -> Self {
    
            let file_path = match &config.load {
                Some(pth) => pth.clone(),
    
                None => {
                    if let Some(loaded) = Self::try_load_from_existing_file(config) {
                        return loaded;
                    }
    
    Pierre Jarriges's avatar
    Pierre Jarriges committed
    
    
                    Self::blank_website_template()
                }
    
            Self::from_json(&std::fs::read_to_string(file_path).unwrap())
        }
    
        fn try_load_from_existing_file(config: &AppConfig) -> Option<Self> {
            let website_file_path = config.storage_dir.join("website.json");
    
            if website_file_path.exists() {
                match &std::fs::read_to_string(&website_file_path) {
                    Ok(json) => return Some(Self::from_json(&json)),
                    Err(_) => return None,
                }
            }
    
            None
        }
    
    peterrabbit's avatar
    peterrabbit committed
    impl WebSite {
    
        pub fn build(&mut self) -> Result<Self, String> {
            if let Err(err) = self.root_page.build(
    
                &self.templates,
                PathBuf::from("/"),
                &self.static_files_manager,
    
            ) {
                return Err(err);
            };
    
    
    Pierre Jarriges's avatar
    Pierre Jarriges committed
            self.generate_pages_ids();
    
    
            Ok(self.clone())
    
    peterrabbit's avatar
    peterrabbit committed
        }
    
    Pierre Jarriges's avatar
    Pierre Jarriges committed
        pub fn get_all_pages_as_vec(&self) -> Vec<&Page> {
            let mut pages = Vec::new();
            fn collect_pages<'a>(root: &'a Page, vec: &mut Vec<&'a Page>) {
                vec.push(root);
                for p in root.sub_pages.iter() {
                    collect_pages(p, vec);
    
    Pierre Jarriges's avatar
    Pierre Jarriges committed
                }
            }
    
    Pierre Jarriges's avatar
    Pierre Jarriges committed
            collect_pages(&self.root_page, &mut pages);
            pages
    
    Pierre Jarriges's avatar
    Pierre Jarriges committed
        }
    
    
    Pierre Jarriges's avatar
    Pierre Jarriges committed
        fn max_page_id(&self) -> usize {
            self.get_all_pages_as_vec()
                .iter()
                .map(|p| p.get_id())
                .max()
                .unwrap()
        }
    
    Pierre Jarriges's avatar
    Pierre Jarriges committed
    
    
    Pierre Jarriges's avatar
    Pierre Jarriges committed
        fn generate_pages_ids<'a>(&'a mut self) {
            self.last_generated_page_id = self.max_page_id();
    
    Pierre Jarriges's avatar
    Pierre Jarriges committed
    
    
    Pierre Jarriges's avatar
    Pierre Jarriges committed
            fn process_page(page: &mut Page, id: &mut usize) {
                *id += 1;
                page.set_id(*id);
                for sp in page.sub_pages.iter_mut() {
                    process_page(sp, id);
    
    Pierre Jarriges's avatar
    Pierre Jarriges committed
                }
            }
    
    
    Pierre Jarriges's avatar
    Pierre Jarriges committed
            process_page(&mut self.root_page, &mut self.last_generated_page_id);
    
    Pierre Jarriges's avatar
    Pierre Jarriges committed
        }
    
    
        pub fn save(&self, config: &AppConfig) -> std::io::Result<()> {
            let save_json = serde_json::to_string(self).unwrap();
            let json_path = config.storage_dir.join("website.json");
            let mut f = std::fs::OpenOptions::new()
                .write(true)
                .create(true)
                .truncate(true)
                .open(&json_path)?;
    
            f.write_all(save_json.as_bytes())?;
            f.flush()?;
    
            Ok(())
        }
    
    Pierre Jarriges's avatar
    Pierre Jarriges committed
    
        fn find_page(page: &Page, match_id: usize) -> Option<&Page> {
            if page.get_id() == match_id {
                return Some(page);
            }
            for sp in page.sub_pages.iter() {
                if let Some(page) = Self::find_page(sp, match_id) {
                    return Some(page);
                };
            }
            None
        }
    
        fn find_page_mut(page: &mut Page, match_id: usize) -> Option<&mut Page> {
            if page.get_id() == match_id {
                return Some(page);
            }
    
            for sp in page.sub_pages.iter_mut() {
                if let Some(p) = Self::find_page_mut(sp, match_id) {
                    return Some(p);
                };
            }
    
            None
        }
    
        pub fn get_page(&self, id: usize) -> Option<&Page> {
            Self::find_page(&self.root_page, id)
        }
    
    
    Pierre Jarriges's avatar
    Pierre Jarriges committed
        pub fn get_page_mut(&mut self, id: usize) -> Option<&mut Page> {
            Self::find_page_mut(&mut self.root_page, id)
        }
    
    
    Pierre Jarriges's avatar
    Pierre Jarriges committed
        pub fn add_page(&mut self, parent_page_id: usize, page: Page) -> Result<(), String> {
            let id = self.last_generated_page_id + 1;
            let parent_page = Self::find_page_mut(&mut self.root_page, parent_page_id);
            match parent_page {
                Some(pp) => {
                    let mut page = page;
                    page.set_id(id);
                    pp.add_sub_page(page);
                }
                None => {
                    return Err(format!(
                        "Parent page with id {} was not found",
                        parent_page_id
                    ))
                }
            }
    
            self.last_generated_page_id = id;
    
            Ok(())
        }
    
    Pierre Jarriges's avatar
    Pierre Jarriges committed
    
        pub fn update_page(&mut self, id: usize, updated_page: Page) -> Result<&mut Page, String> {
            match self.get_page_mut(id) {
                Some(page) => {
                    *page = updated_page;
                    Ok(page)
                }
                None => Err(format!("Page with id {} was not found", id)),
            }
        }
    
    Pierre Jarriges's avatar
    Pierre Jarriges committed
    }
    
    #[cfg(test)]
    mod test_website {
        use super::*;
    
        fn test_website(pth: &PathBuf) -> WebSite {
            WebSiteBuilder::testing(pth).build().unwrap()
        }
    
        fn remove_test_dir(pth: &PathBuf) {
            let _ = std::fs::remove_dir_all(pth);
        }
    
        #[test]
        fn test_pages_id() {
            let test_dir = &PathBuf::from("./test");
            let ws = test_website(&test_dir);
    
            assert_eq!(ws.last_generated_page_id, 3);
            assert_eq!(ws.max_page_id(), 3);
            assert!(!ws.get_all_pages_as_vec().iter().any(|p| p.get_id() == 0));
            remove_test_dir(&test_dir);
        }
    
        #[test]
        fn test_get_page() {
            let test_dir = &PathBuf::from("./test");
            let ws = test_website(&test_dir);
            let page = ws.get_page(1);
            assert!(page.is_some());
            let page = ws.get_page(2);
            assert!(page.is_some());
            let page = ws.get_page(3);
            assert!(page.is_some());
            let page = ws.get_page(4);
            assert!(page.is_none());
            remove_test_dir(&test_dir);
        }
    
        #[test]
        fn test_add_page() {
            let test_dir = &PathBuf::from("./test");
            let mut ws = test_website(&test_dir);
            let test_new_page: &'static str = "
    {
        \"template_name\": \"TEST TEMPLATE\",
        \"metadata\": {
            \"title\": \"Added page\",
            \"description\": \"Added page descr\",
            \"image\": [\"https://example.com/images/ex_pic.png\"],
            \"css\": [],
            \"js\": [],
            \"url_slug\": \"\",
            \"lang\": \"en\"
        },
        \"body\": [
            {
                \"tag\": \"h1\",
                \"text\": \"Added page\"
            }
        ],
        \"sub_pages\": []
    }
            ";
    
            let new_page: Page = serde_json::from_str(test_new_page).unwrap();
            let added = ws.add_page(1, new_page);
            assert!(added.is_ok());
            let retrieved = ws.get_page(4);
            assert!(retrieved.is_some());
            let retrieved = retrieved.unwrap();
            assert_eq!(retrieved.metadata.title, "Added page");
            remove_test_dir(&test_dir);
        }
    
    Pierre Jarriges's avatar
    Pierre Jarriges committed
    
        #[test]
        fn test_update_page() {
            let test_dir = PathBuf::from("./test");
            let mut ws = test_website(&test_dir);
            let mut root_page = ws.get_page_mut(1).unwrap().clone();
            assert_eq!(root_page.metadata.title, "TEST");
            root_page.metadata.title = String::from("UPDATED");
            let updated = ws.update_page(1, root_page).unwrap();
            assert_eq!(updated.metadata.title, "UPDATED");
    
            let root_page = ws.get_page(1).unwrap();
            assert_eq!(root_page.metadata.title, "UPDATED");
    
            remove_test_dir(&test_dir);
        }