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