@@ -19,6 +19,9 @@ use crate::{
19
19
template:: Template ,
20
20
} ;
21
21
22
+ /// A set of partials to be included in a Liquid template.
23
+ type PartialsBuilder = liquid:: partials:: EagerCompiler < liquid:: partials:: InMemorySource > ;
24
+
22
25
/// Executes a template to the point where it is ready to generate
23
26
/// artefacts.
24
27
pub struct Run {
@@ -104,6 +107,9 @@ impl Run {
104
107
} ;
105
108
}
106
109
110
+ let partials = self . partials ( ) ?;
111
+ let parser = Self :: template_parser ( partials) ?;
112
+
107
113
self . validate_provided_values ( ) ?;
108
114
109
115
let files = match self . template . content_dir ( ) {
@@ -112,15 +118,15 @@ impl Run {
112
118
let from = path
113
119
. absolutize ( )
114
120
. context ( "Failed to get absolute path of template directory" ) ?;
115
- self . included_files ( & from, & to) ?
121
+ self . included_files ( & from, & to, & parser ) ?
116
122
}
117
123
} ;
118
124
119
125
let snippets = self
120
126
. template
121
127
. snippets ( & self . options . variant )
122
128
. iter ( )
123
- . map ( |( id, path) | self . snippet_operation ( id, path) )
129
+ . map ( |( id, path) | self . snippet_operation ( id, path, & parser ) )
124
130
. collect :: < anyhow:: Result < Vec < _ > > > ( ) ?;
125
131
126
132
let extras = self
@@ -151,7 +157,12 @@ impl Run {
151
157
}
152
158
}
153
159
154
- fn included_files ( & self , from : & Path , to : & Path ) -> anyhow:: Result < Vec < RenderOperation > > {
160
+ fn included_files (
161
+ & self ,
162
+ from : & Path ,
163
+ to : & Path ,
164
+ parser : & liquid:: Parser ,
165
+ ) -> anyhow:: Result < Vec < RenderOperation > > {
155
166
let gitignore = ".gitignore" ;
156
167
let mut all_content_files = Self :: list_content_files ( from) ?;
157
168
// If user asked for no_vcs
@@ -164,7 +175,7 @@ impl Run {
164
175
let included_files =
165
176
self . template
166
177
. included_files ( from, all_content_files, & self . options . variant ) ;
167
- let template_contents = self . read_all ( included_files) ?;
178
+ let template_contents = self . read_all ( included_files, parser ) ?;
168
179
let outputs = Self :: to_output_paths ( from, to, template_contents) ;
169
180
let file_ops = outputs
170
181
. into_iter ( )
@@ -262,7 +273,12 @@ impl Run {
262
273
}
263
274
}
264
275
265
- fn snippet_operation ( & self , id : & str , snippet_file : & str ) -> anyhow:: Result < RenderOperation > {
276
+ fn snippet_operation (
277
+ & self ,
278
+ id : & str ,
279
+ snippet_file : & str ,
280
+ parser : & liquid:: Parser ,
281
+ ) -> anyhow:: Result < RenderOperation > {
266
282
let snippets_dir = self
267
283
. template
268
284
. snippets_dir ( )
@@ -271,7 +287,7 @@ impl Run {
271
287
let abs_snippet_file = snippets_dir. join ( snippet_file) ;
272
288
let file_content = std:: fs:: read ( abs_snippet_file)
273
289
. with_context ( || format ! ( "Error reading snippet file {}" , snippet_file) ) ?;
274
- let content = TemplateContent :: infer_from_bytes ( file_content, & Self :: template_parser ( ) )
290
+ let content = TemplateContent :: infer_from_bytes ( file_content, parser )
275
291
. with_context ( || format ! ( "Error parsing snippet file {}" , snippet_file) ) ?;
276
292
277
293
match id {
@@ -356,11 +372,14 @@ impl Run {
356
372
}
357
373
358
374
// TODO: async when we know where things sit
359
- fn read_all ( & self , paths : Vec < PathBuf > ) -> anyhow:: Result < Vec < ( PathBuf , TemplateContent ) > > {
360
- let template_parser = Self :: template_parser ( ) ;
375
+ fn read_all (
376
+ & self ,
377
+ paths : Vec < PathBuf > ,
378
+ template_parser : & liquid:: Parser ,
379
+ ) -> anyhow:: Result < Vec < ( PathBuf , TemplateContent ) > > {
361
380
let contents = paths
362
381
. iter ( )
363
- . map ( |path| TemplateContent :: infer_from_bytes ( std:: fs:: read ( path) ?, & template_parser) )
382
+ . map ( |path| TemplateContent :: infer_from_bytes ( std:: fs:: read ( path) ?, template_parser) )
364
383
. collect :: < Result < Vec < _ > , _ > > ( ) ?;
365
384
// Strip optional .tmpl extension
366
385
// Templates can use this if they don't want to store files with their final extensions
@@ -394,16 +413,45 @@ impl Run {
394
413
pathdiff:: diff_paths ( source, src_dir) . map ( |rel| ( dest_dir. join ( rel) , cont) )
395
414
}
396
415
397
- fn template_parser ( ) -> liquid:: Parser {
416
+ fn template_parser (
417
+ partials : impl liquid:: partials:: PartialCompiler ,
418
+ ) -> anyhow:: Result < liquid:: Parser > {
398
419
let builder = liquid:: ParserBuilder :: with_stdlib ( )
420
+ . partials ( partials)
399
421
. filter ( crate :: filters:: KebabCaseFilterParser )
400
422
. filter ( crate :: filters:: PascalCaseFilterParser )
401
423
. filter ( crate :: filters:: DottedPascalCaseFilterParser )
402
424
. filter ( crate :: filters:: SnakeCaseFilterParser )
403
425
. filter ( crate :: filters:: HttpWildcardFilterParser ) ;
404
426
builder
405
427
. build ( )
406
- . expect ( "can't fail due to no partials support" )
428
+ . context ( "Template error: unable to build parser" )
429
+ }
430
+
431
+ fn partials ( & self ) -> anyhow:: Result < impl liquid:: partials:: PartialCompiler > {
432
+ let mut partials = PartialsBuilder :: empty ( ) ;
433
+
434
+ if let Some ( partials_dir) = self . template . partials_dir ( ) {
435
+ let partials_dir = std:: fs:: read_dir ( partials_dir)
436
+ . context ( "Error opening template partials directory" ) ?;
437
+ for partial_file in partials_dir {
438
+ let partial_file =
439
+ partial_file. context ( "Error scanning template partials directory" ) ?;
440
+ if !partial_file. file_type ( ) . is_ok_and ( |t| t. is_file ( ) ) {
441
+ anyhow:: bail!( "Non-file in partials directory: {partial_file:?}" ) ;
442
+ }
443
+ let partial_name = partial_file
444
+ . file_name ( )
445
+ . into_string ( )
446
+ . map_err ( |f| anyhow ! ( "Unusable partial name {f:?}" ) ) ?;
447
+ let partial_file = partial_file. path ( ) ;
448
+ let content = std:: fs:: read_to_string ( & partial_file)
449
+ . with_context ( || format ! ( "Invalid partial template {partial_file:?}" ) ) ?;
450
+ partials. add ( partial_name, content) ;
451
+ }
452
+ }
453
+
454
+ Ok ( partials)
407
455
}
408
456
}
409
457
@@ -418,7 +466,8 @@ mod test {
418
466
"kebabby" : "originally-kebabby" ,
419
467
"dotted" : "originally.semi-dotted"
420
468
} ) ;
421
- let parser = Run :: template_parser ( ) ;
469
+ let no_partials = super :: PartialsBuilder :: empty ( ) ;
470
+ let parser = Run :: template_parser ( no_partials) . unwrap ( ) ;
422
471
423
472
let eval = |s : & str | parser. parse ( s) . unwrap ( ) . render ( & data) . unwrap ( ) ;
424
473
0 commit comments