Skip to content

Instantly share code, notes, and snippets.

@sirhcel

sirhcel/main.rs Secret

Created December 10, 2023 20:50
Show Gist options
  • Save sirhcel/ce2ebde35df64bd8159f7a19275c9a39 to your computer and use it in GitHub Desktop.
Save sirhcel/ce2ebde35df64bd8159f7a19275c9a39 to your computer and use it in GitHub Desktop.
Quick-and-dirty dependents check on crates.io
use clap::Parser;
use crates_io_api::{FullCrate, ReverseDependency, SyncClient};
use std::cmp::Reverse;
use std::time::Duration;
const CRATES_IO_MINIMUM_CRAWLER_INTERVAL: Duration = Duration::from_millis(1000);
#[derive(Clone, Debug, Eq, Parser, PartialEq)]
struct Config {
/// The user agent to use for querying crates.io. For example "my_bot (my_bot.com/info)" or
/// "my_bot (help@my_bot.com)". Please respect crate.io's crawler
/// (https://crates.io/policies#crawlers).
#[arg(short, long, env = "DEPENDENTS_CRATES_IO_USER_AGENT")]
user_agent: String,
/// Version requirement like "^1.2.3".
#[arg(short, long)]
version_requirement: String,
/// The crate to investigate the MSRV of its depedents for.
#[arg(name = "CRATE")]
krate: String,
}
#[derive(Clone, Debug, Eq, PartialEq)]
struct Info {
name: String,
num: String,
downloads: u64,
msrv: Option<String>,
dependency_req: Option<String>,
}
fn collect_from_versions(full: &FullCrate, dependency: &str) -> anyhow::Result<Vec<Info>> {
let infos = full
.versions
.iter()
.map(|v| {
let req: Vec<String> = v
.dependencies
.iter()
.filter(|d| d.crate_id == dependency)
.map(|d| d.req.clone())
.collect();
assert!(
req.len() <= 1,
"crate {} has dependency {} given more than one time ({})",
full.name,
dependency,
req.len()
);
Info {
name: full.name.clone(),
num: v.num.clone(),
downloads: v.downloads,
msrv: v.rust_version.clone(),
// TODO: Isn't there a nicer type?
dependency_req: req.first().map(|r| r.to_string()),
}
})
.collect();
Ok(infos)
}
fn main() -> anyhow::Result<()> {
env_logger::init();
let config = Config::parse();
let client = SyncClient::new(&config.user_agent, CRATES_IO_MINIMUM_CRAWLER_INTERVAL)?;
let reverse_dependencies = client.crate_reverse_dependencies(&config.krate)?;
println!(
"total dependencies: {}",
reverse_dependencies.dependencies.len()
);
let mut dependencies: Vec<ReverseDependency> = reverse_dependencies
.dependencies
.into_iter()
.filter(|e| e.dependency.req.starts_with(&config.version_requirement))
.collect();
println!(
"dependencies on {}: {}",
&config.version_requirement,
dependencies.len()
);
dependencies.sort_by_key(|e| Reverse(e.crate_version.downloads));
println!();
for d in dependencies.iter() {
let version = &d.crate_version;
if version.rust_version.is_some() {
let full = client.full_crate(&version.crate_name, true)?;
let infos = collect_from_versions(&full, &config.krate)?;
println!("infos for {}", version.crate_name);
for info in infos {
println!(" {:?}", info);
}
}
}
Ok(())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment