use std::path::PathBuf; use comrade::rally; use rand::seq::SliceRandom; use super::{Package, Repository, arch::Architecture}; pub struct MirrorRepository { pub inner: Repository, } impl MirrorRepository { pub fn new(repo: &str) -> Self { std::fs::create_dir_all(format!("./data/{repo}")).unwrap(); Self { inner: Repository::new(repo).unwrap(), } } pub async fn download_file( &self, url: &str, file_path: PathBuf, mirrorlist: &[String], arch: Architecture, ) { log::info!("Downloading {url} to {}", file_path.to_str().unwrap()); let arch = if matches!(arch, Architecture::any) { Architecture::x86_64.to_string() // `any` packages dont get served under repo "any" for some reason } else { arch.to_string() }; let parent = file_path.parent().unwrap(); std::fs::create_dir_all(parent).unwrap(); let mirrorlist_links = mirrorlist .into_iter() .take(5) .map(|x| { let mirror = x.replace("$repo", &self.inner.name).replace("$arch", &arch); ( x.clone(), format!("{mirror}/{url}"), file_path.to_str().unwrap().to_string(), ) }) .collect(); let data = rally(mirrorlist_links, |input| { let (mirror, url, file_path) = input; let client = reqwest::blocking::Client::new(); if let Ok(resp) = client.get(url).send() { if resp.status().is_success() { let data = resp.bytes().unwrap().to_vec(); log::info!("Downloaded {url}"); return Some(data); } else { log::warn!("Mirror {mirror} failed [{}]", resp.status().as_u16()); return None; } } None }); if let Some(data) = data.1 { std::fs::write(file_path, data).unwrap(); } else { log::error!("Downloading {url} failed. No mirror could provide a valid response."); } } /// Get the `.db.tar.gz` content for the repository of `arch` pub async fn db_content(&self, arch: Architecture, mirrorlist: &[String]) -> Option> { self.download_file( &format!("{}.db.tar.gz", self.inner.name), self.inner .base_path(arch.clone()) .join(format!("{}.db.tar.gz", self.inner.name)), mirrorlist, arch.clone(), ) .await; self.inner.db_content(arch) } /// Get the `.db.tar.gz.sig` content for the repository of `arch` pub async fn sig_content(&self, arch: Architecture, mirrorlist: &[String]) -> Option> { self.download_file( &format!("{}.db.tar.gz.sig", self.inner.name), self.inner .base_path(arch.clone()) .join(format!("{}.db.tar.gz.sig", self.inner.name)), mirrorlist, arch.clone(), ) .await; self.inner.sig_content(arch) } pub async fn get_pkg(&self, pkg_name: &str, mirrorlist: &[String]) -> Option { if let Some(pkg) = self.inner.get_pkg(pkg_name) { return Some(pkg); } // PKG let (name, _, _, arch, _) = Package::extract_pkg_name(pkg_name).unwrap(); self.download_package(pkg_name, &name, arch, mirrorlist) .await; self.inner.get_pkg(pkg_name) } pub async fn download_package( &self, pkg_name: &str, name: &str, arch: Architecture, mirrorlist: &[String], ) { self.download_file( pkg_name, self.inner.base_path(arch.clone()).join(name).join(pkg_name), mirrorlist, arch.clone(), ) .await; // SIG self.download_file( &format!("{pkg_name}.sig"), self.inner .base_path(arch.clone()) .join(&name) .join(format!("{pkg_name}.sig")), mirrorlist, arch.clone(), ) .await; } }