Skip to content

Commit

Permalink
perf(layouts): Switch from lazy to eager
Browse files Browse the repository at this point in the history
No idea what impact this will have on performance actually.

BREAKING CHANGE: The paths used for layouts now must be clean (no
`//./file.liquid`).
  • Loading branch information
epage committed Mar 3, 2018
1 parent 71cc7ac commit b2cbe13
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 53 deletions.
83 changes: 47 additions & 36 deletions src/cobalt.rs
Expand Up @@ -22,8 +22,8 @@ pub fn build(config: &Config) -> Result<()> {
let dest = config.destination.as_path();

let parser = &config.liquid;
let layouts = &config.layouts_dir;
let mut layouts_cache = HashMap::new();
let layouts = find_layouts(&config.layouts_dir)?;
let layouts = parse_layouts(layouts);

debug!("Layouts directory: {:?}", layouts);

Expand All @@ -35,12 +35,7 @@ pub fn build(config: &Config) -> Result<()> {
let documents = parse_pages(&page_files, &config.pages, source)?;

sort_pages(&mut posts, &config.posts)?;
generate_posts(&mut posts,
config,
&parser,
dest,
&layouts,
&mut layouts_cache)?;
generate_posts(&mut posts, config, &parser, dest, &layouts)?;

// check if we should create an RSS file and create it!
if let Some(ref path) = config.posts.rss {
Expand All @@ -51,13 +46,7 @@ pub fn build(config: &Config) -> Result<()> {
create_jsonfeed(path, dest, &config.posts, &posts)?;
}

generate_pages(posts,
documents,
config,
&parser,
dest,
&layouts,
&mut layouts_cache)?;
generate_pages(posts, documents, config, &parser, dest, &layouts)?;

// copy all remaining files in the source to the destination
// compile SASS along the way
Expand All @@ -70,8 +59,7 @@ pub fn build(config: &Config) -> Result<()> {
}

fn generate_doc(dest: &Path,
layouts: &Path,
mut layouts_cache: &mut HashMap<String, String>,
layouts: &HashMap<String, String>,
parser: &cobalt_model::Liquid,
posts_data: &[liquid::Value],
doc: &mut Document,
Expand Down Expand Up @@ -102,7 +90,7 @@ fn generate_doc(dest: &Path,
// Refresh `page` with the `excerpt` / `content` attribute
globals.insert("page".to_owned(),
liquid::Value::Object(doc.attributes.clone()));
let doc_html = doc.render(&globals, parser, layouts, &mut layouts_cache)
let doc_html = doc.render(&globals, parser, &layouts)
.chain_err(|| format!("Failed to render for {:?}", doc.file_path))?;
files::write_document_file(doc_html, dest.join(&doc.file_path))?;
Ok(())
Expand All @@ -113,8 +101,7 @@ fn generate_pages(posts: Vec<Document>,
config: &Config,
parser: &cobalt_model::Liquid,
dest: &Path,
layouts: &Path,
mut layouts_cache: &mut HashMap<String, String>)
layouts: &HashMap<String, String>)
-> Result<()> {
// during post rendering additional attributes such as content were
// added to posts. collect them so that non-post documents can access them
Expand All @@ -126,13 +113,7 @@ fn generate_pages(posts: Vec<Document>,
trace!("Generating other documents");
for mut doc in documents {
trace!("Generating {}", doc.url_path);
generate_doc(dest,
layouts,
&mut layouts_cache,
parser,
&posts_data,
&mut doc,
config)?;
generate_doc(dest, &layouts, parser, &posts_data, &mut doc, config)?;
}

Ok(())
Expand All @@ -142,8 +123,7 @@ fn generate_posts(posts: &mut Vec<Document>,
config: &Config,
parser: &cobalt_model::Liquid,
dest: &Path,
layouts: &Path,
mut layouts_cache: &mut HashMap<String, String>)
layouts: &HashMap<String, String>)
-> Result<()> {
// collect all posts attributes to pass them to other posts for rendering
let simple_posts_data: Vec<liquid::Value> = posts
Expand All @@ -170,13 +150,7 @@ fn generate_posts(posts: &mut Vec<Document>,
.unwrap_or(liquid::Value::Nil);
post.attributes.insert("next".to_owned(), next);

generate_doc(dest,
layouts,
&mut layouts_cache,
parser,
&simple_posts_data,
post,
config)?;
generate_doc(dest, &layouts, parser, &simple_posts_data, post, config)?;
}

Ok(())
Expand Down Expand Up @@ -280,6 +254,43 @@ fn find_page_files(source: &Path, collection: &Collection) -> Result<files::File
page_files.build()
}

fn find_layouts(layouts: &Path) -> Result<files::Files> {
let mut files = files::FilesBuilder::new(layouts)?;
files.ignore_hidden(false)?;
files.build()
}

fn parse_layouts(files: files::Files) -> HashMap<String, String> {
let (entries, errors): (Vec<_>, Vec<_>) = files
.files()
.map(|file_path| {
let rel_src = file_path
.strip_prefix(files.root())
.expect("file was found under the root");

let layout_data =
files::read_file(&file_path)
.map_err(|e| format!("Failed to load layout {:?}: {}", rel_src, e))?;

let path = rel_src
.to_str()
.ok_or_else(|| format!("File name not valid liquid path: {:?}", rel_src))?
.to_owned();

Ok((path, layout_data))
})
.partition(Result::is_ok);

for error in errors {
warn!("{}", error.expect_err("partition to filter out oks"));
}

entries
.into_iter()
.map(|entry| entry.expect("partition to filter out errors"))
.collect()
}

fn parse_pages(page_files: &files::Files,
collection: &Collection,
source: &Path)
Expand Down
24 changes: 8 additions & 16 deletions src/document.rs
@@ -1,5 +1,4 @@
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::default::Default;
use std::path::{Path, PathBuf};
use std::clone::Clone;
Expand Down Expand Up @@ -326,23 +325,16 @@ impl Document {
pub fn render(&mut self,
globals: &liquid::Object,
parser: &cobalt_model::Liquid,
layouts_dir: &Path,
layouts_cache: &mut HashMap<String, String>)
layouts: &HashMap<String, String>)
-> Result<String> {
if let Some(ref layout) = self.front.layout {
let layout_data_ref = match layouts_cache.entry(layout.to_owned()) {
Entry::Vacant(vacant) => {
let layout_data = files::read_file(layouts_dir.join(layout))
.map_err(|e| {
format!("Layout {} can not be read (defined in {:?}): {}",
layout,
self.file_path,
e)
})?;
vacant.insert(layout_data)
}
Entry::Occupied(occupied) => occupied.into_mut(),
};
let layout_data_ref = layouts
.get(layout)
.ok_or_else(|| {
format!("Layout {} does not exist (referenced in {:?}).",
layout,
self.file_path)
})?;

let template = parser
.parse(layout_data_ref)
Expand Down
2 changes: 1 addition & 1 deletion tests/mod.rs
Expand Up @@ -185,7 +185,7 @@ pub fn no_extends_error() {
let err = run_test("no_extends_error");
assert!(err.is_err());
assert_contains!(format!("{}", err.unwrap_err().display_chain()),
"Layout default_nonexistent.liquid can not be read (defined in \
"Layout default_nonexistent.liquid does not exist (referenced in \
\"index.html\")");
}

Expand Down

0 comments on commit b2cbe13

Please sign in to comment.