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