Skip to main content

pubhubs/cli/
doc.rs

1use std::ffi::OsString;
2
3use anyhow::{Result, ensure};
4
5#[derive(clap::Args, Debug)]
6pub struct DocArgs {
7    /// Arguments forwarded to `cargo doc` (e.g. `--open`, `--document-private-items`, `--no-deps`).
8    #[arg(trailing_var_arg = true, allow_hyphen_values = true)]
9    cargo_args: Vec<OsString>,
10}
11
12impl DocArgs {
13    pub fn run(self, _spec: &mut clap::Command) -> Result<()> {
14        let manifest = env!("CARGO_MANIFEST_DIR");
15        let katex_hdr = format!("{manifest}/docs/assets/rustdoc-include-katex-header.html");
16
17        let mut cmd = std::process::Command::new(env!("CARGO"));
18        cmd.current_dir(manifest)
19            .arg("doc")
20            .args(&self.cargo_args)
21            .env("RUSTDOCFLAGS", format!("--html-in-header {katex_hdr}"));
22
23        // We might be spawned by `cargo run`, which sets some CARGO_... environment variables that
24        // would, by default, be inherited by the `cargo doc` we spawn. They're absent for a plain
25        // top-level `cargo`, so leaking them in makes any build script that tracks one recompile on
26        // every run — e.g. ring tracks CARGO_MANIFEST_DIR, see
27        // https://github.com/rust-lang/cargo/issues/16134. Cargo overwrites them unconditionally, so
28        // dropping them can never clobber a user-set value. We strip only these package-identity
29        // vars, not config-input ones the user may have set on purpose.
30        for (key, _) in std::env::vars_os() {
31            if key
32                .to_str()
33                .is_some_and(|k| k.starts_with("CARGO_PKG_") || k.starts_with("CARGO_MANIFEST_"))
34            {
35                cmd.env_remove(&key);
36            }
37        }
38
39        let status = cmd.status()?;
40        ensure!(status.success(), "cargo doc exited with {status}");
41        Ok(())
42    }
43}