1 use std::path::PathBuf;
2 
3 use rayon::prelude::*;
4 use serde_derive::Serialize;
5 use tera::Context;
6 
7 use crate::Site;
8 use errors::Result;
9 use library::{sort_actual_pages_by_date, Page, TaxonomyItem};
10 use utils::templates::render_template;
11 
12 #[derive(Debug, Clone, PartialEq, Serialize)]
13 pub struct SerializedFeedTaxonomyItem<'a> {
14     name: &'a str,
15     slug: &'a str,
16     permalink: &'a str,
17 }
18 
19 impl<'a> SerializedFeedTaxonomyItem<'a> {
from_item(item: &'a TaxonomyItem) -> Self20     pub fn from_item(item: &'a TaxonomyItem) -> Self {
21         SerializedFeedTaxonomyItem {
22             name: &item.name,
23             slug: &item.slug,
24             permalink: &item.permalink,
25         }
26     }
27 }
28 
render_feed( site: &Site, all_pages: Vec<&Page>, lang: &str, base_path: Option<&PathBuf>, additional_context_fn: impl Fn(Context) -> Context, ) -> Result<Option<String>>29 pub fn render_feed(
30     site: &Site,
31     all_pages: Vec<&Page>,
32     lang: &str,
33     base_path: Option<&PathBuf>,
34     additional_context_fn: impl Fn(Context) -> Context,
35 ) -> Result<Option<String>> {
36     let mut pages = all_pages.into_iter().filter(|p| p.meta.date.is_some()).collect::<Vec<_>>();
37 
38     // Don't generate a feed if none of the pages has a date
39     if pages.is_empty() {
40         return Ok(None);
41     }
42 
43     pages.par_sort_unstable_by(sort_actual_pages_by_date);
44 
45     let mut context = Context::new();
46     context.insert(
47         "last_updated",
48         pages
49             .iter()
50             .filter_map(|page| page.meta.updated.as_ref())
51             .chain(pages[0].meta.date.as_ref())
52             .max() // I love lexicographically sorted date strings
53             .unwrap(), // Guaranteed because of pages[0].meta.date
54     );
55     let library = site.library.read().unwrap();
56     // limit to the last n elements if the limit is set; otherwise use all.
57     let num_entries = site.config.feed_limit.unwrap_or_else(|| pages.len());
58     let p =
59         pages.iter().take(num_entries).map(|x| x.to_serialized_basic(&library)).collect::<Vec<_>>();
60 
61     context.insert("pages", &p);
62     context.insert("config", &site.config.serialize(lang));
63     context.insert("lang", lang);
64 
65     let feed_filename = &site.config.feed_filename;
66     let feed_url = if let Some(base) = base_path {
67         site.config.make_permalink(&base.join(feed_filename).to_string_lossy().replace('\\', "/"))
68     } else {
69         site.config.make_permalink(feed_filename)
70     };
71 
72     context.insert("feed_url", &feed_url);
73 
74     context = additional_context_fn(context);
75 
76     let feed = render_template(feed_filename, &site.tera, context, &site.config.theme)?;
77 
78     Ok(Some(feed))
79 }
80