Skip to content

Commit

Permalink
web: speed up loading Tera templates
Browse files Browse the repository at this point in the history
  • Loading branch information
pietroalbini committed Jun 28, 2020
1 parent e7ee68b commit 9c9647d
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 9 deletions.
7 changes: 4 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 6 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ staticfile = { version = "0.4", features = [ "cache" ] }
tempfile = "3.1.0"

# Templating
tera = { version = "1.3.0", features = ["builtins"] }
tera = { version = "1.3.1", features = ["builtins"] }
walkdir = "2"

# Template hot-reloading
arc-swap = "0.4.6"
Expand All @@ -67,17 +68,17 @@ notify = "4.0.15"
chrono = { version = "0.4.11", features = ["serde"] }
time = "0.1" # TODO: Remove once `iron` is removed

[dependencies.postgres]
version = "0.15"
features = ["with-chrono", "with-serde_json"]

[target.'cfg(not(windows))'.dependencies]
# Process information
procfs = "0.7"

[target.'cfg(windows)'.dependencies]
path-slash = "0.1.1"

[dependencies.postgres]
version = "0.15"
features = ["with-chrono", "with-serde_json"]

[dev-dependencies]
once_cell = "1.2.0"
criterion = "0.3"
Expand Down
42 changes: 41 additions & 1 deletion src/web/page/templates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,19 @@ use crate::db::Pool;
use crate::error::Result;
use arc_swap::ArcSwap;
use chrono::{DateTime, Utc};
use failure::ResultExt;
use notify::{watcher, RecursiveMode, Watcher};
use postgres::Connection;
use serde_json::Value;
use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::{mpsc::channel, Arc};
use std::thread;
use std::time::Duration;
use tera::{Result as TeraResult, Tera};
use walkdir::WalkDir;

const TEMPLATES_DIRECTORY: &str = "tera-templates";

/// Holds all data relevant to templating
#[derive(Debug)]
Expand Down Expand Up @@ -84,7 +89,16 @@ fn load_rustc_resource_suffix(conn: &Connection) -> Result<String> {
}

pub(super) fn load_templates(conn: &Connection) -> Result<Tera> {
let mut tera = Tera::new("tera-templates/**/*")?;
// This uses a custom function to find the templates in the filesystem instead of Tera's
// builtin way (passing a glob expression to Tera::new), speeding up the startup of the
// application and running the tests.
//
// The problem with Tera's template loading code is, it walks all the files in the current
// directory and matches them against the provided glob expression. Unfortunately this means
// Tera will walk all the rustwide workspaces, the git repository and a bunch of other
// unrelated data, slowing down the search a lot.
let mut tera = Tera::default();
tera.add_template_files(find_templates_in_filesystem(TEMPLATES_DIRECTORY)?)?;

// This function will return any global alert, if present.
ReturnValue::add_function_to(
Expand Down Expand Up @@ -120,6 +134,32 @@ pub(super) fn load_templates(conn: &Connection) -> Result<Tera> {
Ok(tera)
}

fn find_templates_in_filesystem(base: &str) -> Result<Vec<(PathBuf, Option<String>)>> {
let root = std::fs::canonicalize(base)?;

let mut files = Vec::new();
for entry in WalkDir::new(&root) {
let entry = entry?;
let path = entry.path();

if !entry.metadata()?.is_file() {
continue;
}

// Strip the root directory from the path and use it as the template name.
let name = path
.strip_prefix(&root)
.with_context(|_| format!("{} is not a child of {}", path.display(), root.display()))?
.to_str()
.ok_or_else(|| failure::format_err!("path {} is not UTF-8", path.display()))?
.to_string();

files.push((path.to_path_buf(), Some(name)));
}

Ok(files)
}

/// Simple function that returns the pre-defined value.
struct ReturnValue {
name: &'static str,
Expand Down

0 comments on commit 9c9647d

Please sign in to comment.