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.

257 lines
9.4KB

  1. use std::path::Path;
  2. use errors::Result;
  3. use site::Site;
  4. use content::{Page, Section};
  5. use front_matter::{PageFrontMatter, SectionFrontMatter};
  6. /// Finds the section that contains the page given if there is one
  7. pub fn find_parent_section<'a>(site: &'a Site, page: &Page) -> Option<&'a Section> {
  8. for section in site.sections.values() {
  9. if section.is_child_page(&page.file.path) {
  10. return Some(section)
  11. }
  12. }
  13. None
  14. }
  15. #[derive(Debug, Clone, Copy, PartialEq)]
  16. enum PageChangesNeeded {
  17. /// Editing `tags`
  18. Tags,
  19. /// Editing `categories`
  20. Categories,
  21. /// Editing `date` or `order`
  22. Sort,
  23. /// Editing anything else
  24. Render,
  25. }
  26. // TODO: seems like editing sort_by/render do weird stuff
  27. #[derive(Debug, Clone, Copy, PartialEq)]
  28. enum SectionChangesNeeded {
  29. /// Editing `sort_by`
  30. Sort,
  31. /// Editing `title`, `description`, `extra`, `template` or setting `render` to true
  32. Render,
  33. /// Editing `paginate_by`, `paginate_path` or `insert_anchor`
  34. RenderWithPages,
  35. /// Setting `render` to false
  36. Delete,
  37. }
  38. /// Evaluates all the params in the front matter that changed so we can do the smallest
  39. /// delta in the serve command
  40. fn find_section_front_matter_changes(current: &SectionFrontMatter, other: &SectionFrontMatter) -> Vec<SectionChangesNeeded> {
  41. let mut changes_needed = vec![];
  42. if current.sort_by != other.sort_by {
  43. changes_needed.push(SectionChangesNeeded::Sort);
  44. }
  45. if !current.should_render() && other.should_render() {
  46. changes_needed.push(SectionChangesNeeded::Delete);
  47. // Nothing else we can do
  48. return changes_needed;
  49. }
  50. if current.paginate_by != other.paginate_by
  51. || current.paginate_path != other.paginate_path
  52. || current.insert_anchor != other.insert_anchor {
  53. changes_needed.push(SectionChangesNeeded::RenderWithPages);
  54. // Nothing else we can do
  55. return changes_needed;
  56. }
  57. // Any other change will trigger a re-rendering of the section page only
  58. changes_needed.push(SectionChangesNeeded::Render);
  59. changes_needed
  60. }
  61. /// Evaluates all the params in the front matter that changed so we can do the smallest
  62. /// delta in the serve command
  63. fn find_page_front_matter_changes(current: &PageFrontMatter, other: &PageFrontMatter) -> Vec<PageChangesNeeded> {
  64. let mut changes_needed = vec![];
  65. if current.tags != other.tags {
  66. changes_needed.push(PageChangesNeeded::Tags);
  67. }
  68. if current.category != other.category {
  69. changes_needed.push(PageChangesNeeded::Categories);
  70. }
  71. if current.date != other.date || current.order != other.order {
  72. changes_needed.push(PageChangesNeeded::Sort);
  73. }
  74. changes_needed.push(PageChangesNeeded::Render);
  75. changes_needed
  76. }
  77. // What happens when a section or a page is changed
  78. pub fn after_content_change(site: &mut Site, path: &Path) -> Result<()> {
  79. let is_section = path.file_name().unwrap() == "_index.md";
  80. // A page or section got deleted
  81. if !path.exists() {
  82. // A folder got deleted, ignore this event
  83. if !site.sections.contains_key(path) && !site.pages.contains_key(path) {
  84. return Ok(());
  85. }
  86. if is_section {
  87. // A section was deleted, many things can be impacted:
  88. // - the pages of the section are becoming orphans
  89. // - any page that was referencing the section (index, etc)
  90. let relative_path = site.sections[path].file.relative.clone();
  91. // Remove the link to it and the section itself from the Site
  92. site.permalinks.remove(&relative_path);
  93. site.sections.remove(path);
  94. site.populate_sections();
  95. } else {
  96. // A page was deleted, many things can be impacted:
  97. // - the section the page is in
  98. // - any page that was referencing the section (index, etc)
  99. let relative_path = site.pages[path].file.relative.clone();
  100. site.permalinks.remove(&relative_path);
  101. if let Some(p) = site.pages.remove(path) {
  102. if p.meta.has_tags() || p.meta.category.is_some() {
  103. site.populate_tags_and_categories();
  104. }
  105. if find_parent_section(site, &p).is_some() {
  106. site.populate_sections();
  107. }
  108. };
  109. }
  110. // Ensure we have our fn updated so it doesn't contain the permalinks deleted
  111. site.register_tera_global_fns();
  112. // Deletion is something that doesn't happen all the time so we
  113. // don't need to optimise it too much
  114. return site.build();
  115. }
  116. // A section was edited
  117. if is_section {
  118. let section = Section::from_file(path, &site.config)?;
  119. match site.add_section(section, true)? {
  120. Some(prev) => {
  121. // Updating a section
  122. let current_meta = site.sections[path].meta.clone();
  123. // Front matter didn't change, only content did
  124. // so we render only the section page, not its pages
  125. if current_meta == prev.meta {
  126. return site.render_section(&site.sections[path], false);
  127. }
  128. // Front matter changed
  129. for changes in find_section_front_matter_changes(&current_meta, &prev.meta) {
  130. // Sort always comes first if present so the rendering will be fine
  131. match changes {
  132. SectionChangesNeeded::Sort => site.sort_sections_pages(Some(path)),
  133. SectionChangesNeeded::Render => site.render_section(&site.sections[path], false)?,
  134. SectionChangesNeeded::RenderWithPages => site.render_section(&site.sections[path], true)?,
  135. // can't be arsed to make the Delete efficient, it's not a common enough operation
  136. SectionChangesNeeded::Delete => {
  137. site.populate_sections();
  138. site.build()?;
  139. },
  140. };
  141. }
  142. return Ok(());
  143. },
  144. None => {
  145. // New section, only render that one
  146. site.populate_sections();
  147. site.register_tera_global_fns();
  148. return site.render_section(&site.sections[path], true);
  149. }
  150. };
  151. }
  152. // A page was edited
  153. let page = Page::from_file(path, &site.config)?;
  154. match site.add_page(page, true)? {
  155. Some(prev) => {
  156. site.register_tera_global_fns();
  157. // Updating a page
  158. let current = site.pages[path].clone();
  159. // Front matter didn't change, only content did
  160. // so we render only the section page, not its content
  161. if current.meta == prev.meta {
  162. return site.render_page(&current);
  163. }
  164. // Front matter changed
  165. for changes in find_page_front_matter_changes(&current.meta, &prev.meta) {
  166. // Sort always comes first if present so the rendering will be fine
  167. match changes {
  168. PageChangesNeeded::Tags => {
  169. site.populate_tags_and_categories();
  170. site.render_tags()?;
  171. },
  172. PageChangesNeeded::Categories => {
  173. site.populate_tags_and_categories();
  174. site.render_categories()?;
  175. },
  176. PageChangesNeeded::Sort => {
  177. let section_path = match find_parent_section(site, &site.pages[path]) {
  178. Some(s) => s.file.path.clone(),
  179. None => continue // Do nothing if it's an orphan page
  180. };
  181. site.populate_sections();
  182. site.sort_sections_pages(Some(&section_path));
  183. site.render_index()?;
  184. },
  185. PageChangesNeeded::Render => {
  186. site.render_page(&site.pages[path])?;
  187. },
  188. };
  189. }
  190. return Ok(());
  191. },
  192. None => {
  193. // It's a new page!
  194. site.populate_sections();
  195. site.populate_tags_and_categories();
  196. site.register_tera_global_fns();
  197. // No need to optimise that yet, we can revisit if it becomes an issue
  198. site.build()?;
  199. }
  200. }
  201. Ok(())
  202. }
  203. /// What happens when a template is changed
  204. pub fn after_template_change(site: &mut Site, path: &Path) -> Result<()> {
  205. site.tera.full_reload()?;
  206. match path.file_name().unwrap().to_str().unwrap() {
  207. "sitemap.xml" => site.render_sitemap(),
  208. "rss.xml" => site.render_rss_feed(),
  209. "robots.txt" => site.render_robots(),
  210. "categories.html" | "category.html" => site.render_categories(),
  211. "tags.html" | "tag.html" => site.render_tags(),
  212. "page.html" => {
  213. site.render_sections()?;
  214. site.render_orphan_pages()
  215. },
  216. "section.html" => site.render_sections(),
  217. // Either the index or some unknown template changed
  218. // We can't really know what this change affects so rebuild all
  219. // the things
  220. _ => {
  221. site.render_sections()?;
  222. site.render_orphan_pages()?;
  223. site.render_categories()?;
  224. site.render_tags()
  225. },
  226. }
  227. }