You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

408 lines
15KB

  1. use std::collections::{HashMap, HashSet};
  2. use std::path::{Path, PathBuf};
  3. use slotmap::{DenseSlotMap, Key};
  4. use front_matter::SortBy;
  5. use content::{Page, Section};
  6. use sorting::{find_siblings, sort_pages_by_date, sort_pages_by_weight};
  7. /// Houses everything about pages and sections
  8. /// Think of it as a database where each page and section has an id (Key here)
  9. /// that can be used to find the actual value
  10. /// Sections and pages can then refer to other elements by those keys, which are very cheap to
  11. /// copy.
  12. /// We can assume the keys are always existing as removing a page/section deletes all references
  13. /// to that key.
  14. #[derive(Debug)]
  15. pub struct Library {
  16. /// All the pages of the site
  17. pages: DenseSlotMap<Page>,
  18. /// All the sections of the site
  19. sections: DenseSlotMap<Section>,
  20. /// A mapping path -> key for pages so we can easily get their key
  21. pub paths_to_pages: HashMap<PathBuf, Key>,
  22. /// A mapping path -> key for sections so we can easily get their key
  23. pub paths_to_sections: HashMap<PathBuf, Key>,
  24. /// Whether we need to look for translations
  25. is_multilingual: bool,
  26. }
  27. impl Library {
  28. pub fn new(cap_pages: usize, cap_sections: usize, is_multilingual: bool) -> Self {
  29. Library {
  30. pages: DenseSlotMap::with_capacity(cap_pages),
  31. sections: DenseSlotMap::with_capacity(cap_sections),
  32. paths_to_pages: HashMap::with_capacity(cap_pages),
  33. paths_to_sections: HashMap::with_capacity(cap_sections),
  34. is_multilingual,
  35. }
  36. }
  37. /// Add a section and return its Key
  38. pub fn insert_section(&mut self, section: Section) -> Key {
  39. let path = section.file.path.clone();
  40. let key = self.sections.insert(section);
  41. self.paths_to_sections.insert(path, key);
  42. key
  43. }
  44. /// Add a page and return its Key
  45. pub fn insert_page(&mut self, page: Page) -> Key {
  46. let path = page.file.path.clone();
  47. let key = self.pages.insert(page);
  48. self.paths_to_pages.insert(path, key);
  49. key
  50. }
  51. pub fn pages(&self) -> &DenseSlotMap<Page> {
  52. &self.pages
  53. }
  54. pub fn pages_mut(&mut self) -> &mut DenseSlotMap<Page> {
  55. &mut self.pages
  56. }
  57. pub fn pages_values(&self) -> Vec<&Page> {
  58. self.pages.values().collect::<Vec<_>>()
  59. }
  60. pub fn sections(&self) -> &DenseSlotMap<Section> {
  61. &self.sections
  62. }
  63. pub fn sections_mut(&mut self) -> &mut DenseSlotMap<Section> {
  64. &mut self.sections
  65. }
  66. pub fn sections_values(&self) -> Vec<&Section> {
  67. self.sections.values().collect::<Vec<_>>()
  68. }
  69. /// Find out the direct subsections of each subsection if there are some
  70. /// as well as the pages for each section
  71. pub fn populate_sections(&mut self) {
  72. let root_path =
  73. self.sections.values().find(|s| s.is_index()).map(|s| s.file.parent.clone()).unwrap();
  74. // We are going to get both the ancestors and grandparents for each section in one go
  75. let mut ancestors: HashMap<PathBuf, Vec<_>> = HashMap::new();
  76. let mut subsections: HashMap<PathBuf, Vec<_>> = HashMap::new();
  77. for section in self.sections.values_mut() {
  78. // Make sure the pages of a section are empty since we can call that many times on `serve`
  79. section.pages = vec![];
  80. section.ignored_pages = vec![];
  81. if let Some(ref grand_parent) = section.file.grand_parent {
  82. subsections
  83. // Using the original filename to work for multi-lingual sections
  84. .entry(grand_parent.join(&section.file.filename))
  85. .or_insert_with(|| vec![])
  86. .push(section.file.path.clone());
  87. }
  88. // Index has no ancestors, no need to go through it
  89. if section.is_index() {
  90. ancestors.insert(section.file.path.clone(), vec![]);
  91. continue;
  92. }
  93. let mut path = root_path.clone();
  94. let root_key = self.paths_to_sections[&root_path.join(&section.file.filename)];
  95. // Index section is the first ancestor of every single section
  96. let mut parents = vec![root_key];
  97. for component in &section.file.components {
  98. path = path.join(component);
  99. // Skip itself
  100. if path == section.file.parent {
  101. continue;
  102. }
  103. if let Some(section_key) =
  104. self.paths_to_sections.get(&path.join(&section.file.filename))
  105. {
  106. parents.push(*section_key);
  107. }
  108. }
  109. ancestors.insert(section.file.path.clone(), parents);
  110. }
  111. for (key, page) in &mut self.pages {
  112. let parent_filename = if let Some(ref lang) = page.lang {
  113. format!("_index.{}.md", lang)
  114. } else {
  115. "_index.md".to_string()
  116. };
  117. let mut parent_section_path = page.file.parent.join(&parent_filename);
  118. while let Some(section_key) = self.paths_to_sections.get(&parent_section_path) {
  119. let parent_is_transparent;
  120. // We need to get a reference to a section later so keep the scope of borrowing small
  121. {
  122. let mut section = self.sections.get_mut(*section_key).unwrap();
  123. section.pages.push(key);
  124. parent_is_transparent = section.meta.transparent;
  125. }
  126. page.ancestors =
  127. ancestors.get(&parent_section_path).cloned().unwrap_or_else(|| vec![]);
  128. // Don't forget to push the actual parent
  129. page.ancestors.push(*section_key);
  130. // Find the page template if one of a parent has page_template set
  131. // Stops after the first one found, keep in mind page.ancestors
  132. // is [index, ..., parent] so we need to reverse it first
  133. if page.meta.template.is_none() {
  134. for ancestor in page.ancestors.iter().rev() {
  135. let s = self.sections.get(*ancestor).unwrap();
  136. if s.meta.page_template.is_some() {
  137. page.meta.template = s.meta.page_template.clone();
  138. break;
  139. }
  140. }
  141. }
  142. if !parent_is_transparent {
  143. break;
  144. }
  145. // We've added `_index(.{LANG})?.md` so if we are here so we need to go up twice
  146. match parent_section_path.clone().parent().unwrap().parent() {
  147. Some(parent) => parent_section_path = parent.join(&parent_filename),
  148. None => break,
  149. }
  150. }
  151. }
  152. self.populate_translations();
  153. self.sort_sections_pages();
  154. let sections = self.paths_to_sections.clone();
  155. let mut sections_weight = HashMap::new();
  156. for (key, section) in &self.sections {
  157. sections_weight.insert(key, section.meta.weight);
  158. }
  159. for section in self.sections.values_mut() {
  160. if let Some(ref children) = subsections.get(&section.file.path) {
  161. let mut children: Vec<_> = children.iter().map(|p| sections[p]).collect();
  162. children.sort_by(|a, b| sections_weight[a].cmp(&sections_weight[b]));
  163. section.subsections = children;
  164. }
  165. section.ancestors =
  166. ancestors.get(&section.file.path).cloned().unwrap_or_else(|| vec![]);
  167. }
  168. }
  169. /// Sort all sections pages according to sorting method given
  170. /// Pages that cannot be sorted are set to the section.ignored_pages instead
  171. pub fn sort_sections_pages(&mut self) {
  172. let mut updates = HashMap::new();
  173. for (key, section) in &self.sections {
  174. let (sorted_pages, cannot_be_sorted_pages) = match section.meta.sort_by {
  175. SortBy::None => continue,
  176. SortBy::Date => {
  177. let data = section
  178. .pages
  179. .iter()
  180. .map(|k| {
  181. if let Some(page) = self.pages.get(*k) {
  182. (k, page.meta.datetime, page.permalink.as_ref())
  183. } else {
  184. unreachable!("Sorting got an unknown page")
  185. }
  186. })
  187. .collect();
  188. sort_pages_by_date(data)
  189. }
  190. SortBy::Weight => {
  191. let data = section
  192. .pages
  193. .iter()
  194. .map(|k| {
  195. if let Some(page) = self.pages.get(*k) {
  196. (k, page.meta.weight, page.permalink.as_ref())
  197. } else {
  198. unreachable!("Sorting got an unknown page")
  199. }
  200. })
  201. .collect();
  202. sort_pages_by_weight(data)
  203. }
  204. };
  205. updates.insert(key, (sorted_pages, cannot_be_sorted_pages, section.meta.sort_by));
  206. }
  207. for (key, (sorted, cannot_be_sorted, sort_by)) in updates {
  208. // Find sibling between sorted pages first
  209. let with_siblings = find_siblings(
  210. sorted
  211. .iter()
  212. .map(|k| {
  213. if let Some(page) = self.pages.get(*k) {
  214. (k, page.is_draft())
  215. } else {
  216. unreachable!("Sorting got an unknown page")
  217. }
  218. })
  219. .collect(),
  220. );
  221. for (k2, val1, val2) in with_siblings {
  222. if let Some(page) = self.pages.get_mut(k2) {
  223. match sort_by {
  224. SortBy::Date => {
  225. page.earlier = val2;
  226. page.later = val1;
  227. }
  228. SortBy::Weight => {
  229. page.lighter = val1;
  230. page.heavier = val2;
  231. }
  232. SortBy::None => unreachable!("Impossible to find siblings in SortBy::None"),
  233. }
  234. } else {
  235. unreachable!("Sorting got an unknown page")
  236. }
  237. }
  238. if let Some(s) = self.sections.get_mut(key) {
  239. s.pages = sorted;
  240. s.ignored_pages = cannot_be_sorted;
  241. }
  242. }
  243. }
  244. /// Finds all the translations for each section/page and set the `translations`
  245. /// field of each as needed
  246. /// A no-op for sites without multiple languages
  247. fn populate_translations(&mut self) {
  248. if !self.is_multilingual {
  249. return;
  250. }
  251. // Sections first
  252. let mut sections_translations = HashMap::new();
  253. for (key, section) in &self.sections {
  254. sections_translations
  255. .entry(section.file.canonical.clone()) // TODO: avoid this clone
  256. .or_insert_with(Vec::new)
  257. .push(key);
  258. }
  259. for (key, section) in self.sections.iter_mut() {
  260. let translations = &sections_translations[&section.file.canonical];
  261. if translations.len() == 1 {
  262. section.translations = vec![];
  263. continue;
  264. }
  265. section.translations = translations.iter().filter(|k| **k != key).cloned().collect();
  266. }
  267. // Same thing for pages
  268. let mut pages_translations = HashMap::new();
  269. for (key, page) in &self.pages {
  270. pages_translations
  271. .entry(page.file.canonical.clone()) // TODO: avoid this clone
  272. .or_insert_with(Vec::new)
  273. .push(key);
  274. }
  275. for (key, page) in self.pages.iter_mut() {
  276. let translations = &pages_translations[&page.file.canonical];
  277. if translations.len() == 1 {
  278. page.translations = vec![];
  279. continue;
  280. }
  281. page.translations = translations.iter().filter(|k| **k != key).cloned().collect();
  282. }
  283. }
  284. /// Find all the orphan pages: pages that are in a folder without an `_index.md`
  285. pub fn get_all_orphan_pages(&self) -> Vec<&Page> {
  286. let pages_in_sections =
  287. self.sections.values().flat_map(|s| &s.pages).collect::<HashSet<_>>();
  288. self.pages
  289. .iter()
  290. .filter(|(key, _)| !pages_in_sections.contains(&key))
  291. .map(|(_, page)| page)
  292. .collect()
  293. }
  294. pub fn find_parent_section<P: AsRef<Path>>(&self, path: P) -> Option<&Section> {
  295. let page_key = self.paths_to_pages[path.as_ref()];
  296. for s in self.sections.values() {
  297. if s.pages.contains(&page_key) {
  298. return Some(s);
  299. }
  300. }
  301. None
  302. }
  303. /// Only used in tests
  304. pub fn get_section_key<P: AsRef<Path>>(&self, path: P) -> Option<&Key> {
  305. self.paths_to_sections.get(path.as_ref())
  306. }
  307. pub fn get_section<P: AsRef<Path>>(&self, path: P) -> Option<&Section> {
  308. self.sections.get(self.paths_to_sections.get(path.as_ref()).cloned().unwrap_or_default())
  309. }
  310. pub fn get_section_mut<P: AsRef<Path>>(&mut self, path: P) -> Option<&mut Section> {
  311. self.sections
  312. .get_mut(self.paths_to_sections.get(path.as_ref()).cloned().unwrap_or_default())
  313. }
  314. pub fn get_section_by_key(&self, key: Key) -> &Section {
  315. self.sections.get(key).unwrap()
  316. }
  317. pub fn get_section_mut_by_key(&mut self, key: Key) -> &mut Section {
  318. self.sections.get_mut(key).unwrap()
  319. }
  320. pub fn get_section_path_by_key(&self, key: Key) -> &str {
  321. &self.get_section_by_key(key).file.relative
  322. }
  323. pub fn get_page<P: AsRef<Path>>(&self, path: P) -> Option<&Page> {
  324. self.pages.get(self.paths_to_pages.get(path.as_ref()).cloned().unwrap_or_default())
  325. }
  326. pub fn get_page_by_key(&self, key: Key) -> &Page {
  327. self.pages.get(key).unwrap()
  328. }
  329. pub fn get_page_mut_by_key(&mut self, key: Key) -> &mut Page {
  330. self.pages.get_mut(key).unwrap()
  331. }
  332. pub fn remove_section<P: AsRef<Path>>(&mut self, path: P) -> Option<Section> {
  333. if let Some(k) = self.paths_to_sections.remove(path.as_ref()) {
  334. self.sections.remove(k)
  335. } else {
  336. None
  337. }
  338. }
  339. pub fn remove_page<P: AsRef<Path>>(&mut self, path: P) -> Option<Page> {
  340. if let Some(k) = self.paths_to_pages.remove(path.as_ref()) {
  341. self.pages.remove(k)
  342. } else {
  343. None
  344. }
  345. }
  346. /// Used in rebuild, to check if we know it already
  347. pub fn contains_section<P: AsRef<Path>>(&self, path: P) -> bool {
  348. self.paths_to_sections.contains_key(path.as_ref())
  349. }
  350. /// Used in rebuild, to check if we know it already
  351. pub fn contains_page<P: AsRef<Path>>(&self, path: P) -> bool {
  352. self.paths_to_pages.contains_key(path.as_ref())
  353. }
  354. }