use std::error::Error;
use std::fmt;
use std::fs;
use std::path::{Path,PathBuf};
use log::{error,info};
use reqwest;
use reqwest::Url;
#[derive(Clone)]
pub struct Dependency {
name: String,
version: String
}
impl Dependency {
pub fn new(name: String, version: String) -> Self {
Dependency{name,version}
}
pub fn to_zipname(&self) -> String {
format!("{}-v{}.zip",self.name,self.version)
}
pub fn to_url(&self, base: &Url) -> Url {
let n = format!("{}/{}/{}",self.name,self.version,self.to_zipname());
base.join(&n).unwrap()
}
}
impl fmt::Display for Dependency {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f,"{}-{}",self.name,self.version)
}
}
#[derive(Clone,Debug,PartialEq)]
pub struct PackageResolver<T: AsRef<Path>> {
dir: T,
url: Url
}
impl<T: AsRef<Path>> PackageResolver<T> {
pub fn new(dir: T, url: Url) -> Self {
fs::create_dir_all(dir.as_ref()).unwrap();
PackageResolver{dir,url}
}
pub fn resolve(&self, deps : &[Dependency]) -> Result<(),Box<dyn Error>> {
for dep in deps {
self.get(&dep)?;
}
Ok(())
}
pub fn get<'b>(&self, dep: &Dependency) -> Result<PathBuf,Box<dyn Error>> {
let mut zip = PathBuf::new();
zip.push(self.dir.as_ref());
zip.push(dep.to_zipname());
if !zip.as_path().exists() {
let url = dep.to_url(&self.url);
let response = reqwest::blocking::get(url.clone())?;
if response.status().is_success() {
info!("Downloaded {}",url.as_str());
fs::write(zip.as_path(),response.bytes()?)?;
} else {
error!("Downloading {} ({:?})",url.as_str(),response.status());
return Err(Box::new(ResolutionError{dep:(*dep).clone()}));
}
}
Ok(zip)
}
}
#[derive(Clone)]
struct ResolutionError {
dep: Dependency
}
impl fmt::Display for ResolutionError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "failed resolving package {}",self.dep)
}
}
impl fmt::Debug for ResolutionError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "failed resolving package {}",self.dep)
}
}
impl Error for ResolutionError {}