Extract some feed logic out of site/lib.rs

This commit is contained in:
Vincent Prouillet 2020-07-25 10:30:55 +02:00
parent 5fe1036a1d
commit d7b53687a5
5 changed files with 103 additions and 80 deletions

View file

@ -258,8 +258,8 @@ impl Section {
None => None, None => None,
Some(x) => match x { Some(x) => match x {
0 => None, 0 => None,
_ => Some(x) _ => Some(x),
} },
} }
} }
} }

View file

@ -403,7 +403,7 @@ pub fn after_template_change(site: &mut Site, path: &Path) -> Result<()> {
site.library.read().unwrap().pages_values(), site.library.read().unwrap().pages_values(),
None, None,
&site.config.default_language, &site.config.default_language,
None, |c| c,
) )
} }
"split_sitemap_index.xml" => site.render_sitemap(), "split_sitemap_index.xml" => site.render_sitemap(),

View file

@ -0,0 +1,79 @@
use std::path::PathBuf;
use rayon::prelude::*;
use serde_derive::Serialize;
use tera::Context;
use crate::Site;
use errors::Result;
use library::{sort_actual_pages_by_date, Page, TaxonomyItem};
use utils::templates::render_template;
#[derive(Debug, Clone, PartialEq, Serialize)]
pub struct SerializedFeedTaxonomyItem<'a> {
name: &'a str,
slug: &'a str,
permalink: &'a str,
}
impl<'a> SerializedFeedTaxonomyItem<'a> {
pub fn from_item(item: &'a TaxonomyItem) -> Self {
SerializedFeedTaxonomyItem {
name: &item.name,
slug: &item.slug,
permalink: &item.permalink,
}
}
}
pub fn render_feed(
site: &Site,
all_pages: Vec<&Page>,
lang: &str,
base_path: Option<&PathBuf>,
additional_context_fn: impl Fn(Context) -> Context,
) -> Result<Option<String>> {
let mut pages = all_pages.into_iter().filter(|p| p.meta.date.is_some()).collect::<Vec<_>>();
// Don't generate a feed if none of the pages has a date
if pages.is_empty() {
return Ok(None);
}
pages.par_sort_unstable_by(sort_actual_pages_by_date);
let mut context = Context::new();
context.insert(
"last_updated",
pages
.iter()
.filter_map(|page| page.meta.updated.as_ref())
.chain(pages[0].meta.date.as_ref())
.max() // I love lexicographically sorted date strings
.unwrap(), // Guaranteed because of pages[0].meta.date
);
let library = site.library.read().unwrap();
// limit to the last n elements if the limit is set; otherwise use all.
let num_entries = site.config.feed_limit.unwrap_or_else(|| pages.len());
let p =
pages.iter().take(num_entries).map(|x| x.to_serialized_basic(&library)).collect::<Vec<_>>();
context.insert("pages", &p);
context.insert("config", &site.config);
context.insert("lang", lang);
let feed_filename = &site.config.feed_filename;
let feed_url = if let Some(ref base) = base_path {
site.config.make_permalink(&base.join(feed_filename).to_string_lossy().replace('\\', "/"))
} else {
site.config.make_permalink(feed_filename)
};
context.insert("feed_url", &feed_url);
context = additional_context_fn(context);
let feed = render_template(feed_filename, &site.tera, context, &site.config.theme)?;
Ok(Some(feed))
}

View file

@ -1,3 +1,4 @@
pub mod feed;
pub mod link_checking; pub mod link_checking;
pub mod sass; pub mod sass;
pub mod sitemap; pub mod sitemap;
@ -10,18 +11,15 @@ use std::sync::{Arc, Mutex, RwLock};
use glob::glob; use glob::glob;
use rayon::prelude::*; use rayon::prelude::*;
use serde_derive::Serialize;
use tera::{Context, Tera}; use tera::{Context, Tera};
use crate::feed::render_feed;
use crate::link_checking::{check_external_links, check_internal_links_with_anchors}; use crate::link_checking::{check_external_links, check_internal_links_with_anchors};
use crate::tpls::{load_tera, register_early_global_fns, register_tera_global_fns}; use crate::tpls::{load_tera, register_early_global_fns, register_tera_global_fns};
use config::{get_config, Config, Taxonomy as TaxonomyConfig}; use config::{get_config, Config};
use errors::{bail, Error, Result}; use errors::{bail, Error, Result};
use front_matter::InsertAnchor; use front_matter::InsertAnchor;
use library::{ use library::{find_taxonomies, Library, Page, Paginator, Section, Taxonomy};
find_taxonomies, sort_actual_pages_by_date, Library, Page, Paginator, Section, Taxonomy,
TaxonomyItem,
};
use templates::render_redirect_template; use templates::render_redirect_template;
use utils::fs::{copy_directory, create_directory, create_file, ensure_directory_exists}; use utils::fs::{copy_directory, create_directory, create_file, ensure_directory_exists};
use utils::net::get_available_port; use utils::net::get_available_port;
@ -50,23 +48,6 @@ pub struct Site {
include_drafts: bool, include_drafts: bool,
} }
#[derive(Debug, Clone, PartialEq, Serialize)]
struct SerializedFeedTaxonomyItem<'a> {
name: &'a str,
slug: &'a str,
permalink: &'a str,
}
impl<'a> SerializedFeedTaxonomyItem<'a> {
pub fn from_item(item: &'a TaxonomyItem) -> Self {
SerializedFeedTaxonomyItem {
name: &item.name,
slug: &item.slug,
permalink: &item.permalink,
}
}
}
impl Site { impl Site {
/// Parse a site at the given path. Defaults to the current dir /// Parse a site at the given path. Defaults to the current dir
/// Passing in a path is used in tests and when --root argument is passed /// Passing in a path is used in tests and when --root argument is passed
@ -533,7 +514,7 @@ impl Site {
} else { } else {
library.pages_values() library.pages_values()
}; };
self.render_feed(pages, None, &self.config.default_language, None)?; self.render_feed(pages, None, &self.config.default_language, |c| c)?;
} }
for lang in &self.config.languages { for lang in &self.config.languages {
@ -542,7 +523,7 @@ impl Site {
} }
let pages = let pages =
library.pages_values().iter().filter(|p| p.lang == lang.code).cloned().collect(); library.pages_values().iter().filter(|p| p.lang == lang.code).cloned().collect();
self.render_feed(pages, Some(&PathBuf::from(lang.code.clone())), &lang.code, None)?; self.render_feed(pages, Some(&PathBuf::from(lang.code.clone())), &lang.code, |c| c)?;
} }
self.render_404()?; self.render_404()?;
@ -714,7 +695,12 @@ impl Site {
} else { } else {
&self.config.default_language &self.config.default_language
}, },
Some((&taxonomy.kind, &item)), |mut context: Context| {
context.insert("taxonomy", &taxonomy.kind);
context
.insert("term", &feed::SerializedFeedTaxonomyItem::from_item(item));
context
},
) )
} else { } else {
Ok(()) Ok(())
@ -782,58 +768,15 @@ impl Site {
all_pages: Vec<&Page>, all_pages: Vec<&Page>,
base_path: Option<&PathBuf>, base_path: Option<&PathBuf>,
lang: &str, lang: &str,
taxonomy_and_item: Option<(&TaxonomyConfig, &TaxonomyItem)>, additional_context_fn: impl Fn(Context) -> Context,
) -> Result<()> { ) -> Result<()> {
ensure_directory_exists(&self.output_path)?; ensure_directory_exists(&self.output_path)?;
let mut context = Context::new(); let feed = match render_feed(self, all_pages, lang, base_path, additional_context_fn)? {
let mut pages = all_pages.into_iter().filter(|p| p.meta.date.is_some()).collect::<Vec<_>>(); Some(v) => v,
None => return Ok(()),
// Don't generate a feed if none of the pages has a date
if pages.is_empty() {
return Ok(());
}
pages.par_sort_unstable_by(sort_actual_pages_by_date);
context.insert(
"last_updated",
pages
.iter()
.filter_map(|page| page.meta.updated.as_ref())
.chain(pages[0].meta.date.as_ref())
.max() // I love lexicographically sorted date strings
.unwrap(), // Guaranteed because of pages[0].meta.date
);
let library = self.library.read().unwrap();
// limit to the last n elements if the limit is set; otherwise use all.
let num_entries = self.config.feed_limit.unwrap_or_else(|| pages.len());
let p = pages
.iter()
.take(num_entries)
.map(|x| x.to_serialized_basic(&library))
.collect::<Vec<_>>();
context.insert("pages", &p);
context.insert("config", &self.config);
context.insert("lang", lang);
let feed_filename = &self.config.feed_filename;
let feed_url = if let Some(ref base) = base_path {
self.config
.make_permalink(&base.join(feed_filename).to_string_lossy().replace('\\', "/"))
} else {
self.config.make_permalink(feed_filename)
}; };
let feed_filename = &self.config.feed_filename;
context.insert("feed_url", &feed_url);
if let Some((taxonomy, item)) = taxonomy_and_item {
context.insert("taxonomy", taxonomy);
context.insert("term", &SerializedFeedTaxonomyItem::from_item(item));
}
let feed = &render_template(feed_filename, &self.tera, context, &self.config.theme)?;
if let Some(ref base) = base_path { if let Some(ref base) = base_path {
let mut output_path = self.output_path.clone(); let mut output_path = self.output_path.clone();
@ -843,9 +786,9 @@ impl Site {
create_directory(&output_path)?; create_directory(&output_path)?;
} }
} }
create_file(&output_path.join(feed_filename), feed)?; create_file(&output_path.join(feed_filename), &feed)?;
} else { } else {
create_file(&self.output_path.join(feed_filename), feed)?; create_file(&self.output_path.join(feed_filename), &feed)?;
} }
Ok(()) Ok(())
} }

View file

@ -89,7 +89,8 @@ pub fn find_entries<'a>(
if let Some(paginate_by) = section.paginate_by() { if let Some(paginate_by) = section.paginate_by() {
let number_pagers = (section.pages.len() as f64 / paginate_by as f64).ceil() as isize; let number_pagers = (section.pages.len() as f64 / paginate_by as f64).ceil() as isize;
for i in 1..=number_pagers { for i in 1..=number_pagers {
let permalink = format!("{}{}/{}/", section.permalink, section.meta.paginate_path, i); let permalink =
format!("{}{}/{}/", section.permalink, section.meta.paginate_path, i);
sections.push(SitemapEntry::new(Cow::Owned(permalink), None)) sections.push(SitemapEntry::new(Cow::Owned(permalink), None))
} }
} }