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 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 fn badge(args: BadgeArgs) -> Result<()> {
let rt = tokio::runtime::Runtime::new().context("Failed to create tokio runtime")?;
rt.block_on(badge_async(args))
}
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, packages_with_dirs) = tokio::task::spawn_blocking({
let packages = metadata.packages.clone();
let current = current_dir.clone();
move || {
let canonical_current_dir = current.canonicalize().ok();
let packages_with_dirs: Vec<_> = packages
.iter()
.filter_map(|pkg| {
pkg.manifest_path
.as_std_path()
.parent()
.and_then(|p| p.canonicalize().ok())
.map(|p| (pkg.clone(), p))
})
.collect();
(canonical_current_dir, packages_with_dirs)
}
})
.await
.context("Failed to spawn blocking task")?;
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, packages_with_manifests) = tokio::task::spawn_blocking({
let packages = metadata.packages.clone();
let current = current_manifest.clone();
move || {
let canonical_current_manifest = current.canonicalize().ok();
let packages_with_manifests: Vec<_> = packages
.iter()
.filter_map(|pkg| {
pkg.manifest_path
.as_std_path()
.canonicalize()
.ok()
.map(|p| (pkg.clone(), p))
})
.collect();
(canonical_current_manifest, packages_with_manifests)
}
})
.await
.context("Failed to spawn blocking task")?;
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."
)
}