@@ -149,23 +149,39 @@ impl MDBook {
149
149
pub fn build ( & self ) -> Result < ( ) > {
150
150
info ! ( "Book building has started" ) ;
151
151
152
+ for renderer in & self . renderers {
153
+ self . execute_build_process ( & * * renderer) ?;
154
+ }
155
+
156
+ Ok ( ( ) )
157
+ }
158
+
159
+ /// Run the entire build process for a particular `Renderer`.
160
+ fn execute_build_process ( & self , renderer : & Renderer ) -> Result < ( ) > {
152
161
let mut preprocessed_book = self . book . clone ( ) ;
153
- let preprocess_ctx = PreprocessorContext :: new ( self . root . clone ( ) , self . config . clone ( ) ) ;
162
+ let preprocess_ctx = PreprocessorContext :: new ( self . root . clone ( ) ,
163
+ self . config . clone ( ) ,
164
+ renderer. name ( ) . to_string ( ) ) ;
154
165
155
166
for preprocessor in & self . preprocessors {
156
- debug ! ( "Running the {} preprocessor." , preprocessor. name( ) ) ;
157
- preprocessor. run ( & preprocess_ctx, & mut preprocessed_book) ?;
167
+ if preprocessor_should_run ( & * * preprocessor, renderer, & self . config ) {
168
+ debug ! ( "Running the {} preprocessor." , preprocessor. name( ) ) ;
169
+ preprocessed_book =
170
+ preprocessor. run ( & preprocess_ctx, preprocessed_book) ?;
171
+ }
158
172
}
159
173
160
- for renderer in & self . renderers {
161
- info ! ( "Running the {} backend" , renderer. name( ) ) ;
162
- self . run_renderer ( & preprocessed_book, renderer. as_ref ( ) ) ?;
163
- }
174
+ info ! ( "Running the {} backend" , renderer. name( ) ) ;
175
+ self . render ( & preprocessed_book, renderer) ?;
164
176
165
177
Ok ( ( ) )
166
178
}
167
179
168
- fn run_renderer ( & self , preprocessed_book : & Book , renderer : & Renderer ) -> Result < ( ) > {
180
+ fn render (
181
+ & self ,
182
+ preprocessed_book : & Book ,
183
+ renderer : & Renderer ,
184
+ ) -> Result < ( ) > {
169
185
let name = renderer. name ( ) ;
170
186
let build_dir = self . build_dir_for ( name) ;
171
187
if build_dir. exists ( ) {
@@ -215,13 +231,16 @@ impl MDBook {
215
231
216
232
let temp_dir = TempFileBuilder :: new ( ) . prefix ( "mdbook-" ) . tempdir ( ) ?;
217
233
218
- let preprocess_context = PreprocessorContext :: new ( self . root . clone ( ) , self . config . clone ( ) ) ;
234
+ // FIXME: Is "test" the proper renderer name to use here?
235
+ let preprocess_context = PreprocessorContext :: new ( self . root . clone ( ) ,
236
+ self . config . clone ( ) ,
237
+ "test" . to_string ( ) ) ;
219
238
220
- LinkPreprocessor :: new ( ) . run ( & preprocess_context, & mut self . book ) ?;
239
+ let book = LinkPreprocessor :: new ( ) . run ( & preprocess_context, self . book . clone ( ) ) ?;
221
240
// Index Preprocessor is disabled so that chapter paths continue to point to the
222
241
// actual markdown files.
223
242
224
- for item in self . iter ( ) {
243
+ for item in book . iter ( ) {
225
244
if let BookItem :: Chapter ( ref ch) = * item {
226
245
if !ch. path . as_os_str ( ) . is_empty ( ) {
227
246
let path = self . source_dir ( ) . join ( & ch. path ) ;
@@ -330,19 +349,32 @@ fn default_preprocessors() -> Vec<Box<Preprocessor>> {
330
349
]
331
350
}
332
351
352
+ fn is_default_preprocessor ( pre : & Preprocessor ) -> bool {
353
+ let name = pre. name ( ) ;
354
+ name == LinkPreprocessor :: NAME || name == IndexPreprocessor :: NAME
355
+ }
356
+
333
357
/// Look at the `MDBook` and try to figure out what preprocessors to run.
334
358
fn determine_preprocessors ( config : & Config ) -> Result < Vec < Box < Preprocessor > > > {
335
- let preprocess_list = match config. build . preprocess {
336
- Some ( ref p) => p,
359
+ let preprocessor_keys = config. get ( "preprocessor" )
360
+ . and_then ( |value| value. as_table ( ) )
361
+ . map ( |table| table. keys ( ) ) ;
362
+
363
+ let mut preprocessors = if config. build . use_default_preprocessors {
364
+ default_preprocessors ( )
365
+ } else {
366
+ Vec :: new ( )
367
+ } ;
368
+
369
+ let preprocessor_keys = match preprocessor_keys {
370
+ Some ( keys) => keys,
337
371
// If no preprocessor field is set, default to the LinkPreprocessor and
338
372
// IndexPreprocessor. This allows you to disable default preprocessors
339
373
// by setting "preprocess" to an empty list.
340
- None => return Ok ( default_preprocessors ( ) ) ,
374
+ None => return Ok ( preprocessors ) ,
341
375
} ;
342
376
343
- let mut preprocessors: Vec < Box < Preprocessor > > = Vec :: new ( ) ;
344
-
345
- for key in preprocess_list {
377
+ for key in preprocessor_keys {
346
378
match key. as_ref ( ) {
347
379
"links" => preprocessors. push ( Box :: new ( LinkPreprocessor :: new ( ) ) ) ,
348
380
"index" => preprocessors. push ( Box :: new ( IndexPreprocessor :: new ( ) ) ) ,
@@ -366,6 +398,31 @@ fn interpret_custom_renderer(key: &str, table: &Value) -> Box<Renderer> {
366
398
Box :: new ( CmdRenderer :: new ( key. to_string ( ) , command. to_string ( ) ) )
367
399
}
368
400
401
+ /// Check whether we should run a particular `Preprocessor` in combination
402
+ /// with the renderer, falling back to `Preprocessor::supports_renderer()`
403
+ /// method if the user doesn't say anything.
404
+ ///
405
+ /// The `build.use-default-preprocessors` config option can be used to ensure
406
+ /// default preprocessors always run if they support the renderer.
407
+ fn preprocessor_should_run ( preprocessor : & Preprocessor , renderer : & Renderer , cfg : & Config ) -> bool {
408
+ // default preprocessors should be run by default (if supported)
409
+ if cfg. build . use_default_preprocessors && is_default_preprocessor ( preprocessor) {
410
+ return preprocessor. supports_renderer ( renderer. name ( ) ) ;
411
+ }
412
+
413
+ let key = format ! ( "preprocessor.{}.renderers" , preprocessor. name( ) ) ;
414
+ let renderer_name = renderer. name ( ) ;
415
+
416
+ if let Some ( Value :: Array ( ref explicit_renderers) ) = cfg. get ( & key) {
417
+ return explicit_renderers. into_iter ( )
418
+ . filter_map ( |val| val. as_str ( ) )
419
+ . any ( |name| name == renderer_name) ;
420
+ }
421
+
422
+ preprocessor. supports_renderer ( renderer_name)
423
+ }
424
+
425
+
369
426
#[ cfg( test) ]
370
427
mod tests {
371
428
use super :: * ;
@@ -413,8 +470,8 @@ mod tests {
413
470
fn config_defaults_to_link_and_index_preprocessor_if_not_set ( ) {
414
471
let cfg = Config :: default ( ) ;
415
472
416
- // make sure we haven't got anything in the `output ` table
417
- assert ! ( cfg. build . preprocess . is_none( ) ) ;
473
+ // make sure we haven't got anything in the `preprocessor ` table
474
+ assert ! ( cfg. get ( "preprocessor" ) . is_none( ) ) ;
418
475
419
476
let got = determine_preprocessors ( & cfg) ;
420
477
@@ -425,47 +482,88 @@ mod tests {
425
482
}
426
483
427
484
#[ test]
428
- fn config_doesnt_default_if_empty ( ) {
485
+ fn use_default_preprocessors_works ( ) {
486
+ let mut cfg = Config :: default ( ) ;
487
+ cfg. build . use_default_preprocessors = false ;
488
+
489
+ let got = determine_preprocessors ( & cfg) . unwrap ( ) ;
490
+
491
+ assert_eq ! ( got. len( ) , 0 ) ;
492
+ }
493
+
494
+ #[ test]
495
+ fn config_complains_if_unimplemented_preprocessor ( ) {
429
496
let cfg_str: & ' static str = r#"
430
497
[book]
431
498
title = "Some Book"
432
499
500
+ [preprocessor.random]
501
+
433
502
[build]
434
503
build-dir = "outputs"
435
504
create-missing = false
436
- preprocess = []
437
505
"# ;
438
506
439
507
let cfg = Config :: from_str ( cfg_str) . unwrap ( ) ;
440
508
441
- // make sure we have something in the `output ` table
442
- assert ! ( cfg. build . preprocess . is_some( ) ) ;
509
+ // make sure the `preprocessor.random ` table exists
510
+ assert ! ( cfg. get_preprocessor ( "random" ) . is_some( ) ) ;
443
511
444
512
let got = determine_preprocessors ( & cfg) ;
445
513
446
- assert ! ( got. is_ok( ) ) ;
447
- assert ! ( got. unwrap( ) . is_empty( ) ) ;
514
+ assert ! ( got. is_err( ) ) ;
448
515
}
449
516
450
517
#[ test]
451
- fn config_complains_if_unimplemented_preprocessor ( ) {
518
+ fn config_respects_preprocessor_selection ( ) {
452
519
let cfg_str: & ' static str = r#"
453
- [book]
454
- title = "Some Book"
455
-
456
- [build]
457
- build-dir = "outputs"
458
- create-missing = false
459
- preprocess = ["random"]
520
+ [preprocessor.links]
521
+ renderers = ["html"]
460
522
"# ;
461
523
462
524
let cfg = Config :: from_str ( cfg_str) . unwrap ( ) ;
463
525
464
- // make sure we have something in the `output` table
465
- assert ! ( cfg. build. preprocess. is_some( ) ) ;
526
+ // double-check that we can access preprocessor.links.renderers[0]
527
+ let html = cfg. get_preprocessor ( "links" )
528
+ . and_then ( |links| links. get ( "renderers" ) )
529
+ . and_then ( |renderers| renderers. as_array ( ) )
530
+ . and_then ( |renderers| renderers. get ( 0 ) )
531
+ . and_then ( |renderer| renderer. as_str ( ) )
532
+ . unwrap ( ) ;
533
+ assert_eq ! ( html, "html" ) ;
534
+ let html_renderer = HtmlHandlebars :: default ( ) ;
535
+ let pre = LinkPreprocessor :: new ( ) ;
536
+
537
+ let should_run = preprocessor_should_run ( & pre, & html_renderer, & cfg) ;
538
+ assert ! ( should_run) ;
539
+ }
466
540
467
- let got = determine_preprocessors ( & cfg) ;
541
+ struct BoolPreprocessor ( bool ) ;
542
+ impl Preprocessor for BoolPreprocessor {
543
+ fn name ( & self ) -> & str {
544
+ "bool-preprocessor"
545
+ }
468
546
469
- assert ! ( got. is_err( ) ) ;
547
+ fn run ( & self , _ctx : & PreprocessorContext , _book : Book ) -> Result < Book > {
548
+ unimplemented ! ( )
549
+ }
550
+
551
+ fn supports_renderer ( & self , _renderer : & str ) -> bool {
552
+ self . 0
553
+ }
554
+ }
555
+
556
+ #[ test]
557
+ fn preprocessor_should_run_falls_back_to_supports_renderer_method ( ) {
558
+ let cfg = Config :: default ( ) ;
559
+ let html = HtmlHandlebars :: new ( ) ;
560
+
561
+ let should_be = true ;
562
+ let got = preprocessor_should_run ( & BoolPreprocessor ( should_be) , & html, & cfg) ;
563
+ assert_eq ! ( got, should_be) ;
564
+
565
+ let should_be = false ;
566
+ let got = preprocessor_should_run ( & BoolPreprocessor ( should_be) , & html, & cfg) ;
567
+ assert_eq ! ( got, should_be) ;
470
568
}
471
569
}
0 commit comments