deps-manager/src/main.rs

119 lines
3.4 KiB
Rust

// SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
//
// SPDX-License-Identifier: AGPL-3.0-or-later
use std::{fs, path::PathBuf};
use clap::*;
use log::*;
use semver::Version;
use semver::VersionReq;
#[derive(Parser, Debug)]
#[clap(author, version, about, long_about = None)]
struct CLI {
path: PathBuf,
}
fn init_logger() {
// if std::env::var("RUST_LOG").is_err() {
// std::env::set_var("RUST_LOG", "info");
// }
pretty_env_logger::init();
}
fn skip_entry(name: &str, v: &toml::Value) -> bool {
if v.is_table() {
if v.get("git").is_some() {
info!("[{name}] Skipping, Found Git src: {:?}", v);
return true;
} else if v.get("path").is_some() {
info!("[{name}] Skipping, Found local dep: {:?}", v);
return true;
}
}
false
}
async fn find_latest_version(c: &reqwest::Client, name: &str) -> Version {
let url = if name.len() == 1 {
format!("https://index.crates.io/1/{}", name)
} else if name.len() == 2 {
format!("https://index.crates.io/2/{}", name)
} else if name.len() == 3 {
format!("https://index.crates.io/3/{}/{}", &name[0..1], name)
} else {
format!(
"https://index.crates.io/{}/{}/{}",
&name[0..2].to_lowercase(),
&name[2..4].to_lowercase(),
name
)
};
let resp = c.get(&url).send().await.unwrap();
let text = resp.text().await.unwrap();
let mut latest = Version::parse("0.0.0").unwrap();
for line in text.lines() {
if line.trim().is_empty() {
continue;
}
let this: serde_json::Value =
serde_json::from_str(&line).unwrap_or_else(|_| panic!("{}", line));
let vers = this.get("vers").unwrap();
let vers = Version::parse(vers.as_str().unwrap()).unwrap();
if vers > latest {
latest = vers;
}
}
latest
}
#[tokio::main]
async fn main() {
init_logger();
let opts = CLI::parse();
let contents = fs::read_to_string(opts.path).unwrap();
let manifest: toml::Value = toml::from_str(&contents).unwrap();
let c =
reqwest::Client::builder();
let c = c.build().unwrap();
let mut updates = Vec::default();
if let Some(toml::Value::Table(deps)) = manifest.get("dependencies") {
for d in deps.iter() {
let name = d.0.as_str();
if skip_entry(name, d.1) {
continue;
}
let using = if d.1.is_str() {
d.1.as_str().unwrap()
} else {
d.1.get("version")
.unwrap_or_else(|| panic!("version not found: {:?}", d))
.as_str()
.unwrap()
};
let using = VersionReq::parse(using).unwrap();
debug!("name: {name} version: {using}");
let latest = find_latest_version(&c, name).await;
info!("{name} using: {using} latest {latest}");
// since remove version can only increase, failing to match can be assumed to be the
// same as "new version available"
if !using.matches(&latest) {
updates.push((name, (using, latest)));
}
}
}
for (name, (using, latest)) in updates.iter() {
println!("[{name}] {using} < {latest}")
}
}