use std::collections::{HashMap, HashSet}; use std::path::{Path, PathBuf}; use slotmap::{DenseSlotMap, Key}; use front_matter::SortBy; use sorting::{find_siblings, sort_pages_by_weight, sort_pages_by_date}; use content::{Page, Section}; /// Houses everything about pages and sections /// Think of it as a database where each page and section has an id (Key here) /// that can be used to find the actual value /// Sections and pages can then refer to other elements by those keys, which are very cheap to /// copy. /// We can assume the keys are always existing as removing a page/section deletes all references /// to that key. #[derive(Debug)] pub struct Library { /// All the pages of the site pages: DenseSlotMap, /// All the sections of the site sections: DenseSlotMap
, /// A mapping path -> key for pages so we can easily get their key paths_to_pages: HashMap, /// A mapping path -> key for sections so we can easily get their key paths_to_sections: HashMap, } impl Library { pub fn new(cap_pages: usize, cap_sections: usize) -> Self { Library { pages: DenseSlotMap::with_capacity(cap_pages), sections: DenseSlotMap::with_capacity(cap_sections), paths_to_pages: HashMap::with_capacity(cap_pages), paths_to_sections: HashMap::with_capacity(cap_sections), } } /// Add a section and return its Key pub fn insert_section(&mut self, section: Section) -> Key { let path = section.file.path.clone(); let key = self.sections.insert(section); self.paths_to_sections.insert(path, key); key } /// Add a page and return its Key pub fn insert_page(&mut self, page: Page) -> Key { let path = page.file.path.clone(); let key = self.pages.insert(page); self.paths_to_pages.insert(path, key); key } pub fn pages(&self) -> &DenseSlotMap { &self.pages } pub fn pages_mut(&mut self) -> &mut DenseSlotMap { &mut self.pages } pub fn pages_values(&self) -> Vec<&Page> { self.pages.values().collect::>() } pub fn sections(&self) -> &DenseSlotMap
{ &self.sections } pub fn sections_mut(&mut self) -> &mut DenseSlotMap
{ &mut self.sections } pub fn sections_values(&self) -> Vec<&Section> { self.sections.values().collect::>() } /// Find out the direct subsections of each subsection if there are some /// as well as the pages for each section pub fn populate_sections(&mut self) { let mut grandparent_paths: HashMap> = HashMap::new(); for section in self.sections.values_mut() { if let Some(ref grand_parent) = section.file.grand_parent { grandparent_paths .entry(grand_parent.to_path_buf()) .or_insert_with(|| vec![]) .push(section.file.path.clone()); } // Make sure the pages of a section are empty since we can call that many times on `serve` section.pages = vec![]; section.ignored_pages = vec![]; } for (key, page) in &mut self.pages { let parent_section_path = page.file.parent.join("_index.md"); if let Some(section_key) = self.paths_to_sections.get(&parent_section_path) { self.sections.get_mut(*section_key).unwrap().pages.push(key); } } self.sort_sections_pages(None); let sections = self.paths_to_sections.clone(); let mut sections_weight = HashMap::new(); for (key, section) in &self.sections { sections_weight.insert(key, section.meta.weight); } for section in self.sections.values_mut() { if let Some(paths) = grandparent_paths.get(§ion.file.parent) { section.subsections = paths .iter() .map(|p| sections[p]) .collect::>(); section.subsections .sort_by(|a, b| sections_weight[a].cmp(§ions_weight[b])); } } } /// Sort all sections pages unless `only` is set. /// If `only` is set, only the pages of the section at that specific Path will be rendered. pub fn sort_sections_pages(&mut self, only: Option<&Path>) { let mut updates = HashMap::new(); for (key, section) in &self.sections { if let Some(p) = only { if p != section.file.path { continue; } } let (sorted_pages, cannot_be_sorted_pages) = match section.meta.sort_by { SortBy::None => continue, SortBy::Date => { let data = section.pages .iter() .map(|k| { if let Some(page) = self.pages.get(*k) { (k, page.meta.datetime, page.permalink.as_ref()) } else { unreachable!("Sorting got an unknown page") } }) .collect(); sort_pages_by_date(data) }, SortBy::Weight => { let data = section.pages .iter() .map(|k| { if let Some(page) = self.pages.get(*k) { (k, page.meta.weight, page.permalink.as_ref()) } else { unreachable!("Sorting got an unknown page") } }) .collect(); sort_pages_by_weight(data) } }; updates.insert(key, (sorted_pages, cannot_be_sorted_pages, section.meta.sort_by)); } for (key, (sorted, cannot_be_sorted, sort_by)) in updates { // Find sibling between sorted pages first let with_siblings = find_siblings(sorted.iter().map(|k| { if let Some(page) = self.pages.get(*k) { (k, page.is_draft()) } else { unreachable!("Sorting got an unknown page") } }).collect()); for (k2, val1, val2) in with_siblings { if let Some(page) = self.pages.get_mut(k2) { match sort_by { SortBy::Date => { page.earlier = val2; page.later = val1; }, SortBy::Weight => { page.lighter = val1; page.heavier = val2; }, SortBy::None => unreachable!("Impossible to find siblings in SortBy::None") } } else { unreachable!("Sorting got an unknown page") } } if let Some(s) = self.sections.get_mut(key) { s.pages = sorted; s.ignored_pages = cannot_be_sorted; } } } /// Find all the orphan pages: pages that are in a folder without an `_index.md` pub fn get_all_orphan_pages(&self) -> Vec<&Page> { let pages_in_sections = self.sections .values() .flat_map(|s| &s.pages) .collect::>(); self.pages .iter() .filter(|(key, _)| !pages_in_sections.contains(&key)) .map(|(_, page)| page) .collect() } pub fn get_section(&self, path: &PathBuf) -> Option<&Section> { self.sections.get(self.paths_to_sections.get(path).cloned().unwrap_or_default()) } pub fn get_section_mut(&mut self, path: &PathBuf) -> Option<&mut Section> { self.sections.get_mut(self.paths_to_sections.get(path).cloned().unwrap_or_default()) } pub fn get_section_by_key(&self, key: Key) -> &Section { self.sections.get(key).unwrap() } pub fn get_page(&self, path: &PathBuf) -> Option<&Page> { self.pages.get(self.paths_to_pages.get(path).cloned().unwrap_or_default()) } pub fn get_page_by_key(&self, key: Key) -> &Page { self.pages.get(key).unwrap() } pub fn remove_section(&mut self, path: &PathBuf) -> Option
{ if let Some(k) = self.paths_to_sections.remove(path) { // TODO: delete section from parent subsection if there is one self.sections.remove(k) } else { None } } pub fn remove_page(&mut self, path: &PathBuf) -> Option { if let Some(k) = self.paths_to_pages.remove(path) { // TODO: delete page from all parent sections self.pages.remove(k) } else { None } } /// Used in rebuild, to check if we know it already pub fn contains_section(&self, path: &PathBuf) -> bool { self.paths_to_sections.contains_key(path) } /// Used in rebuild, to check if we know it already pub fn contains_page(&self, path: &PathBuf) -> bool { self.paths_to_pages.contains_key(path) } }