use reqwest;
use reqwest::Client;
use semver_rs::{Range, Version};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::HashMap;
mod error;
use error::CustomError;
pub type Result<T> = std::result::Result<T, CustomError>;
use anyhow::anyhow;
use std::collections::HashSet;
use std::collections::VecDeque;
use std::sync::Arc;
#[macro_use]
extern crate time_test;
use tokio::sync::Mutex;
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct PackageVersion {
pub versions: Value,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct PackageDependencies {
pub dependencies: Option<HashMap<String, String>>,
}
pub async fn resolve_dependency(
client: Client,
dependencies: HashMap<String, String>,
) -> Result<()> {
let context: Arc<Mutex<HashSet<String>>> = Arc::new(Mutex::new(HashSet::new()));
let queue: Arc<Mutex<VecDeque<(String, String)>>> = Arc::new(Mutex::new(VecDeque::new()));
for (p, v) in dependencies {
queue.lock().await.push_back((p, v));
}
loop {
if !queue.lock().await.is_empty() {
let (package, version) = queue.lock().await.pop_front().unwrap();
let queue = queue.clone();
let context = context.clone();
let client = client.clone();
tokio::spawn(async move {
let mut queue = queue.lock().await;
println!("{} {}", package, version);
let mut available_versions = Vec::new();
let range: Range = Range::new(&version).parse().unwrap();
let package_url = format!("https://registry.npmjs.org/{}", package);
let resp = client.get(package_url).send().await.unwrap();
let version_info: PackageVersion = resp.json().await.unwrap();
for key in version_info.versions.as_object().unwrap().keys() {
let version = Version::new(key).parse().unwrap();
if range.test(&version) {
available_versions.push(version);
}
}
let target = match available_versions.iter().max() {
Some(version) => version.to_string(),
None => return Err(CustomError::Other(anyhow!("version missing"))),
};
let ident = format!("{}+{}", package, target);
let mut context = context.lock().await;
if context.get(&ident).is_none() {
context.insert(ident);
let package_version_url =
format!("https://registry.npmjs.org/{}/{}", package, version);
let resp = client.get(package_version_url).send().await?;
let dependency_info: PackageDependencies = resp.json().await?;
if let Some(dependencies) = dependency_info.dependencies {
for (p, v) in dependencies {
queue.push_back((p, v));
}
}
}
Ok(())
});
}
}
println!("{:?}", context);
Ok(())
}