From 0a105365c7a6ebe3da1b32cd9f5741c6f79056d3 Mon Sep 17 00:00:00 2001 From: clearloop <26088946+clearloop@users.noreply.github.com> Date: Sun, 17 Dec 2023 02:26:48 +0800 Subject: [PATCH] refactor(conta): use toml_edit instead of hand written sed (#195) * refactor(conta): edit versions with toml_edit * feat(conta): trim the bump logic with toml_edit * fix(conta): packages matching * ci(typos): update to 1.16.25 --- .cargo/config.toml | 1 + .github/workflows/typos.yml | 2 +- Cargo.lock | 171 +++++++++++++++++++++++------------ Cargo.toml | 20 ++-- RELEASES.md | 10 ++ cli/conta/Cargo.toml | 7 +- cli/conta/src/bin/conta.rs | 5 +- cli/conta/src/cmd/bump.rs | 58 +++++------- cli/conta/src/cmd/mod.rs | 16 +++- cli/conta/src/cmd/publish.rs | 110 +++++++++------------- cli/conta/src/lib.rs | 3 +- cli/conta/src/sed.rs | 117 ------------------------ cli/conta/src/version.rs | 31 +++++++ 13 files changed, 246 insertions(+), 305 deletions(-) delete mode 100644 cli/conta/src/sed.rs create mode 100644 cli/conta/src/version.rs diff --git a/.cargo/config.toml b/.cargo/config.toml index 80a307734..b56562ab9 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,4 +1,5 @@ [alias] +bb = "build --all --all-features --release" be = "build --examples --target wasm32-unknown-unknown --release" tt = "nextest run --release --all --no-fail-fast" te = "nextest run --workspace --no-fail-fast --release --examples" diff --git a/.github/workflows/typos.yml b/.github/workflows/typos.yml index c7760ed5e..c07523a8b 100644 --- a/.github/workflows/typos.yml +++ b/.github/workflows/typos.yml @@ -15,4 +15,4 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: crate-ci/typos@v1.16.23 + - uses: crate-ci/typos@v1.16.25 diff --git a/Cargo.lock b/Cargo.lock index 280009bbb..abdb2aff3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -693,13 +693,12 @@ name = "conta" version = "0.0.0" dependencies = [ "anyhow", - "cargo_metadata", - "clap", - "crates-io", - "curl", + "ccli", + "reqwest", "semver", "serde", "toml", + "toml_edit 0.21.0", ] [[package]] @@ -733,20 +732,6 @@ dependencies = [ "libc", ] -[[package]] -name = "crates-io" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aadfd000bd635ce58527e2dffe008339867991ab12a786b859d9cfe967c0f72" -dependencies = [ - "curl", - "percent-encoding", - "serde", - "serde_json", - "thiserror", - "url", -] - [[package]] name = "crc32fast" version = "1.3.2" @@ -825,36 +810,6 @@ dependencies = [ "cipher", ] -[[package]] -name = "curl" -version = "0.4.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "509bd11746c7ac09ebd19f0b17782eae80aadee26237658a6b4808afb5c11a22" -dependencies = [ - "curl-sys", - "libc", - "openssl-probe", - "openssl-sys", - "schannel", - "socket2 0.4.10", - "winapi", -] - -[[package]] -name = "curl-sys" -version = "0.4.70+curl-8.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c0333d8849afe78a4c8102a429a446bfdd055832af071945520e835ae2d841e" -dependencies = [ - "cc", - "libc", - "libz-sys", - "openssl-sys", - "pkg-config", - "vcpkg", - "windows-sys 0.48.0", -] - [[package]] name = "cxx" version = "1.0.110" @@ -1521,6 +1476,21 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -1873,6 +1843,19 @@ dependencies = [ "tokio-rustls", ] +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + [[package]] name = "idna" version = "0.5.0" @@ -2133,18 +2116,6 @@ dependencies = [ "redox_syscall", ] -[[package]] -name = "libz-sys" -version = "1.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d97137b25e321a73eef1418d1d5d2eda4d77e12813f8e6dead84bc52c5870a7b" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - [[package]] name = "link-cplusplus" version = "1.0.9" @@ -2242,6 +2213,24 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "new_debug_unreachable" version = "1.0.4" @@ -2416,6 +2405,32 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "openssl" +version = "0.10.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b8419dc8cc6d866deb801274bba2e6f8f6108c1bb7fcc10ee5ab864931dbb45" +dependencies = [ + "bitflags 2.4.1", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.41", +] + [[package]] name = "openssl-probe" version = "0.1.5" @@ -2967,10 +2982,12 @@ dependencies = [ "http-body", "hyper", "hyper-rustls", + "hyper-tls", "ipnet", "js-sys", "log", "mime", + "native-tls", "once_cell", "percent-encoding", "pin-project-lite", @@ -2981,6 +2998,7 @@ dependencies = [ "serde_urlencoded", "system-configuration", "tokio", + "tokio-native-tls", "tokio-rustls", "tower-service", "url", @@ -3337,6 +3355,29 @@ dependencies = [ "cc", ] +[[package]] +name = "security-framework" +version = "2.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "semver" version = "1.0.20" @@ -3881,6 +3922,16 @@ dependencies = [ "syn 2.0.41", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "tokio-rustls" version = "0.24.1" diff --git a/Cargo.toml b/Cargo.toml index 8d57290fc..e3ca6bf1d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,10 +24,8 @@ repository = "https://github.com/clearloop/zink.git" anyhow = "1.0.75" cargo_metadata = "0.18.1" clap = "4.4.11" -curl = "0.4.44" color-eyre = "0.6.2" colored = "2.1.0" -crates-io = "0.39.0" etc = "0.1.16" ethers = "2.0.11" hex = "0.4.3" @@ -39,6 +37,7 @@ postcard = { version = "1.0.8", default-features = false } proc-macro2 = "1.0.70" quote = "1.0.33" revm = "3.5.0" +reqwest = { version = "0.11.22", default-features = false } semver = "1.0.20" serde = { version = "1.0.193", default-features = false } serde_json = "1.0.108" @@ -49,6 +48,7 @@ target-lexicon = "0.12.12" thiserror = "1.0.51" tokio = "1.35.0" toml = "0.8.8" +toml_edit = "0.21.0" tracing = "0.1.40" tracing-subscriber = "0.3.18" url = "2.5.0" @@ -62,14 +62,14 @@ sol-abi = { path = "evm/abi", version = "=0.0.1" } opcodes = { package = "evm-opcodes", path = "evm/opcodes", version = "=0.0.3", features = [ "data" ] } ## Zink packages -elko = { path = "cli/elko", version = "=0.1.8" } -zabi = { path = "abi", version = "=0.1.8" } -zingen = { path = "codegen", version = "=0.1.8" } -zinkc = { path = "compiler", version = "=0.1.8" } -filetests = { package = "zinkc-filetests", path = "compiler/filetests", version = "=0.1.8" } -zink = { path = ".", version = "=0.1.8" } -zink-codegen = { path = "zink/codegen", version = "=0.1.8" } -zint = { path = "zint", version = "=0.1.8" } +elko = { path = "cli/elko", version = "0.1.8" } +zabi = { path = "abi", version = "0.1.8" } +zingen = { path = "codegen", version = "0.1.8" } +zinkc = { path = "compiler", version = "0.1.8" } +filetests = { package = "zinkc-filetests", path = "compiler/filetests", version = "0.1.8" } +zink = { path = ".", version = "0.1.8" } +zink-codegen = { path = "zink/codegen", version = "0.1.8" } +zint = { path = "zint", version = "0.1.8" } [profile] dev = { panic = "abort"} diff --git a/RELEASES.md b/RELEASES.md index 34b6170f8..dbb4d5ed2 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,13 @@ +## v0.1.9 + +### Changes + +- Refactor conta with `toml_edit` + +### FIXED + +- Missing logic of adapt package alias in conta + ## v0.1.8 ### Added diff --git a/cli/conta/Cargo.toml b/cli/conta/Cargo.toml index 7889087e0..2779ced2a 100644 --- a/cli/conta/Cargo.toml +++ b/cli/conta/Cargo.toml @@ -11,10 +11,9 @@ repository.workspace = true [dependencies] anyhow.workspace = true -cargo_metadata.workspace = true -clap = { workspace = true, features = [ "derive" ] } -crates-io.workspace = true -curl.workspace = true +ccli.workspace = true +reqwest = { workspace = true, features = [ "blocking", "json", "default-tls" ] } semver.workspace = true serde = { workspace = true, features = [ "derive" ] } toml.workspace = true +toml_edit.workspace = true diff --git a/cli/conta/src/bin/conta.rs b/cli/conta/src/bin/conta.rs index 60d8b4a4a..643df0988 100644 --- a/cli/conta/src/bin/conta.rs +++ b/cli/conta/src/bin/conta.rs @@ -1,7 +1,6 @@ -use anyhow::Result; -use clap::Parser; +use ccli::{App, Result}; use conta::Conta; fn main() -> Result<()> { - Conta::parse().run() + Conta::start() } diff --git a/cli/conta/src/cmd/bump.rs b/cli/conta/src/cmd/bump.rs index 3ed442c35..90cc27c36 100644 --- a/cli/conta/src/cmd/bump.rs +++ b/cli/conta/src/cmd/bump.rs @@ -1,10 +1,10 @@ //! Command bump -use crate::{Config, Sed}; -use anyhow::Result; -use cargo_metadata::MetadataCommand; -use clap::Parser; -use semver::{Version, VersionReq}; -use std::path::PathBuf; +use crate::Config; +use anyhow::{anyhow, Result}; +use ccli::clap::{self, Parser}; +use semver::Version; +use std::{fs, path::PathBuf, str::FromStr}; +use toml_edit::Document; /// Bump versions. #[derive(Debug, Parser, Clone)] @@ -15,9 +15,6 @@ pub struct Bump { /// Dry run the command and print the result. #[clap(short, long, value_name = "dry-run")] dry_run: bool, - // TODO: - // - // support bump major, minor, patch, pre, build. } impl Bump { @@ -26,43 +23,30 @@ impl Bump { /// NOTE: This implementation only works for workspace /// for now. pub fn run(&self, manifest: &PathBuf, config: Config) -> Result<()> { - let mut sed = Sed::new(manifest)?; - - sed.set_workspace_version(&self.version)?; - let version_req = format!("={}", self.version); - sed.set_dep_versions(&VersionReq::parse(&version_req)?, &config.packages)?; + let mut workspace = Document::from_str(&std::fs::read_to_string(manifest)?)?; + let version = self.version.to_string(); + workspace["workspace"]["package"]["version"] = toml_edit::value(version.clone()); if self.dry_run { - println!("{}", String::from_utf8_lossy(&sed.buf)); - } else { - sed.flush()?; - self.verify(manifest, &config.packages)?; + println!("{workspace}"); + return Ok(()); } - Ok(()) - } + let Some(deps) = workspace["workspace"]["dependencies"].as_table_mut() else { + return Err(anyhow!( + "Failed to parse dependencies from workspace {manifest:?}" + )); + }; - /// Get the metadata of the workspace. - pub fn verify(&self, manifest: &PathBuf, packages: &[String]) -> Result<()> { - let metadata = MetadataCommand::new() - .no_deps() - .manifest_path(manifest) - .exec()?; - - for package in metadata.packages.iter() { - if !packages.contains(&package.name) { - continue; + for package in config.packages { + if !deps.contains_key(&package) { + return Err(anyhow!("package {} not found", package)); } - if package.version != self.version { - return Err(anyhow::anyhow!( - "incorrect crate version {} in {}", - self.version, - package.name - )); - } + deps[&package]["version"] = toml_edit::value(version.clone()); } + fs::write(manifest, workspace.to_string())?; Ok(()) } } diff --git a/cli/conta/src/cmd/mod.rs b/cli/conta/src/cmd/mod.rs index 1f4ba2bd2..8499746ab 100644 --- a/cli/conta/src/cmd/mod.rs +++ b/cli/conta/src/cmd/mod.rs @@ -3,7 +3,10 @@ pub use crate::{ Config, }; use anyhow::Result; -use clap::Parser; +use ccli::{ + clap::{self, Parser}, + App, +}; use std::{fs, path::PathBuf}; mod bump; @@ -54,15 +57,20 @@ impl Conta { toml::from_str(&fs::read_to_string(path)?).map_err(Into::into) } +} + +impl App for Conta { + fn verbose(&self) -> u8 { + 0 + } - /// Process commands - pub fn run(&self) -> Result<()> { + fn run(&self) -> Result<()> { let manifest = self.manifest(); let config = self.config()?; match &self.command { Command::Bump(bump) => bump.run(&manifest, config), - Command::Publish(publish) => publish.run(&manifest, &config.packages), + Command::Publish(publish) => publish.run(&manifest, config.packages), } } } diff --git a/cli/conta/src/cmd/publish.rs b/cli/conta/src/cmd/publish.rs index b0fefbc28..0642fc042 100644 --- a/cli/conta/src/cmd/publish.rs +++ b/cli/conta/src/cmd/publish.rs @@ -1,26 +1,19 @@ //! Command publish + +use crate::version; use anyhow::{anyhow, Result}; -use cargo_metadata::MetadataCommand; -use clap::Parser; -use crates_io::Registry; -use curl::easy::Easy; -use std::{collections::BTreeMap, path::PathBuf, process::Command}; +use ccli::clap::{self, Parser}; +use core::str::FromStr; +use std::{path::PathBuf, process::Command}; +use toml_edit::Document; /// Publish crates. #[derive(Debug, Parser, Clone)] -pub struct Publish { - /// If allow dirty publish. - #[clap(short, long, value_name = "dry-run")] - allow_dirty: bool, - - /// If dry run. - #[clap(short, long, value_name = "dry-run")] - dry_run: bool, -} +pub struct Publish; impl Publish { /// Run publish - pub fn run(&self, manifest: &PathBuf, packages: &[String]) -> Result<()> { + pub fn run(&self, manifest: &PathBuf, packages: Vec) -> Result<()> { let pkgs = self.verify(manifest, packages)?; for pkg in pkgs { @@ -33,65 +26,48 @@ impl Publish { } /// Publish cargo package - fn publish(&self, mut package: &str) -> Result { - if package == "zinkc-filetests" { - package = "filetests"; - } - - let mut cargo = Command::new("cargo"); - cargo.arg("publish").arg("-p").arg(package); - - if self.dry_run { - cargo.arg("--dry-run"); - } - - if self.allow_dirty { - cargo.arg("--allow-dirty"); - } - - Ok(cargo.status()?.success()) + fn publish(&self, package: &str) -> Result { + Command::new("cargo") + .arg("publish") + .arg("-p") + .arg(package) + .arg("--allow-dirty") + .status() + .map(|status| status.success()) + .map_err(|err| err.into()) } - fn verify(&self, manifest: &PathBuf, packages: &[String]) -> Result> { - let mut registry = { - let mut handle = Easy::new(); - handle.useragent("zink-lang")?; - Registry::new_handle("https://crates.io".into(), None, handle, false) - }; + fn verify(&self, manifest: &PathBuf, packages: Vec) -> Result> { + let workspace = Document::from_str(&std::fs::read_to_string(manifest)?)?; + let version = workspace["workspace"]["package"]["version"] + .as_str() + .ok_or_else(|| anyhow!("Failed to parse version from workspace {manifest:?}"))?; - let metadata = MetadataCommand::new() - .no_deps() - .manifest_path(manifest) - .exec()?; + let Some(deps) = workspace["workspace"]["dependencies"].as_table() else { + return Err(anyhow!( + "Failed to parse dependencies from workspace {manifest:?}" + )); + }; - let pkgs = metadata - .packages - .iter() - .map(|pkg| (pkg.name.clone(), pkg.version.to_string())) - .collect::>(); + let mut unpublished = vec![]; + for package in packages { + if !deps.contains_key(&package) { + continue; + } - packages - .iter() - .filter_map(|pkg| -> Option> { - let pkg = if pkg.as_str() == "filetests" { - "zinkc-filetests".into() - } else { - pkg.clone() - }; + let name = deps[&package] + .get("package") + .and_then(|p| p.as_str()) + .unwrap_or(&package); - let Some((name, version)) = pkgs.get_key_value(&pkg) else { - return Some(Err(anyhow!("Package {} not found in metadata", pkg))); - }; + if version::verify(name, version)? { + println!("Package {name}@{version} has already been published."); + continue; + } - if let Ok((crates, _total)) = registry.search(&pkg, 1) { - if crates.len() == 1 && crates[0].max_version == *version { - println!("Package {}@{} has already been published.", name, version); - return None; - } - } + unpublished.push(name.into()); + } - Some(Ok(name.clone())) - }) - .collect::>>() + Ok(unpublished) } } diff --git a/cli/conta/src/lib.rs b/cli/conta/src/lib.rs index 1241731f6..617454792 100644 --- a/cli/conta/src/lib.rs +++ b/cli/conta/src/lib.rs @@ -3,9 +3,8 @@ pub use crate::{ cmd::{Bump, Conta, Publish}, config::Config, - sed::Sed, }; mod cmd; mod config; -mod sed; +mod version; diff --git a/cli/conta/src/sed.rs b/cli/conta/src/sed.rs deleted file mode 100644 index 9b6703316..000000000 --- a/cli/conta/src/sed.rs +++ /dev/null @@ -1,117 +0,0 @@ -//! Manifest reader - -use anyhow::{anyhow, Result}; -use semver::{Version, VersionReq}; -use std::{ - fs, - path::{Path, PathBuf}, -}; - -const WORKSPACE_PACKAGE: &str = "[workspace.package]"; -const WORKSPACE_DEPENDENCIES: &str = "[workspace.dependencies]"; -const PATT_VERSION: &str = "version = \""; - -/// Position of version field -/// -/// This is used to determine the position of the version -/// field in the manifest file. -/// -/// # Example -/// -/// ```toml -/// [workspace.package] -/// version = "0.1.0" -/// ``` -/// -/// The range of Pos will be the first and the second quote. -#[derive(Debug, Clone)] -pub struct Pos { - /// Start position - pub start: usize, - /// End position - pub end: usize, -} - -/// Manifest stream editor. -pub struct Sed { - pub buf: Vec, - manifest: PathBuf, -} - -impl Sed { - /// Find the version field from provided context. - pub fn find_version(context: &str, start: usize, patt: &str) -> Result { - let mut start = context[start..] - .find(patt) - .ok_or(anyhow!("pattern {patt} not found"))? - + start - + patt.len(); - - start = context[start..] - .find(PATT_VERSION) - .ok_or(anyhow!("version not found"))? - + start - + PATT_VERSION.len(); - - let end = context[start..] - .find('"') - .ok_or(anyhow!("the end of version field is invalid"))? - + start; - - Ok(Pos { start, end }) - } - - /// Create a new manifest stream editor. - pub fn new(p: impl AsRef) -> Result { - let manifest = p.as_ref().into(); - let buf = fs::read(&manifest)?; - - Ok(Self { buf, manifest }) - } - - /// Set the version from pos. - pub fn set_version(&mut self, version: &str, pos: &Pos) -> Result<()> { - let Pos { start, end } = pos; - - let buf = self.buf.clone(); - let (before, after) = buf.split_at(*start); - let (_, after) = after.split_at(end - start); - - self.buf = [before, version.as_bytes(), after].concat(); - - Ok(()) - } - - /// Set the version of the root package. - pub fn set_dep_versions(&mut self, version: &VersionReq, packages: &[String]) -> Result<()> { - for package in packages { - let buf = self.buf.to_vec(); - let context = String::from_utf8_lossy(&buf); - let start = context - .find(WORKSPACE_DEPENDENCIES) - .ok_or(anyhow!("workspace.dependencies not found"))?; - - // TODO: Refactor the lines above - - let pos = Self::find_version(&context, start, &format!("{package} "))?; - self.set_version(&version.to_string(), &pos)?; - } - - Ok(()) - } - - /// Set the version of the root package. - pub fn set_workspace_version(&mut self, version: &Version) -> Result<()> { - let context = String::from_utf8_lossy(&self.buf); - - // Get the version position of the root package. - let root = Self::find_version(&context, 0, WORKSPACE_PACKAGE)?; - self.set_version(&version.to_string(), &root) - } - - /// Flush the changes to the manifest file. - pub fn flush(self) -> Result<()> { - fs::write(self.manifest, self.buf)?; - Ok(()) - } -} diff --git a/cli/conta/src/version.rs b/cli/conta/src/version.rs new file mode 100644 index 000000000..a587cab1f --- /dev/null +++ b/cli/conta/src/version.rs @@ -0,0 +1,31 @@ +//! Crate version verifier + +use anyhow::Result; +use serde::Deserialize; + +#[derive(Debug, Deserialize)] +struct Resp { + pub versions: Vec, +} + +#[derive(Debug, Deserialize)] +struct Version { + pub num: String, +} + +/// Verify if the package has already been published. +pub fn verify(name: &str, version: &str) -> Result { + let client = reqwest::blocking::Client::builder() + .user_agent("conta") + .build()?; + + if let Ok(resp) = client + .get(format!("https://crates.io/api/v1/crates/{name}/versions")) + .send()? + .json::() + { + return Ok(resp.versions.into_iter().any(|v| v.num == version)); + } + + Ok(false) +}