pharos_runtime_docs/
rustdoc.rs1use std::ffi::OsString;
4use std::path::{Path, PathBuf};
5
6pub fn cargo_arguments(manifest: &Path, target_dir: &Path) -> Vec<OsString> {
8 [
9 OsString::from("doc"),
10 OsString::from("--manifest-path"),
11 manifest.as_os_str().to_owned(),
12 OsString::from("--workspace"),
13 OsString::from("--no-default-features"),
14 OsString::from("--features"),
15 OsString::from("pharos_native/full-release,pharos_runtime_ops_cli/full-release"),
16 OsString::from("--locked"),
17 OsString::from("--offline"),
18 OsString::from("--no-deps"),
19 OsString::from("--document-private-items"),
20 OsString::from("--lib"),
21 OsString::from("--bins"),
22 OsString::from("--target-dir"),
23 target_dir.as_os_str().to_owned(),
24 ]
25 .into()
26}
27
28pub fn docs_cargo_executable_candidates() -> Vec<std::ffi::OsString> {
30 let mut candidates = Vec::new();
31 for var_name in ["CARGO", "CARGO_BIN"] {
32 if let Some(cargo) = std::env::var_os(var_name).filter(|value| !value.is_empty()) {
33 docs_push_cargo_candidate(&mut candidates, PathBuf::from(cargo));
34 }
35 }
36 candidates.push(std::ffi::OsString::from("cargo"));
37 if let Some(home) = std::env::var_os("HOME").filter(|value| !value.is_empty()) {
38 docs_push_cargo_candidate(
39 &mut candidates,
40 PathBuf::from(home).join(".cargo/bin/cargo"),
41 );
42 }
43 for cargo_path in [
44 "/opt/local/bin/cargo",
45 "/usr/local/bin/cargo",
46 "/opt/homebrew/bin/cargo",
47 ] {
48 docs_push_cargo_candidate(&mut candidates, PathBuf::from(cargo_path));
49 }
50 let mut deduped = Vec::new();
51 for candidate in candidates {
52 if !deduped.iter().any(|seen| seen == &candidate) {
53 deduped.push(candidate);
54 }
55 }
56 deduped
57}
58
59fn docs_push_cargo_candidate(candidates: &mut Vec<std::ffi::OsString>, cargo: PathBuf) {
61 if !cargo.is_file() {
62 return;
63 }
64 candidates.push(cargo.clone().into_os_string());
65 if let Ok(canonical) = cargo.canonicalize()
66 && canonical.is_file()
67 && canonical != cargo
68 {
69 candidates.push(canonical.into_os_string());
70 }
71}
72
73fn docs_rustdoc_toolchain_path(cargo: &std::ffi::OsStr) -> std::ffi::OsString {
75 let mut paths = Vec::new();
76 let cargo_path = PathBuf::from(cargo);
77 if let Some(parent) = cargo_path.parent().filter(|path| path.is_dir()) {
78 paths.push(parent.to_path_buf());
79 }
80 for path in ["/opt/local/bin", "/usr/local/bin", "/opt/homebrew/bin"] {
81 let candidate = PathBuf::from(path);
82 if candidate.is_dir() {
83 paths.push(candidate);
84 }
85 }
86 if let Some(existing) = std::env::var_os("PATH") {
87 paths.extend(std::env::split_paths(&existing));
88 }
89 let mut deduped = Vec::new();
90 for path in paths {
91 if !deduped.iter().any(|seen| seen == &path) {
92 deduped.push(path);
93 }
94 }
95 std::env::join_paths(deduped).unwrap_or_else(|_| {
96 std::ffi::OsString::from("/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin")
97 })
98}
99
100pub fn docs_run_cargo_doc(repo_root: &Path) -> Result<std::process::Output, String> {
102 let manifest_path = repo_root.join("Cargo.toml");
103 let target_dir = repo_root.join("target/rustdoc");
104 let mut launch_errors = Vec::new();
105 for cargo in docs_cargo_executable_candidates() {
106 let toolchain_path = docs_rustdoc_toolchain_path(&cargo);
107 match std::process::Command::new(&cargo)
108 .args(cargo_arguments(&manifest_path, &target_dir))
109 .env("PATH", toolchain_path)
110 .current_dir(repo_root)
111 .output()
112 {
113 Ok(output) => return Ok(output),
114 Err(error) => {
115 launch_errors.push(format!("{}: {error}", cargo.to_string_lossy()));
116 }
117 }
118 }
119 Err(launch_errors.join("; "))
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125
126 #[test]
128 fn documentation_uses_explicit_runtime_features() {
129 let args = cargo_arguments(Path::new("Cargo.toml"), Path::new("target/rustdoc"));
130 assert!(!args.contains(&OsString::from("--all-features")));
131 assert!(args.contains(&OsString::from("--locked")));
132 assert!(args.contains(&OsString::from("--offline")));
133 assert!(args.contains(&OsString::from("--document-private-items")));
134 assert!(args.contains(&OsString::from("--bins")));
135 assert!(args.contains(&OsString::from(
136 "pharos_native/full-release,pharos_runtime_ops_cli/full-release"
137 )));
138 }
139}