@@ -2,14 +2,19 @@ use crate::db::Pool;
22use crate :: error:: Result ;
33use arc_swap:: ArcSwap ;
44use chrono:: { DateTime , Utc } ;
5+ use failure:: ResultExt ;
56use notify:: { watcher, RecursiveMode , Watcher } ;
67use postgres:: Connection ;
78use serde_json:: Value ;
89use std:: collections:: HashMap ;
10+ use std:: path:: PathBuf ;
911use std:: sync:: { mpsc:: channel, Arc } ;
1012use std:: thread;
1113use std:: time:: Duration ;
1214use tera:: { Result as TeraResult , Tera } ;
15+ use walkdir:: WalkDir ;
16+
17+ const TEMPLATES_DIRECTORY : & str = "tera-templates" ;
1318
1419/// Holds all data relevant to templating
1520#[ derive( Debug ) ]
@@ -84,7 +89,16 @@ fn load_rustc_resource_suffix(conn: &Connection) -> Result<String> {
8489}
8590
8691pub ( super ) fn load_templates ( conn : & Connection ) -> Result < Tera > {
87- let mut tera = Tera :: new ( "tera-templates/**/*" ) ?;
92+ // This uses a custom function to find the templates in the filesystem instead of Tera's
93+ // builtin way (passing a glob expression to Tera::new), speeding up the startup of the
94+ // application and running the tests.
95+ //
96+ // The problem with Tera's template loading code is, it walks all the files in the current
97+ // directory and matches them against the provided glob expression. Unfortunately this means
98+ // Tera will walk all the rustwide workspaces, the git repository and a bunch of other
99+ // unrelated data, slowing down the search a lot.
100+ let mut tera = Tera :: default ( ) ;
101+ tera. add_template_files ( find_templates_in_filesystem ( TEMPLATES_DIRECTORY ) ?) ?;
88102
89103 // This function will return any global alert, if present.
90104 ReturnValue :: add_function_to (
@@ -120,6 +134,32 @@ pub(super) fn load_templates(conn: &Connection) -> Result<Tera> {
120134 Ok ( tera)
121135}
122136
137+ fn find_templates_in_filesystem ( base : & str ) -> Result < Vec < ( PathBuf , Option < String > ) > > {
138+ let root = std:: fs:: canonicalize ( base) ?;
139+
140+ let mut files = Vec :: new ( ) ;
141+ for entry in WalkDir :: new ( & root) {
142+ let entry = entry?;
143+ let path = entry. path ( ) ;
144+
145+ if !entry. metadata ( ) ?. is_file ( ) {
146+ continue ;
147+ }
148+
149+ // Strip the root directory from the path and use it as the template name.
150+ let name = path
151+ . strip_prefix ( & root)
152+ . with_context ( |_| format ! ( "{} is not a child of {}" , path. display( ) , root. display( ) ) ) ?
153+ . to_str ( )
154+ . ok_or_else ( || failure:: format_err!( "path {} is not UTF-8" , path. display( ) ) ) ?
155+ . to_string ( ) ;
156+
157+ files. push ( ( path. to_path_buf ( ) , Some ( name) ) ) ;
158+ }
159+
160+ Ok ( files)
161+ }
162+
123163/// Simple function that returns the pre-defined value.
124164struct ReturnValue {
125165 name : & ' static str ,
0 commit comments