use std::collections::HashSet;
use std::path::PathBuf;
use clap::Parser;
use crate::diagnostic::{DiagnosticSink, Severity, render_diagnostics};
use crate::metadata;
use super::input::{PackageSelection, filter_library_targets, resolve_input, select_packages};
#[derive(Debug, Parser)]
pub(super) struct CacheArgs {
#[command(subcommand)]
command: CacheCommand,
}
#[derive(Debug, clap::Subcommand)]
enum CacheCommand {
Warm(WarmArgs),
Clear,
#[command(subcommand)]
Show(ShowCommand),
}
#[derive(Debug, clap::Subcommand)]
enum ShowCommand {
Dir,
}
#[derive(Debug, Parser)]
struct WarmArgs {
input: Option<PathBuf>,
#[command(flatten)]
package_selection: PackageSelection,
#[arg(long)]
metadata: Option<PathBuf>,
#[arg(short, long)]
quiet: bool,
}
pub(super) fn run(args: &CacheArgs) -> anyhow::Result<()> {
match &args.command {
CacheCommand::Warm(args) => warm(args),
CacheCommand::Clear => clear(),
CacheCommand::Show(ShowCommand::Dir) => show_dir(),
}
}
fn warm(args: &WarmArgs) -> anyhow::Result<()> {
let resolved_input = args
.input
.as_ref()
.map(|p| resolve_input(p))
.transpose()?;
let metadata_dir = resolved_input
.as_ref()
.map(|r| r.dir().clone())
.unwrap_or_else(|| PathBuf::from("."));
let package_graph =
metadata::load_package_graph(args.metadata.as_ref(), Some(&metadata_dir))?;
let packages = select_packages(
resolved_input.as_ref(),
&args.package_selection,
&package_graph.workspace(),
)?;
let ws_root: PathBuf = package_graph.workspace().root().to_path_buf().into();
let debug = std::env::var("CHEADERGEN_DEBUG").is_ok_and(|v| v == "true" || v == "1");
let mut diagnostics = DiagnosticSink::new(ws_root, debug);
let explicit_names: HashSet<String> =
args.package_selection.packages.iter().cloned().collect();
let packages =
filter_library_targets(packages, &package_graph, &explicit_names, &mut diagnostics);
let had_errors = render_pending_diagnostics(&mut diagnostics, debug);
if packages.is_empty() {
if had_errors {
anyhow::bail!("aborting due to previous error(s)");
}
if !args.quiet {
eprintln!("No library crates to warm.");
}
return Ok(());
}
if !args.quiet {
eprintln!(
"Warming rustdoc cache for {} workspace member(s)...",
packages.len()
);
}
let collection = metadata::create_collection(package_graph)?;
collection.compute_batch(packages.into_iter().map(|(id, _)| id))?;
if !args.quiet {
eprintln!("Cache warm-up complete.");
}
if had_errors {
anyhow::bail!("aborting due to previous error(s)");
}
Ok(())
}
fn show_dir() -> anyhow::Result<()> {
println!("{}", metadata::cache_dir()?.display());
Ok(())
}
fn clear() -> anyhow::Result<()> {
let cache_dir = metadata::cache_dir()?;
if cache_dir.exists() {
fs_err::remove_dir_all(&cache_dir)?;
eprintln!("Removed cache at {}.", cache_dir.display());
} else {
eprintln!("No cache found at {}.", cache_dir.display());
}
Ok(())
}
fn render_pending_diagnostics(diagnostics: &mut DiagnosticSink, debug: bool) -> bool {
if diagnostics.is_empty() {
return false;
}
let has_hidden_causes = diagnostics.has_hidden_causes();
let all = diagnostics.drain();
let use_color = std::env::var("NO_COLOR").is_err();
eprint!("{}", render_diagnostics(&all, use_color));
if !debug && has_hidden_causes {
eprintln!("note: rerun with `CHEADERGEN_DEBUG=true` for more details");
} else {
eprintln!();
}
all.iter().any(|d| d.severity == Severity::Error)
}