use anyhow::Error;
use directories::ProjectDirs;
use sha2::{Digest, Sha256};
use tokio::fs::File;
use tokio::io::AsyncWriteExt;
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;
use std::sync::Arc;
impl SearchEngine {
fn get_tmp_path_for_ref(&self, reference: &str) -> anyhow::Result<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)?;
path.push(format!("{hash}.mreg"));
Ok(path)
}
pub(super) async fn load_registry(&mut self, reference: &str) -> anyhow::Result<Registry<'_>> {
if self.registries.get(reference).is_none() {
let tmp_path = self.get_tmp_path_for_ref(reference)?;
if !tmp_path.exists() {
self.download_registry(&reference).await?;
}
let data = std::fs::read(&tmp_path)?;
self.registries.insert(
Arc::from(reference),
LoadedRegistry {
data: Arc::new(data),
last_used: Instant::now(),
},
);
}
let loaded = self
.registries
.get(reference)
.ok_or_else(|| anyhow::anyhow!("Registry not found in cache"))?;
Ok(root_as_registry(&loaded.data)?)
}
pub async fn download_registry(&self, reference: &str) -> anyhow::Result<Vec<u8>> {
let url = &self.resolve_registry_url(reference)?;
println!("DOWNLOADING REGISTRY ref'{}'", url);
let response = reqwest::get(url).await?;
if !response.status().is_success() {
anyhow::bail!(
"Failed to download registry '{}': {}",
url,
response.status()
);
}
let mut bytes = response.bytes().await?;
let tmp_path = self.get_tmp_path_for_ref(reference)?;
let tmp_path = tmp_path.as_path();
println!(
"SAVING REGISTRY IN '{}'",
&tmp_path.to_str().ok_or(anyhow::anyhow!("UTF-8 Error"))?
);
File::create(tmp_path).await?.write_buf(&mut 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}/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
)))
}
}
}