Skip to content

Commit 21f2435

Browse files
authored
Merge pull request #2802 from ehuss/with-replace
Change with_renderer/with_preprocessor to overwrite
2 parents 0f0d1f3 + 338a9b4 commit 21f2435

File tree

8 files changed

+81
-43
lines changed

8 files changed

+81
-43
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ font-awesome-as-a-crate = "0.3.0"
3737
futures-util = "0.3.31"
3838
handlebars = "6.3.2"
3939
hex = "0.4.3"
40+
indexmap = "2.10.0"
4041
ignore = "0.4.23"
4142
log = "0.4.27"
4243
mdbook-core = { path = "crates/mdbook-core" }

crates/mdbook-driver/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ rust-version.workspace = true
99

1010
[dependencies]
1111
anyhow.workspace = true
12+
indexmap.workspace = true
1213
log.workspace = true
1314
mdbook-core.workspace = true
1415
mdbook-html.workspace = true

crates/mdbook-driver/src/mdbook.rs

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use crate::builtin_renderers::{CmdRenderer, MarkdownRenderer};
55
use crate::init::BookBuilder;
66
use crate::load::{load_book, load_book_from_disk};
77
use anyhow::{Context, Error, Result, bail};
8+
use indexmap::IndexMap;
89
use log::{debug, error, info, log_enabled, trace, warn};
910
use mdbook_core::book::{Book, BookItem, BookItems};
1011
use mdbook_core::config::{Config, RustEdition};
@@ -28,14 +29,18 @@ mod tests;
2829
pub struct MDBook {
2930
/// The book's root directory.
3031
pub root: PathBuf,
32+
3133
/// The configuration used to tweak now a book is built.
3234
pub config: Config,
35+
3336
/// A representation of the book's contents in memory.
3437
pub book: Book,
35-
renderers: Vec<Box<dyn Renderer>>,
3638

37-
/// List of pre-processors to be run on the book.
38-
preprocessors: Vec<Box<dyn Preprocessor>>,
39+
/// Renderers to execute.
40+
renderers: IndexMap<String, Box<dyn Renderer>>,
41+
42+
/// Pre-processors to be run on the book.
43+
preprocessors: IndexMap<String, Box<dyn Preprocessor>>,
3944
}
4045

4146
impl MDBook {
@@ -156,7 +161,7 @@ impl MDBook {
156161
pub fn build(&self) -> Result<()> {
157162
info!("Book building has started");
158163

159-
for renderer in &self.renderers {
164+
for renderer in self.renderers.values() {
160165
self.execute_build_process(&**renderer)?;
161166
}
162167

@@ -171,7 +176,7 @@ impl MDBook {
171176
renderer.name().to_string(),
172177
);
173178
let mut preprocessed_book = self.book.clone();
174-
for preprocessor in &self.preprocessors {
179+
for preprocessor in self.preprocessors.values() {
175180
if preprocessor_should_run(&**preprocessor, renderer, &self.config)? {
176181
debug!("Running the {} preprocessor.", preprocessor.name());
177182
preprocessed_book = preprocessor.run(&preprocess_ctx, preprocessed_book)?;
@@ -207,13 +212,15 @@ impl MDBook {
207212
/// The only requirement is that your renderer implement the [`Renderer`]
208213
/// trait.
209214
pub fn with_renderer<R: Renderer + 'static>(&mut self, renderer: R) -> &mut Self {
210-
self.renderers.push(Box::new(renderer));
215+
self.renderers
216+
.insert(renderer.name().to_string(), Box::new(renderer));
211217
self
212218
}
213219

214220
/// Register a [`Preprocessor`] to be used when rendering the book.
215221
pub fn with_preprocessor<P: Preprocessor + 'static>(&mut self, preprocessor: P) -> &mut Self {
216-
self.preprocessors.push(Box::new(preprocessor));
222+
self.preprocessors
223+
.insert(preprocessor.name().to_string(), Box::new(preprocessor));
217224
self
218225
}
219226

@@ -258,10 +265,9 @@ impl MDBook {
258265

259266
// Index Preprocessor is disabled so that chapter paths
260267
// continue to point to the actual markdown files.
261-
self.preprocessors = determine_preprocessors(&self.config, &self.root)?
262-
.into_iter()
263-
.filter(|pre| pre.name() != IndexPreprocessor::NAME)
264-
.collect();
268+
self.preprocessors = determine_preprocessors(&self.config, &self.root)?;
269+
self.preprocessors
270+
.shift_remove_entry(IndexPreprocessor::NAME);
265271
let (book, _) = self.preprocess_book(&TestRenderer)?;
266272

267273
let color_output = std::io::stderr().is_terminal();
@@ -399,24 +405,25 @@ struct OutputConfig {
399405
}
400406

401407
/// Look at the `Config` and try to figure out what renderers to use.
402-
fn determine_renderers(config: &Config) -> Result<Vec<Box<dyn Renderer>>> {
403-
let mut renderers = Vec::new();
408+
fn determine_renderers(config: &Config) -> Result<IndexMap<String, Box<dyn Renderer>>> {
409+
let mut renderers = IndexMap::new();
404410

405411
let outputs = config.outputs::<OutputConfig>()?;
406412
renderers.extend(outputs.into_iter().map(|(key, table)| {
407-
if key == "html" {
413+
let renderer = if key == "html" {
408414
Box::new(HtmlHandlebars::new()) as Box<dyn Renderer>
409415
} else if key == "markdown" {
410416
Box::new(MarkdownRenderer::new()) as Box<dyn Renderer>
411417
} else {
412418
let command = table.command.unwrap_or_else(|| format!("mdbook-{key}"));
413-
Box::new(CmdRenderer::new(key, command))
414-
}
419+
Box::new(CmdRenderer::new(key.clone(), command))
420+
};
421+
(key, renderer)
415422
}));
416423

417424
// if we couldn't find anything, add the HTML renderer as a default
418425
if renderers.is_empty() {
419-
renderers.push(Box::new(HtmlHandlebars::new()));
426+
renderers.insert("html".to_string(), Box::new(HtmlHandlebars::new()));
420427
}
421428

422429
Ok(renderers)
@@ -442,7 +449,10 @@ struct PreprocessorConfig {
442449
}
443450

444451
/// Look at the `MDBook` and try to figure out what preprocessors to run.
445-
fn determine_preprocessors(config: &Config, root: &Path) -> Result<Vec<Box<dyn Preprocessor>>> {
452+
fn determine_preprocessors(
453+
config: &Config,
454+
root: &Path,
455+
) -> Result<IndexMap<String, Box<dyn Preprocessor>>> {
446456
// Collect the names of all preprocessors intended to be run, and the order
447457
// in which they should be run.
448458
let mut preprocessor_names = TopologicalSort::<String>::new();
@@ -490,7 +500,7 @@ fn determine_preprocessors(config: &Config, root: &Path) -> Result<Vec<Box<dyn P
490500
}
491501

492502
// Now that all links have been established, queue preprocessors in a suitable order
493-
let mut preprocessors = Vec::with_capacity(preprocessor_names.len());
503+
let mut preprocessors = IndexMap::with_capacity(preprocessor_names.len());
494504
// `pop_all()` returns an empty vector when no more items are not being depended upon
495505
for mut names in std::iter::repeat_with(|| preprocessor_names.pop_all())
496506
.take_while(|names| !names.is_empty())
@@ -516,14 +526,14 @@ fn determine_preprocessors(config: &Config, root: &Path) -> Result<Vec<Box<dyn P
516526
.to_owned()
517527
.unwrap_or_else(|| format!("mdbook-{name}"));
518528
Box::new(CmdPreprocessor::new(
519-
name,
529+
name.clone(),
520530
command,
521531
root.to_owned(),
522532
table.optional,
523533
))
524534
}
525535
};
526-
preprocessors.push(preprocessor);
536+
preprocessors.insert(name, preprocessor);
527537
}
528538
}
529539

crates/mdbook-driver/src/mdbook/tests.rs

Lines changed: 8 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ fn can_determine_third_party_preprocessors() {
8585

8686
let got = determine_preprocessors(&cfg, Path::new("")).unwrap();
8787

88-
assert!(got.into_iter().any(|p| p.name() == "random"));
88+
assert!(got.contains_key("random"));
8989
}
9090

9191
#[test]
@@ -143,19 +143,12 @@ fn preprocessor_order_is_honored() {
143143
let cfg = Config::from_str(cfg_str).unwrap();
144144

145145
let preprocessors = determine_preprocessors(&cfg, Path::new("")).unwrap();
146-
let index = |name| {
147-
preprocessors
148-
.iter()
149-
.enumerate()
150-
.find(|(_, preprocessor)| preprocessor.name() == name)
151-
.unwrap()
152-
.0
153-
};
146+
let index = |name| preprocessors.get_index_of(name).unwrap();
154147
let assert_before = |before, after| {
155148
if index(before) >= index(after) {
156149
eprintln!("Preprocessor order:");
157-
for preprocessor in &preprocessors {
158-
eprintln!(" {}", preprocessor.name());
150+
for preprocessor in preprocessors.keys() {
151+
eprintln!(" {}", preprocessor);
159152
}
160153
panic!("{before} should come before {after}");
161154
}
@@ -193,11 +186,8 @@ fn dependencies_dont_register_undefined_preprocessors() {
193186

194187
let preprocessors = determine_preprocessors(&cfg, Path::new("")).unwrap();
195188

196-
assert!(
197-
!preprocessors
198-
.iter()
199-
.any(|preprocessor| preprocessor.name() == "random")
200-
);
189+
// Does not contain "random"
190+
assert_eq!(preprocessors.keys().collect::<Vec<_>>(), ["index", "links"]);
201191
}
202192

203193
#[test]
@@ -214,11 +204,8 @@ fn dependencies_dont_register_builtin_preprocessors_if_disabled() {
214204

215205
let preprocessors = determine_preprocessors(&cfg, Path::new("")).unwrap();
216206

217-
assert!(
218-
!preprocessors
219-
.iter()
220-
.any(|preprocessor| preprocessor.name() == "links")
221-
);
207+
// Does not contain "links"
208+
assert_eq!(preprocessors.keys().collect::<Vec<_>>(), ["random"]);
222209
}
223210

224211
#[test]

tests/testsuite/book_test.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ enum StatusCode {
2222
pub struct BookTest {
2323
/// The temp directory where the test should perform its work.
2424
pub dir: PathBuf,
25-
assert: snapbox::Assert,
25+
/// Snapshot assertion support.
26+
pub assert: snapbox::Assert,
2627
/// This indicates whether or not the book has been built.
2728
built: bool,
2829
}

tests/testsuite/preprocessor.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,3 +180,22 @@ fn missing_optional_not_fatal() {
180180
"#]]);
181181
});
182182
}
183+
184+
// with_preprocessor of an existing name.
185+
#[test]
186+
fn with_preprocessor_same_name() {
187+
let mut test = BookTest::init(|_| {});
188+
test.change_file(
189+
"book.toml",
190+
"[preprocessor.dummy]\n\
191+
command = 'mdbook-preprocessor-does-not-exist'\n",
192+
);
193+
let spy: Arc<Mutex<Inner>> = Default::default();
194+
let mut book = test.load_book();
195+
book.with_preprocessor(Spy(Arc::clone(&spy)));
196+
// Unfortunately this is unable to capture the output when using the API.
197+
book.build().unwrap();
198+
let inner = spy.lock().unwrap();
199+
assert_eq!(inner.run_count, 1);
200+
assert_eq!(inner.rendered_with, ["html"]);
201+
}

tests/testsuite/renderer.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,3 +241,21 @@ fn relative_command_path() {
241241
})
242242
.check_file("book/output", "test");
243243
}
244+
245+
// with_renderer of an existing name.
246+
#[test]
247+
fn with_renderer_same_name() {
248+
let mut test = BookTest::init(|_| {});
249+
test.change_file(
250+
"book.toml",
251+
"[output.dummy]\n\
252+
command = 'mdbook-renderer-does-not-exist'\n",
253+
);
254+
let spy: Arc<Mutex<Inner>> = Default::default();
255+
let mut book = test.load_book();
256+
book.with_renderer(Spy(Arc::clone(&spy)));
257+
// Unfortunately this is unable to capture the output when using the API.
258+
book.build().unwrap();
259+
let inner = spy.lock().unwrap();
260+
assert_eq!(inner.run_count, 1);
261+
}

0 commit comments

Comments
 (0)