mdbook_driver/builtin_renderers/
mod.rs1use anyhow::{Context, Result, bail};
6use mdbook_core::utils::fs;
7use mdbook_renderer::{RenderContext, Renderer};
8use std::process::Stdio;
9use tracing::{error, info, trace, warn};
10
11pub use self::markdown_renderer::MarkdownRenderer;
12
13mod markdown_renderer;
14
15#[derive(Debug, Clone, PartialEq)]
20pub struct CmdRenderer {
21 name: String,
22 cmd: String,
23}
24
25impl CmdRenderer {
26 pub fn new(name: String, cmd: String) -> CmdRenderer {
28 CmdRenderer { name, cmd }
29 }
30}
31
32impl Renderer for CmdRenderer {
33 fn name(&self) -> &str {
34 &self.name
35 }
36
37 fn render(&self, ctx: &RenderContext) -> Result<()> {
38 info!("Invoking the \"{}\" renderer", self.name);
39
40 let optional_key = format!("output.{}.optional", self.name);
41 let optional = match ctx.config.get(&optional_key) {
42 Ok(Some(value)) => value,
43 Err(e) => bail!("expected bool for `{optional_key}`: {e}"),
44 Ok(None) => false,
45 };
46
47 let _ = fs::create_dir_all(&ctx.destination);
48
49 let mut cmd = crate::compose_command(&self.cmd, &ctx.root)?;
50 let mut child = match cmd
51 .stdin(Stdio::piped())
52 .stdout(Stdio::inherit())
53 .stderr(Stdio::inherit())
54 .current_dir(&ctx.destination)
55 .spawn()
56 {
57 Ok(c) => c,
58 Err(e) => {
59 return crate::handle_command_error(
60 e, optional, "output", "backend", &self.name, &self.cmd,
61 );
62 }
63 };
64
65 let mut stdin = child.stdin.take().expect("Child has stdin");
66 if let Err(e) = serde_json::to_writer(&mut stdin, &ctx) {
67 warn!("Error writing the RenderContext to the backend, {}", e);
70 }
71
72 drop(stdin);
74
75 let status = child
76 .wait()
77 .with_context(|| "Error waiting for the backend to complete")?;
78
79 trace!("{} exited with output: {:?}", self.cmd, status);
80
81 if !status.success() {
82 error!("Renderer exited with non-zero return code.");
83 bail!("The \"{}\" renderer failed", self.name);
84 } else {
85 Ok(())
86 }
87 }
88}