Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 11 additions & 24 deletions crates/mdbook-driver/src/builtin_preprocessors/cmd.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use anyhow::{Context, Result, bail, ensure};
use anyhow::{Context, Result, ensure};
use log::{debug, trace, warn};
use mdbook_core::book::Book;
use mdbook_preprocessor::{Preprocessor, PreprocessorContext};
use shlex::Shlex;
use std::io::{self, Write};
use std::process::{Child, Command, Stdio};
use std::path::PathBuf;
use std::process::{Child, Stdio};

/// A custom preprocessor which will shell out to a 3rd-party program.
///
Expand Down Expand Up @@ -33,12 +33,13 @@ use std::process::{Child, Command, Stdio};
pub struct CmdPreprocessor {
name: String,
cmd: String,
root: PathBuf,
}

impl CmdPreprocessor {
/// Create a new `CmdPreprocessor`.
pub fn new(name: String, cmd: String) -> CmdPreprocessor {
CmdPreprocessor { name, cmd }
pub fn new(name: String, cmd: String, root: PathBuf) -> CmdPreprocessor {
CmdPreprocessor { name, cmd, root }
}

fn write_input_to_child(&self, child: &mut Child, book: &Book, ctx: &PreprocessorContext) {
Expand All @@ -64,22 +65,6 @@ impl CmdPreprocessor {
pub fn cmd(&self) -> &str {
&self.cmd
}

fn command(&self) -> Result<Command> {
let mut words = Shlex::new(&self.cmd);
let executable = match words.next() {
Some(e) => e,
None => bail!("Command string was empty"),
};

let mut cmd = Command::new(executable);

for arg in words {
cmd.arg(arg);
}

Ok(cmd)
}
}

impl Preprocessor for CmdPreprocessor {
Expand All @@ -88,12 +73,13 @@ impl Preprocessor for CmdPreprocessor {
}

fn run(&self, ctx: &PreprocessorContext, book: Book) -> Result<Book> {
let mut cmd = self.command()?;
let mut cmd = crate::compose_command(&self.cmd, &ctx.root)?;

let mut child = cmd
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::inherit())
.current_dir(&self.root)
.spawn()
.with_context(|| {
format!(
Expand Down Expand Up @@ -135,7 +121,7 @@ impl Preprocessor for CmdPreprocessor {
renderer
);

let mut cmd = match self.command() {
let mut cmd = match crate::compose_command(&self.cmd, &self.root) {
Ok(c) => c,
Err(e) => {
warn!(
Expand All @@ -153,6 +139,7 @@ impl Preprocessor for CmdPreprocessor {
.stdin(Stdio::null())
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.current_dir(&self.root)
.status()
.map(|status| status.code() == Some(0));

Expand Down Expand Up @@ -183,8 +170,8 @@ mod tests {

#[test]
fn round_trip_write_and_parse_input() {
let cmd = CmdPreprocessor::new("test".to_string(), "test".to_string());
let md = guide();
let cmd = CmdPreprocessor::new("test".to_string(), "test".to_string(), md.root.clone());
let ctx = PreprocessorContext::new(
md.root.clone(),
md.config.clone(),
Expand Down
32 changes: 3 additions & 29 deletions crates/mdbook-driver/src/builtin_renderers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@
use anyhow::{Context, Result, bail};
use log::{error, info, trace, warn};
use mdbook_renderer::{RenderContext, Renderer};
use shlex::Shlex;
use std::fs;
use std::io::{self, ErrorKind};
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::process::Stdio;

pub use self::markdown_renderer::MarkdownRenderer;

Expand Down Expand Up @@ -49,30 +47,6 @@ impl CmdRenderer {
pub fn new(name: String, cmd: String) -> CmdRenderer {
CmdRenderer { name, cmd }
}

fn compose_command(&self, root: &Path) -> Result<Command> {
let mut words = Shlex::new(&self.cmd);
let exe = match words.next() {
Some(e) => PathBuf::from(e),
None => bail!("Command string was empty"),
};

let exe = if exe.components().count() == 1 {
// Search PATH for the executable.
exe
} else {
// Relative path is relative to book root.
root.join(&exe)
};

let mut cmd = Command::new(exe);

for arg in words {
cmd.arg(arg);
}

Ok(cmd)
}
}

impl CmdRenderer {
Expand Down Expand Up @@ -120,8 +94,8 @@ impl Renderer for CmdRenderer {

let _ = fs::create_dir_all(&ctx.destination);

let mut child = match self
.compose_command(&ctx.root)?
let mut cmd = crate::compose_command(&self.cmd, &ctx.root)?;
let mut child = match cmd
.stdin(Stdio::piped())
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
Expand Down
29 changes: 29 additions & 0 deletions crates/mdbook-driver/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,34 @@ pub mod init;
mod load;
mod mdbook;

use anyhow::{Result, bail};
pub use mdbook::MDBook;
pub use mdbook_core::{book, config, errors};
use shlex::Shlex;
use std::path::{Path, PathBuf};
use std::process::Command;

/// Creates a [`Command`] for command renderers and preprocessors.
fn compose_command(cmd: &str, root: &Path) -> Result<Command> {
let mut words = Shlex::new(cmd);
let exe = match words.next() {
Some(e) => PathBuf::from(e),
None => bail!("Command string was empty"),
};

let exe = if exe.components().count() == 1 {
// Search PATH for the executable.
exe
} else {
// Relative path is relative to book root.
root.join(&exe)
};

let mut cmd = Command::new(exe);

for arg in words {
cmd.arg(arg);
}

Ok(cmd)
}
10 changes: 5 additions & 5 deletions crates/mdbook-driver/src/mdbook.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ impl MDBook {
let book = load_book(src_dir, &config.build)?;

let renderers = determine_renderers(&config)?;
let preprocessors = determine_preprocessors(&config)?;
let preprocessors = determine_preprocessors(&config, &root)?;

Ok(MDBook {
root,
Expand All @@ -93,7 +93,7 @@ impl MDBook {
let book = load_book_from_disk(&summary, src_dir)?;

let renderers = determine_renderers(&config)?;
let preprocessors = determine_preprocessors(&config)?;
let preprocessors = determine_preprocessors(&config, &root)?;

Ok(MDBook {
root,
Expand Down Expand Up @@ -258,7 +258,7 @@ impl MDBook {

// Index Preprocessor is disabled so that chapter paths
// continue to point to the actual markdown files.
self.preprocessors = determine_preprocessors(&self.config)?
self.preprocessors = determine_preprocessors(&self.config, &self.root)?
.into_iter()
.filter(|pre| pre.name() != IndexPreprocessor::NAME)
.collect();
Expand Down Expand Up @@ -440,7 +440,7 @@ struct PreprocessorConfig {
}

/// Look at the `MDBook` and try to figure out what preprocessors to run.
fn determine_preprocessors(config: &Config) -> Result<Vec<Box<dyn Preprocessor>>> {
fn determine_preprocessors(config: &Config, root: &Path) -> Result<Vec<Box<dyn Preprocessor>>> {
// Collect the names of all preprocessors intended to be run, and the order
// in which they should be run.
let mut preprocessor_names = TopologicalSort::<String>::new();
Expand Down Expand Up @@ -513,7 +513,7 @@ fn determine_preprocessors(config: &Config) -> Result<Vec<Box<dyn Preprocessor>>
.command
.to_owned()
.unwrap_or_else(|| format!("mdbook-{name}"));
Box::new(CmdPreprocessor::new(name, command))
Box::new(CmdPreprocessor::new(name, command, root.to_owned()))
}
};
preprocessors.push(preprocessor);
Expand Down
18 changes: 9 additions & 9 deletions crates/mdbook-driver/src/mdbook/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ fn config_defaults_to_link_and_index_preprocessor_if_not_set() {
// make sure we haven't got anything in the `preprocessor` table
assert!(cfg.preprocessors::<toml::Value>().unwrap().is_empty());

let got = determine_preprocessors(&cfg);
let got = determine_preprocessors(&cfg, Path::new(""));

assert!(got.is_ok());
assert_eq!(got.as_ref().unwrap().len(), 2);
Expand All @@ -60,7 +60,7 @@ fn use_default_preprocessors_works() {
let mut cfg = Config::default();
cfg.build.use_default_preprocessors = false;

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

assert_eq!(got.len(), 0);
}
Expand All @@ -83,7 +83,7 @@ fn can_determine_third_party_preprocessors() {
// make sure the `preprocessor.random` table exists
assert!(cfg.get::<Value>("preprocessor.random").unwrap().is_some());

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

assert!(got.into_iter().any(|p| p.name() == "random"));
}
Expand Down Expand Up @@ -114,7 +114,7 @@ fn preprocessor_before_must_be_array() {

let cfg = Config::from_str(cfg_str).unwrap();

assert!(determine_preprocessors(&cfg).is_err());
assert!(determine_preprocessors(&cfg, Path::new("")).is_err());
}

#[test]
Expand All @@ -126,7 +126,7 @@ fn preprocessor_after_must_be_array() {

let cfg = Config::from_str(cfg_str).unwrap();

assert!(determine_preprocessors(&cfg).is_err());
assert!(determine_preprocessors(&cfg, Path::new("")).is_err());
}

#[test]
Expand All @@ -142,7 +142,7 @@ fn preprocessor_order_is_honored() {

let cfg = Config::from_str(cfg_str).unwrap();

let preprocessors = determine_preprocessors(&cfg).unwrap();
let preprocessors = determine_preprocessors(&cfg, Path::new("")).unwrap();
let index = |name| {
preprocessors
.iter()
Expand Down Expand Up @@ -179,7 +179,7 @@ fn cyclic_dependencies_are_detected() {

let cfg = Config::from_str(cfg_str).unwrap();

assert!(determine_preprocessors(&cfg).is_err());
assert!(determine_preprocessors(&cfg, Path::new("")).is_err());
}

#[test]
Expand All @@ -191,7 +191,7 @@ fn dependencies_dont_register_undefined_preprocessors() {

let cfg = Config::from_str(cfg_str).unwrap();

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

assert!(
!preprocessors
Expand All @@ -212,7 +212,7 @@ fn dependencies_dont_register_builtin_preprocessors_if_disabled() {

let cfg = Config::from_str(cfg_str).unwrap();

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

assert!(
!preprocessors
Expand Down
26 changes: 26 additions & 0 deletions tests/testsuite/book_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,26 @@ impl BookTest {
self
}

/// Removes a file or directory relative to the test root.
pub fn rm_r(&mut self, path: impl AsRef<Path>) -> &mut Self {
let path = self.dir.join(path.as_ref());
let meta = match path.symlink_metadata() {
Ok(meta) => meta,
Err(e) => panic!("failed to remove {path:?}, could not read: {e:?}"),
};
// There is a race condition between fetching the metadata and
// actually performing the removal, but we don't care all that much
// for our tests.
if meta.is_dir() {
if let Err(e) = std::fs::remove_dir_all(&path) {
panic!("failed to remove {path:?}: {e:?}");
}
} else if let Err(e) = std::fs::remove_file(&path) {
panic!("failed to remove {path:?}: {e:?}")
}
self
}

/// Builds a Rust program with the given src.
///
/// The given path should be the path where to output the executable in
Expand Down Expand Up @@ -319,6 +339,12 @@ impl BookCommand {
self
}

/// Sets the directory used for running the command.
pub fn current_dir<S: AsRef<std::path::Path>>(&mut self, path: S) -> &mut Self {
self.dir = self.dir.join(path.as_ref());
self
}

/// Use this to debug a command.
///
/// Pass the value that you would normally pass to `RUST_LOG`, and this
Expand Down
Loading
Loading