use std::path::{Path, PathBuf};
use cargo_metadata::{MetadataCommand, TargetKind};
use crate::config::Config;
use crate::error::Result;
use crate::rustdoc::{self, Target};
#[derive(Debug, Clone)]
pub struct IndexedCrate {
pub name: String,
pub version: String,
pub root_module_doc: Option<String>,
pub crate_data: rustdoc_types::Crate,
}
#[derive(Debug, Clone)]
pub struct IndexedWorkspace {
pub root: PathBuf,
pub crates: Vec<IndexedCrate>,
}
impl IndexedWorkspace {
pub fn build(workspace_root: &Path, config: &Config) -> Result<Self> {
let metadata = MetadataCommand::new()
.manifest_path(workspace_root.join("Cargo.toml"))
.exec()?;
let root: PathBuf = metadata.workspace_root.as_std_path().to_path_buf();
let mut crates = Vec::new();
for package in metadata.workspace_packages() {
let package_name = package.name.to_string();
if config
.exclude
.iter()
.any(|excluded| excluded == &package_name)
{
continue;
}
let Some(target) = select_target(&package.targets) else {
continue;
};
let manifest_path = package.manifest_path.as_std_path();
let crate_data =
rustdoc::build_and_parse(&target, &package_name, manifest_path, &root)?;
let root_module_doc = crate_data
.index
.get(&crate_data.root)
.and_then(|item| item.docs.clone());
crates.push(IndexedCrate {
name: package_name,
version: package.version.to_string(),
root_module_doc,
crate_data,
});
}
Ok(IndexedWorkspace { root, crates })
}
}
fn select_target(targets: &[cargo_metadata::Target]) -> Option<Target<'_>> {
if targets.iter().any(|t| {
t.kind
.iter()
.any(|k| matches!(k, TargetKind::Lib | TargetKind::RLib))
}) {
return Some(Target::Lib);
}
targets.iter().find_map(|t| {
if t.kind.iter().any(|k| matches!(k, TargetKind::Bin)) {
Some(Target::Bin(t.name.as_str()))
} else {
None
}
})
}