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.

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