use super::BuildError;
use super::BuildOptions;
use std::{
ffi::OsStr,
path::{Path, PathBuf},
process::Command,
};
const OVERRIDDEN_TOOLCHAIN: Option<&str> = option_env!("RUSTDOC_JSON_OVERRIDDEN_TOOLCHAIN_HACK");
pub(crate) fn run_cargo_rustdoc(options: BuildOptions) -> Result<PathBuf, BuildError> {
let status = cargo_rustdoc_command(
options.toolchain.as_deref(),
&options.manifest_path,
options.quiet,
)
.status()?;
if status.success() {
rustdoc_json_path_for_manifest_path(options.manifest_path)
} else {
let manifest = cargo_toml::Manifest::from_path(&options.manifest_path)?;
if manifest.workspace.is_some() {
Err(BuildError::VirtualManifest(options.manifest_path))
} else {
Err(BuildError::General(String::from("See above")))
}
}
}
fn cargo_rustdoc_command(
requested_toolchain: Option<&OsStr>,
manifest_path: impl AsRef<Path>,
quiet: bool,
) -> Command {
let mut command = Command::new("cargo");
command.env_remove("RUSTDOC");
command.env_remove("RUSTC");
let overridden_toolchain = OVERRIDDEN_TOOLCHAIN.map(OsStr::new);
if let Some(toolchain) = overridden_toolchain.or(requested_toolchain) {
command.arg(toolchain);
}
command.arg("rustdoc");
command.arg("--lib");
if quiet {
command.arg("--quiet");
}
command.arg("--manifest-path");
command.arg(manifest_path.as_ref());
command.arg("--");
command.args(["-Z", "unstable-options"]);
command.args(["--output-format", "json"]);
command.args(["--cap-lints", "warn"]);
command
}
fn rustdoc_json_path_for_manifest_path(
manifest_path: impl AsRef<Path>,
) -> Result<PathBuf, BuildError> {
let target_dir = target_directory(&manifest_path)?;
let lib_name = package_name(&manifest_path)?;
let mut rustdoc_json_path = target_dir;
rustdoc_json_path.push("doc");
rustdoc_json_path.push(lib_name.replace('-', "_"));
rustdoc_json_path.set_extension("json");
Ok(rustdoc_json_path)
}
fn target_directory(manifest_path: impl AsRef<Path>) -> Result<PathBuf, BuildError> {
let mut metadata_cmd = cargo_metadata::MetadataCommand::new();
metadata_cmd.manifest_path(manifest_path.as_ref());
let metadata = metadata_cmd.exec()?;
Ok(metadata.target_directory.as_std_path().to_owned())
}
fn package_name(manifest_path: impl AsRef<Path>) -> Result<String, BuildError> {
let manifest = cargo_toml::Manifest::from_path(&manifest_path)?;
Ok(manifest
.package
.ok_or_else(|| BuildError::VirtualManifest(manifest_path.as_ref().to_owned()))?
.name)
}
impl Default for BuildOptions {
fn default() -> Self {
Self {
toolchain: None,
manifest_path: PathBuf::from("Cargo.toml"),
quiet: false,
}
}
}
impl BuildOptions {
#[must_use]
pub fn toolchain(mut self, toolchain: impl AsRef<OsStr>) -> Self {
self.toolchain = Some(toolchain.as_ref().to_owned());
self
}
#[must_use]
pub fn manifest_path(mut self, manifest_path: impl AsRef<Path>) -> Self {
self.manifest_path = manifest_path.as_ref().to_owned();
self
}
#[must_use]
pub fn quiet(mut self, quiet: bool) -> Self {
self.quiet = quiet;
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ensure_toolchain_not_overridden() {
assert!(OVERRIDDEN_TOOLCHAIN.is_none());
}
}