mod adrs;
mod all;
mod common;
mod coverage;
mod crates_io;
mod docs_rs;
mod framework;
mod license;
mod number_of_tests;
mod platform;
mod runtime;
mod rust_edition;
use std::io::Write;
pub use all::badge_all;
use anyhow::{
Context,
Result,
};
use async_fs_io::canonicalize;
use clap::{
Parser,
Subcommand,
};
#[derive(Parser, Debug)]
pub struct BadgeArgs {
#[arg(long)]
pub no_network: bool,
#[command(subcommand)]
pub subcommand: BadgeSubcommand,
}
#[derive(Subcommand, Debug)]
pub enum BadgeSubcommand {
All,
Rustdocs,
Cratesio,
License,
#[command(name = "rust-edition")]
RustEdition,
Runtime,
Framework,
Platform,
ADRs,
Coverage,
#[command(name = "number-of-tests")]
NumberOfTests,
}
pub async fn badge(args: BadgeArgs) -> Result<()> {
badge_async(args).await
}
async fn badge_async(args: BadgeArgs) -> Result<()> {
let mut logger = cargo_plugin_utils::logger::Logger::new();
logger.status("Checking", "package metadata");
let package = find_package().await?;
let mut buffer = Vec::new();
drop(logger);
match args.subcommand {
BadgeSubcommand::All => {
docs_rs::badge_rustdocs(&mut buffer, &package, args.no_network).await?;
crates_io::badge_cratesio(&mut buffer, &package, args.no_network).await?;
license::badge_license(&mut buffer, &package).await?;
rust_edition::badge_rust_edition(&mut buffer, &package).await?;
runtime::badge_runtime(&mut buffer, &package).await?;
framework::badge_framework(&mut buffer, &package).await?;
platform::badge_platform(&mut buffer, &package).await?;
adrs::badge_adrs(&mut buffer, &package).await?;
coverage::badge_coverage(&mut buffer, &package).await?;
number_of_tests::badge_number_of_tests(&mut buffer, &package).await?;
Ok(())
}
BadgeSubcommand::Rustdocs => {
docs_rs::badge_rustdocs(&mut buffer, &package, args.no_network).await
}
BadgeSubcommand::Cratesio => {
crates_io::badge_cratesio(&mut buffer, &package, args.no_network).await
}
BadgeSubcommand::License => license::badge_license(&mut buffer, &package).await,
BadgeSubcommand::RustEdition => {
rust_edition::badge_rust_edition(&mut buffer, &package).await
}
BadgeSubcommand::Runtime => runtime::badge_runtime(&mut buffer, &package).await,
BadgeSubcommand::Framework => framework::badge_framework(&mut buffer, &package).await,
BadgeSubcommand::Platform => platform::badge_platform(&mut buffer, &package).await,
BadgeSubcommand::ADRs => adrs::badge_adrs(&mut buffer, &package).await,
BadgeSubcommand::Coverage => coverage::badge_coverage(&mut buffer, &package).await,
BadgeSubcommand::NumberOfTests => {
number_of_tests::badge_number_of_tests(&mut buffer, &package).await
}
}?;
std::io::stdout().write_all(&buffer)?;
Ok(())
}
pub async fn find_package() -> Result<cargo_metadata::Package> {
use cargo_metadata::MetadataCommand;
let metadata = tokio::task::spawn_blocking(|| MetadataCommand::new().exec())
.await
.context("Failed to spawn blocking task")?
.context("Failed to get cargo metadata")?;
let current_dir = std::env::current_dir().context("Failed to get current directory")?;
let canonical_current_dir = canonicalize(¤t_dir).await.ok();
let mut packages_with_dirs = Vec::new();
for package in &metadata.packages {
let Some(package_dir) = package.manifest_path.as_std_path().parent() else {
continue;
};
if let Ok(canonical_package_dir) = canonicalize(package_dir).await {
packages_with_dirs.push((package.clone(), canonical_package_dir));
}
}
if let Some(ref canonical_current) = canonical_current_dir
&& let Some((pkg, _)) = packages_with_dirs
.iter()
.find(|(_, pkg_dir)| pkg_dir == canonical_current)
{
return Ok(pkg.clone());
}
let current_manifest = current_dir.join("Cargo.toml");
let canonical_current_manifest = canonicalize(¤t_manifest).await.ok();
let mut packages_with_manifests = Vec::new();
for package in &metadata.packages {
if let Ok(canonical_manifest) = canonicalize(package.manifest_path.as_std_path()).await {
packages_with_manifests.push((package.clone(), canonical_manifest));
}
}
if let Some(ref canonical) = canonical_current_manifest
&& let Some((pkg, _)) = packages_with_manifests
.iter()
.find(|(_, pkg_path)| pkg_path == canonical)
{
return Ok(pkg.clone());
}
if let Some(root_package) = metadata.root_package() {
return Ok(root_package.clone());
}
if metadata.workspace_default_members.is_available()
&& !metadata.workspace_default_members.is_empty()
&& let Some(first_default_id) = metadata.workspace_default_members.first()
&& let Some(default_package) = metadata
.packages
.iter()
.find(|pkg| &pkg.id == first_default_id)
{
return Ok(default_package.clone());
}
anyhow::bail!(
"No package found in current directory. Run this command from a package directory, \
or use --manifest-path to specify a package."
)
}