Untitled

 avatar
unknown
rust
3 years ago
3.2 kB
3
Indexable
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(())
}