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