mercur 0.1.2

mercur - junolang package manager
use anyhow::Error;
use chrono::format::ParseErrorKind;
use directories::ProjectDirs;
use sha2::{Digest, Sha256};
use tokio::time::Instant;

use crate::registry::schema::registry::{Registry, root_as_registry};
use crate::registry::search::{LoadedRegistry, SearchEngine};
use std::fs;
use std::path::PathBuf;

impl SearchEngine {
    fn get_tmp_path_for_ref(&self, reference: String) -> PathBuf {
        let proj_dirs = ProjectDirs::from("dev", "mercurpkg", "mercur")
            .expect("could not determine cache directory");

        let mut hasher = Sha256::new();
        hasher.update(reference.as_bytes());
        let hash = hex::encode(hasher.finalize());

        let mut path = proj_dirs.cache_dir().to_path_buf();
        path.push("registries");

        fs::create_dir_all(&path).unwrap();

        path.push(format!("{hash}.mreg"));

        path
    }
    pub(super) fn load_registry(&mut self, reference: String) -> Registry {
        if self.registries.contains_key(&reference) {
            return root_as_registry(&self.registries.get(&reference).unwrap().data).unwrap();
        } else {
            let tmp_path = self.get_tmp_path_for_ref(reference.clone());
            if !tmp_path.exists() {
                self.download_registry(reference.clone().as_str());
            }
            let data = std::fs::read(&tmp_path).unwrap();

            self.registries.insert(
                reference.clone(),
                LoadedRegistry {
                    reference: reference.clone(),
                    data,
                    last_used: Instant::now(),
                },
            );

            let loaded = self.registries.get(&reference).unwrap();

            root_as_registry(&loaded.data).unwrap()
        }
    }

    pub async fn download_registry(&self, reference: &str) -> anyhow::Result<Vec<u8>> {
        let url = self.resolve_registry_url(reference)?;

        let bytes = reqwest::get(url).await?.bytes().await?;

        Ok(bytes.to_vec())
    }

    pub fn resolve_registry_url(&self, reference: &str) -> anyhow::Result<String> {
        if let Some(rest) = reference.strip_prefix("github:") {
            let mut rest = rest;
            let mut branch = "main";
            let s: Vec<&str> = rest.split("#").collect();
            if (s.len() == 2) {
                rest = s[0];
                branch = s[1];
            }
            Ok(format!(
                "https://raw.githubusercontent.com/{rest}/{branch}/mercur/registry.mreg"
            ))
        } else if reference.starts_with("http://") || reference.starts_with("https://") {
            Ok(reference.to_string())
        } else {
            Err(Error::msg(format!(
                "can not parse reference '{}'",
                reference
            )))
        }
    }
}