use super::page::{Page, PageTemplate}; use crate::app::AppConfig; use crate::static_files::StaticFilesManager; use serde::ser::{SerializeStruct, Serializer}; use serde::{Deserialize, Serialize}; use std::io::prelude::*; use std::path::PathBuf; #[derive(Debug, Serialize, Deserialize, Clone)] pub struct WebSiteData { root_page: Page, #[serde(default = "Vec::new")] assets_index: Vec<String>, templates: Vec<PageTemplate>, } pub struct WebSiteBuilder(WebSiteData); #[derive(Debug, Clone)] pub struct WebSite { last_generated_page_id: usize, root_page: Page, pub static_files_manager: StaticFilesManager, templates: Vec<PageTemplate>, } 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 { last_generated_page_id: 0, root_page: self.0.root_page.clone(), static_files_manager: { let mut static_files_manager = static_files_manager; static_files_manager.add_assets_pathes(&self.0.assets_index); static_files_manager }, templates: self.0.templates.clone(), } } 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; } 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 } } 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); }; self.generate_pages_ids(); Ok(self.clone()) } fn init_last_generated_page_id(&mut self) { let mut ids = vec![self.root_page.get_id()]; fn collect_ids(p: &Page, ids: &mut Vec<usize>) { for sp in p.sub_pages.iter() { ids.push(sp.get_id()); collect_ids(sp, ids); } } collect_ids(&self.root_page, &mut ids); self.last_generated_page_id = *ids.iter().max().unwrap(); } fn generate_pages_ids(&mut self) { self.init_last_generated_page_id(); fn generate_page_id(page: &mut Page, last_generated_page_id: &mut usize) { if page.get_id() == 0 { *last_generated_page_id += 1; page.set_id(*last_generated_page_id); } } fn rec_gen_ids( p: &mut Page, gen_id: fn(&mut Page, &mut usize), last_generated_page_id: &mut usize, ) { gen_id(p, last_generated_page_id); for sp in p.sub_pages.iter_mut() { rec_gen_ids(sp, gen_id, last_generated_page_id) } } rec_gen_ids( &mut self.root_page, generate_page_id, &mut self.last_generated_page_id, ); } 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(()) } }