flowey_lib_common/
run_cargo_doc.rs1use crate::_util::cargo_output;
10use flowey::node::prelude::*;
11use flowey::shell::FloweyCmd;
12use std::collections::BTreeMap;
13#[derive(Serialize, Deserialize)]
14pub struct CargoDocCommands {
15 cmds: Vec<Vec<String>>,
16 cargo_work_dir: PathBuf,
17 no_incremental: bool,
18}
19
20impl CargoDocCommands {
21 pub fn run(self, rt: &RustRuntimeServices<'_>) -> anyhow::Result<PathBuf> {
24 self.run_with(rt, |x| x)
25 }
26
27 pub fn run_with(
33 self,
34 rt: &RustRuntimeServices<'_>,
35 f: impl Fn(FloweyCmd<'_>) -> FloweyCmd<'_>,
36 ) -> anyhow::Result<PathBuf> {
37 let Self {
38 cmds,
39 cargo_work_dir,
40 no_incremental,
41 } = self;
42
43 let out_dir = rt.sh.current_dir();
44 rt.sh.change_dir(cargo_work_dir);
45
46 let mut json = String::new();
47 for mut cmd in cmds {
48 let argv0 = cmd.remove(0);
49 let cmd = flowey::shell_cmd!(rt, "{argv0} {cmd...}");
50 let cmd = if no_incremental {
51 cmd.env("CARGO_INCREMENTAL", "0")
52 } else {
53 cmd
54 };
55 let cmd = f(cmd);
56 json.push_str(&cmd.read()?);
57 }
58 let messages: Vec<cargo_output::Message> = serde_json::Deserializer::from_str(&json)
59 .into_iter()
60 .collect::<Result<_, _>>()?;
61
62 let cargo_out_dir = messages
64 .iter()
65 .find_map(|msg| match msg {
66 cargo_output::Message::CompilerArtifact { filenames, .. } => {
67 filenames.iter().find_map(|filename| {
68 filename
69 .file_name()
70 .is_some_and(|f| f == "index.html")
71 .then(|| filename.parent().unwrap().parent().unwrap())
72 })
73 }
74 _ => None,
75 })
76 .context("could not find cargo doc output directory")?;
77
78 assert_eq!(cargo_out_dir.file_name().unwrap(), "doc");
79
80 let final_dir = out_dir.join("cargo-doc-out");
81 fs_err::rename(cargo_out_dir, &final_dir)?;
82 Ok(final_dir)
83 }
84}
85
86#[derive(Serialize, Deserialize)]
88pub enum DocPackageKind {
89 Workspace { exclude: Vec<String> },
91 Crate(String),
93 NoStdCrate(String),
98}
99
100#[derive(Serialize, Deserialize)]
102pub struct DocPackage {
103 pub kind: DocPackageKind,
105 pub no_deps: bool,
107 pub document_private_items: bool,
109}
110
111flowey_request! {
112 pub struct Request {
113 pub in_folder: ReadVar<PathBuf>,
114 pub packages: Vec<DocPackage>,
116 pub target_triple: target_lexicon::Triple,
118 pub cargo_cmd: WriteVar<CargoDocCommands>,
119 }
120}
121
122#[derive(Default)]
123struct ResolvedDocPackages {
124 workspace: Option<(bool, bool)>,
126 exclude: Vec<String>,
127 crates: BTreeMap<(bool, bool), Vec<String>>,
128 crates_no_std: BTreeMap<(bool, bool), Vec<String>>,
129}
130
131new_flow_node!(struct Node);
132
133impl FlowNode for Node {
134 type Request = Request;
135
136 fn imports(ctx: &mut ImportCtx<'_>) {
137 ctx.import::<crate::cfg_cargo_common_flags::Node>();
138 ctx.import::<crate::install_rust::Node>();
139 }
140
141 fn emit(requests: Vec<Self::Request>, ctx: &mut NodeCtx<'_>) -> anyhow::Result<()> {
142 let rust_toolchain = ctx.reqv(crate::install_rust::Request::GetRustupToolchain);
143 let flags = ctx.reqv(crate::cfg_cargo_common_flags::Request::GetFlags);
144
145 for Request {
146 in_folder,
147 packages,
148 target_triple,
149 cargo_cmd,
150 } in requests
151 {
152 ctx.req(crate::install_rust::Request::InstallTargetTriple(
153 target_triple.clone(),
154 ));
155
156 let mut targets = ResolvedDocPackages::default();
158 for DocPackage {
159 kind,
160 no_deps,
161 document_private_items,
162 } in packages
163 {
164 match kind {
165 DocPackageKind::Workspace { exclude } => {
166 if targets.workspace.is_some() {
167 anyhow::bail!("cannot pass Workspace variant multiple times")
168 }
169 targets.exclude.extend(exclude);
170 targets.workspace = Some((no_deps, document_private_items))
171 }
172 DocPackageKind::Crate(name) => targets
173 .crates
174 .entry((no_deps, document_private_items))
175 .or_default()
176 .push(name),
177 DocPackageKind::NoStdCrate(name) => targets
178 .crates_no_std
179 .entry((no_deps, document_private_items))
180 .or_default()
181 .push(name),
182 }
183 }
184
185 let doc_targets = targets;
186
187 ctx.emit_minor_rust_step("construct cargo doc command", |ctx| {
188 let rust_toolchain = rust_toolchain.clone().claim(ctx);
189 let flags = flags.clone().claim(ctx);
190 let in_folder = in_folder.claim(ctx);
191 let write_doc_cmd = cargo_cmd.claim(ctx);
192
193 move |rt| {
194 let rust_toolchain = rt.read(rust_toolchain);
195 let flags = rt.read(flags);
196 let in_folder = rt.read(in_folder);
197
198 let crate::cfg_cargo_common_flags::Flags {
199 locked,
200 verbose,
201 no_incremental,
202 } = flags;
203
204 let mut cmds = Vec::new();
205 let ResolvedDocPackages {
206 workspace,
207 exclude,
208 mut crates,
209 crates_no_std,
210 } = doc_targets;
211
212 let base_cmd = |no_deps: bool, document_private_items: bool| -> Vec<String> {
213 let mut v = Vec::new();
214 v.push("cargo".into());
215 if let Some(rust_toolchain) = &rust_toolchain {
216 v.push(format!("+{rust_toolchain}"))
217 }
218 v.push("doc".into());
219 v.push("--message-format=json-render-diagnostics".into());
220 v.push("--target".into());
221 v.push(target_triple.to_string());
222 if locked {
223 v.push("--locked".into());
224 }
225 if verbose {
226 v.push("--verbose".into());
227 }
228 if no_deps {
229 v.push("--no-deps".into());
230 }
231 if document_private_items {
232 v.push("--document-private-items".into())
233 }
234 v
235 };
236
237 if let Some((no_deps, document_private_items)) = workspace {
240 crates.remove(&(no_deps, document_private_items));
242
243 let mut v = base_cmd(no_deps, document_private_items);
244
245 v.push("--workspace".into());
246
247 for crates_no_std in crates_no_std.values() {
248 for c in crates_no_std.iter().chain(exclude.iter()) {
249 v.push("--exclude".into());
250 v.push(c.into())
251 }
252 }
253
254 cmds.push(v);
255 }
256
257 for ((no_deps, document_private_items), crates) in crates {
259 let mut v = base_cmd(no_deps, document_private_items);
260
261 for c in crates {
262 v.push("-p".into());
263 v.push(c);
264 }
265
266 cmds.push(v)
267 }
268
269 for ((no_deps, document_private_items), crates) in crates_no_std {
271 let mut v = base_cmd(no_deps, document_private_items);
272
273 for c in crates {
274 v.push("-p".into());
275 v.push(c);
276 }
277
278 cmds.push(v)
279 }
280
281 let cmd = CargoDocCommands {
282 cmds,
283 cargo_work_dir: in_folder.clone(),
284 no_incremental,
285 };
286
287 rt.write(write_doc_cmd, &cmd);
288 }
289 });
290 }
291
292 Ok(())
293 }
294}