Factor out handle_render_command_error

This moves `handle_render_command_error` out to the crate root so that
it can later be shared with `CmdPreprocessor`.
This commit is contained in:
Eric Huss 2025-08-16 12:48:53 -07:00
parent 5d44ef91dc
commit 235c1f87f0
3 changed files with 44 additions and 41 deletions

View file

@ -6,7 +6,6 @@ use anyhow::{Context, Result, bail};
use log::{error, info, trace, warn};
use mdbook_renderer::{RenderContext, Renderer};
use std::fs;
use std::io::{self, ErrorKind};
use std::process::Stdio;
pub use self::markdown_renderer::MarkdownRenderer;
@ -49,41 +48,6 @@ impl CmdRenderer {
}
}
impl CmdRenderer {
fn handle_render_command_error(&self, ctx: &RenderContext, error: io::Error) -> Result<()> {
if let ErrorKind::NotFound = error.kind() {
// Look for "output.{self.name}.optional".
// If it exists and is true, treat this as a warning.
// Otherwise, fail the build.
let optional_key = format!("output.{}.optional", self.name);
let is_optional = match ctx.config.get(&optional_key) {
Ok(Some(value)) => value,
Err(e) => bail!("expected bool for `{optional_key}`: {e}"),
Ok(None) => false,
};
if is_optional {
warn!(
"The command `{}` for backend `{}` was not found, \
but was marked as optional.",
self.cmd, self.name
);
return Ok(());
} else {
error!(
"The command `{0}` wasn't found, is the \"{1}\" backend installed? \
If you want to ignore this error when the \"{1}\" backend is not installed, \
set `optional = true` in the `[output.{1}]` section of the book.toml configuration file.",
self.cmd, self.name
);
}
}
Err(error).with_context(|| "Unable to start the backend")?
}
}
impl Renderer for CmdRenderer {
fn name(&self) -> &str {
&self.name
@ -92,6 +56,13 @@ impl Renderer for CmdRenderer {
fn render(&self, ctx: &RenderContext) -> Result<()> {
info!("Invoking the \"{}\" renderer", self.name);
let optional_key = format!("output.{}.optional", self.name);
let optional = match ctx.config.get(&optional_key) {
Ok(Some(value)) => value,
Err(e) => bail!("expected bool for `{optional_key}`: {e}"),
Ok(None) => false,
};
let _ = fs::create_dir_all(&ctx.destination);
let mut cmd = crate::compose_command(&self.cmd, &ctx.root)?;
@ -103,7 +74,11 @@ impl Renderer for CmdRenderer {
.spawn()
{
Ok(c) => c,
Err(e) => return self.handle_render_command_error(ctx, e),
Err(e) => {
return crate::handle_command_error(
e, optional, "output", "backend", &self.name, &self.cmd,
);
}
};
let mut stdin = child.stdin.take().expect("Child has stdin");

View file

@ -64,7 +64,8 @@ pub mod init;
mod load;
mod mdbook;
use anyhow::{Result, bail};
use anyhow::{Context, Result, bail};
use log::{error, warn};
pub use mdbook::MDBook;
pub use mdbook_core::{book, config, errors};
use shlex::Shlex;
@ -95,3 +96,30 @@ fn compose_command(cmd: &str, root: &Path) -> Result<Command> {
Ok(cmd)
}
/// Handles a failure for a preprocessor or renderer.
fn handle_command_error(
error: std::io::Error,
optional: bool,
key: &str,
what: &str,
name: &str,
cmd: &str,
) -> Result<()> {
if let std::io::ErrorKind::NotFound = error.kind() {
if optional {
warn!(
"The command `{cmd}` for {what} `{name}` was not found, \
but is marked as optional.",
);
return Ok(());
} else {
error!(
"The command `{cmd}` wasn't found, is the `{name}` {what} installed? \
If you want to ignore this error when the `{name}` {what} is not installed, \
set `optional = true` in the `[{key}.{name}]` section of the book.toml configuration file.",
);
}
}
Err(error).with_context(|| format!("Unable to run the {what} `{name}`"))?
}

View file

@ -85,9 +85,9 @@ fn missing_renderer() {
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Book building has started
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Running the missing backend
[TIMESTAMP] [INFO] (mdbook_driver::builtin_renderers): Invoking the "missing" renderer
[TIMESTAMP] [ERROR] (mdbook_driver::builtin_renderers): The command `trduyvbhijnorgevfuhn` wasn't found, is the "missing" backend installed? If you want to ignore this error when the "missing" backend is not installed, set `optional = true` in the `[output.missing]` section of the book.toml configuration file.
[TIMESTAMP] [ERROR] (mdbook_driver): The command `trduyvbhijnorgevfuhn` wasn't found, is the `missing` backend installed? If you want to ignore this error when the `missing` backend is not installed, set `optional = true` in the `[output.missing]` section of the book.toml configuration file.
[TIMESTAMP] [ERROR] (mdbook_core::utils): Error: Rendering failed
[TIMESTAMP] [ERROR] (mdbook_core::utils): [TAB]Caused By: Unable to start the backend
[TIMESTAMP] [ERROR] (mdbook_core::utils): [TAB]Caused By: Unable to run the backend `missing`
[TIMESTAMP] [ERROR] (mdbook_core::utils): [TAB]Caused By: [NOT_FOUND]
"#]]);
@ -102,7 +102,7 @@ fn missing_optional_not_fatal() {
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Book building has started
[TIMESTAMP] [INFO] (mdbook_driver::mdbook): Running the missing backend
[TIMESTAMP] [INFO] (mdbook_driver::builtin_renderers): Invoking the "missing" renderer
[TIMESTAMP] [WARN] (mdbook_driver::builtin_renderers): The command `trduyvbhijnorgevfuhn` for backend `missing` was not found, but was marked as optional.
[TIMESTAMP] [WARN] (mdbook_driver): The command `trduyvbhijnorgevfuhn` for backend `missing` was not found, but is marked as optional.
"#]]);
});