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.

144 lines
4.5KB

  1. use std::borrow::Cow;
  2. use std::collections::HashSet;
  3. use std::hash::{Hash, Hasher};
  4. use serde_derive::Serialize;
  5. use config::Config;
  6. use library::{Library, Taxonomy};
  7. use std::cmp::Ordering;
  8. use tera::{Map, Value};
  9. /// The sitemap only needs links, potentially date and extra for pages in case of updates
  10. /// for examples so we trim down all entries to only that
  11. #[derive(Debug, Serialize)]
  12. pub struct SitemapEntry<'a> {
  13. pub permalink: Cow<'a, str>,
  14. pub date: Option<String>,
  15. pub extra: Option<&'a Map<String, Value>>,
  16. }
  17. // Hash/Eq is not implemented for tera::Map but in our case we only care about the permalink
  18. // when comparing/hashing so we implement it manually
  19. impl<'a> Hash for SitemapEntry<'a> {
  20. fn hash<H: Hasher>(&self, state: &mut H) {
  21. self.permalink.hash(state);
  22. }
  23. }
  24. impl<'a> PartialEq for SitemapEntry<'a> {
  25. fn eq(&self, other: &SitemapEntry) -> bool {
  26. self.permalink == other.permalink
  27. }
  28. }
  29. impl<'a> Eq for SitemapEntry<'a> {}
  30. impl<'a> SitemapEntry<'a> {
  31. pub fn new(permalink: Cow<'a, str>, date: Option<String>) -> Self {
  32. SitemapEntry { permalink, date, extra: None }
  33. }
  34. pub fn add_extra(&mut self, extra: &'a Map<String, Value>) {
  35. self.extra = Some(extra);
  36. }
  37. }
  38. impl<'a> PartialOrd for SitemapEntry<'a> {
  39. fn partial_cmp(&self, other: &SitemapEntry) -> Option<Ordering> {
  40. Some(self.permalink.as_ref().cmp(other.permalink.as_ref()))
  41. }
  42. }
  43. impl<'a> Ord for SitemapEntry<'a> {
  44. fn cmp(&self, other: &SitemapEntry) -> Ordering {
  45. self.permalink.as_ref().cmp(other.permalink.as_ref())
  46. }
  47. }
  48. /// Finds out all the links to put in a sitemap from the pages/sections/taxonomies
  49. /// There are no duplicate permalinks in the output vec
  50. pub fn find_entries<'a>(
  51. library: &'a Library,
  52. taxonomies: &'a [Taxonomy],
  53. config: &'a Config,
  54. ) -> Vec<SitemapEntry<'a>> {
  55. let pages = library
  56. .pages_values()
  57. .iter()
  58. .map(|p| {
  59. let date = match p.meta.date {
  60. Some(ref d) => Some(d.to_string()),
  61. None => None,
  62. };
  63. let mut entry = SitemapEntry::new(Cow::Borrowed(&p.permalink), date);
  64. entry.add_extra(&p.meta.extra);
  65. entry
  66. })
  67. .collect::<Vec<_>>();
  68. let mut sections = library
  69. .sections_values()
  70. .iter()
  71. .filter(|s| s.meta.render)
  72. .map(|s| {
  73. let mut entry = SitemapEntry::new(Cow::Borrowed(&s.permalink), None);
  74. entry.add_extra(&s.meta.extra);
  75. entry
  76. })
  77. .collect::<Vec<_>>();
  78. for section in library.sections_values().iter().filter(|s| s.meta.paginate_by.is_some()) {
  79. let number_pagers =
  80. (section.pages.len() as f64 / section.meta.paginate_by.unwrap() as f64).ceil() as isize;
  81. for i in 1..=number_pagers {
  82. let permalink = format!("{}{}/{}/", section.permalink, section.meta.paginate_path, i);
  83. sections.push(SitemapEntry::new(Cow::Owned(permalink), None))
  84. }
  85. }
  86. let mut taxonomies_entries = vec![];
  87. for taxonomy in taxonomies {
  88. let name = &taxonomy.kind.name;
  89. let mut terms = vec![];
  90. terms.push(SitemapEntry::new(Cow::Owned(config.make_permalink(name)), None));
  91. for item in &taxonomy.items {
  92. terms.push(SitemapEntry::new(
  93. Cow::Owned(config.make_permalink(&format!("{}/{}", name, item.slug))),
  94. None,
  95. ));
  96. if taxonomy.kind.is_paginated() {
  97. let number_pagers = (item.pages.len() as f64
  98. / taxonomy.kind.paginate_by.unwrap() as f64)
  99. .ceil() as isize;
  100. for i in 1..=number_pagers {
  101. let permalink = config.make_permalink(&format!(
  102. "{}/{}/{}/{}",
  103. name,
  104. item.slug,
  105. taxonomy.kind.paginate_path(),
  106. i
  107. ));
  108. terms.push(SitemapEntry::new(Cow::Owned(permalink), None))
  109. }
  110. }
  111. }
  112. taxonomies_entries.push(terms);
  113. }
  114. let mut all_sitemap_entries = HashSet::new();
  115. for p in pages {
  116. all_sitemap_entries.insert(p);
  117. }
  118. for s in sections {
  119. all_sitemap_entries.insert(s);
  120. }
  121. for terms in taxonomies_entries {
  122. for term in terms {
  123. all_sitemap_entries.insert(term);
  124. }
  125. }
  126. all_sitemap_entries.into_iter().collect::<Vec<_>>()
  127. }