lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README | LICENSE

commit 82dd1ad28ad5107fff1b00275a202b5d9135e5ee
parent 55b2f1eb69df0b2578f2e7c8426a8b0547c2d3c1
Author: triesap <tyson@radroots.org>
Date:   Sun, 22 Feb 2026 03:15:51 +0000

coverage: add workspace crate discovery command


- parse workspace members from the root cargo.toml contract
- resolve package names from each crate manifest and validate uniqueness
- expose sdk coverage workspace-crates for ci and local tooling
- add xtask tests that assert workspace crate enumeration works

Diffstat:
Mcrates/xtask/src/coverage.rs | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 71 insertions(+), 0 deletions(-)

diff --git a/crates/xtask/src/coverage.rs b/crates/xtask/src/coverage.rs @@ -126,6 +126,26 @@ struct CoverageRequiredList { crates: Vec<String>, } +#[derive(Debug, Deserialize)] +struct WorkspaceManifest { + workspace: WorkspaceMembers, +} + +#[derive(Debug, Deserialize)] +struct WorkspaceMembers { + members: Vec<String>, +} + +#[derive(Debug, Deserialize)] +struct PackageManifest { + package: PackageSection, +} + +#[derive(Debug, Deserialize)] +struct PackageSection { + name: String, +} + pub fn read_summary(path: &Path) -> Result<CoverageSummary, String> { let raw = fs::read_to_string(path) .map_err(|err| format!("failed to read summary {}: {err}", path.display()))?; @@ -166,6 +186,37 @@ fn read_required_crates(path: &Path) -> Result<Vec<String>, String> { Ok(parsed.required.crates) } +fn read_workspace_crates(workspace_root: &Path) -> Result<Vec<String>, String> { + let workspace_manifest = parse_toml::<WorkspaceManifest>(&workspace_root.join("Cargo.toml"))?; + if workspace_manifest.workspace.members.is_empty() { + return Err("workspace members list must not be empty".to_string()); + } + let mut names = Vec::with_capacity(workspace_manifest.workspace.members.len()); + let mut seen = BTreeSet::new(); + for member in workspace_manifest.workspace.members { + let package_manifest = + parse_toml::<PackageManifest>(&workspace_root.join(member).join("Cargo.toml"))?; + let package_name = package_manifest.package.name; + if package_name.trim().is_empty() { + return Err("workspace includes an empty package name".to_string()); + } + if !seen.insert(package_name.clone()) { + return Err(format!( + "workspace includes duplicate package name {}", + package_name + )); + } + names.push(package_name); + } + Ok(names) +} + +fn parse_toml<T: for<'de> Deserialize<'de>>(path: &Path) -> Result<T, String> { + let raw = fs::read_to_string(path) + .map_err(|err| format!("failed to read {}: {err}", path.display()))?; + toml::from_str::<T>(&raw).map_err(|err| format!("failed to parse {}: {err}", path.display())) +} + pub fn read_lcov(path: &Path) -> Result<LcovCoverage, String> { let raw = fs::read_to_string(path) .map_err(|err| format!("failed to read lcov {}: {err}", path.display()))?; @@ -614,12 +665,24 @@ fn list_required_crates() -> Result<(), String> { Ok(()) } +fn list_workspace_crates() -> Result<(), String> { + let root = workspace_root()?; + let crates = read_workspace_crates(&root)?; + let mut stdout = std::io::stdout().lock(); + for crate_name in crates { + writeln!(stdout, "{crate_name}") + .map_err(|err| format!("failed to write workspace crates output: {err}"))?; + } + Ok(()) +} + pub fn run(args: &[String]) -> Result<(), String> { match args.first().map(String::as_str) { Some("help") => Ok(()), Some("run-crate") => run_crate(&args[1..]), Some("report") => report_gate(&args[1..]), Some("required-crates") => list_required_crates(), + Some("workspace-crates") => list_workspace_crates(), Some(_) => Err("unknown sdk coverage subcommand".to_string()), None => Err("missing sdk coverage subcommand".to_string()), } @@ -747,4 +810,12 @@ mod tests { assert!(err.contains("duplicate crate a")); fs::remove_file(dup_path).expect("remove dup required crates"); } + + #[test] + fn reads_workspace_crates_and_contains_xtask() { + let root = workspace_root().expect("workspace root"); + let crates = read_workspace_crates(&root).expect("workspace crates"); + assert!(!crates.is_empty()); + assert!(crates.iter().any(|crate_name| crate_name == "xtask")); + } }