use Error;
use package::{Package, PackagePath, PackageError};
use std::collections::HashMap;
use std::path::PathBuf;
use std::fs;
use walkdir::WalkDir;
use git2;
pub struct PackageManager
{
pub directory: PathBuf,
pub packages: HashMap<PackagePath, Package>,
}
impl PackageManager
{
pub fn new(directory: PathBuf) -> Result<Self, Error> {
let mut pm = PackageManager {
directory: directory,
packages: HashMap::new(),
};
pm.update_package_cache()?;
Ok(pm)
}
pub fn install(&mut self, path: &PackagePath) -> Result<(), Error> {
let url = path.url();
let destination = self.package_directory(path);
if destination.exists() {
return Err(Error::Package(PackageError::AlreadyExists));
}
git2::Repository::clone(&url, &destination).expect("error cloning");
let package = Package::open(path.clone(), &destination)?;
self.packages.insert(package.remote_path.clone(), package);
Ok(())
}
pub fn uninstall(&mut self, path: &PackagePath) -> Result<(), Error> {
match self.packages.remove(path) {
Some(package) => {
package.destroy()?;
Ok(())
},
None => Err(Error::Package(PackageError::NotInstalled)),
}
}
pub fn lookup(&self, path: &PackagePath) -> Option<&Package> {
self.packages.get(path)
}
pub fn lookup_mut(&mut self, path: &PackagePath) -> Option<&mut Package> {
self.packages.get_mut(path)
}
fn ensure_directory_exist(&mut self) -> Result<(), Error> {
if !self.directory.exists() {
fs::create_dir_all(&self.directory)?
}
Ok(())
}
fn update_package_cache(&mut self) -> Result<(), Error> {
self.ensure_directory_exist()?;
self.packages = self.current_packages(&self.directory)?;
Ok(())
}
fn package_directory(&self, path: &PackagePath) -> PathBuf {
self.directory.join(path.directory())
}
fn current_packages(&self, directory: &PathBuf) -> Result<HashMap<PackagePath, Package>, Error> {
let manifest_paths: Vec<_> = WalkDir::new(directory).min_depth(1).into_iter().filter_map(|entry| {
let entry = entry.unwrap();
let path = entry.path().to_owned();
if let Some(name) = path.file_name() {
if name == "package.toml" { Some(path.clone()) } else { None }
} else {
None
}
}).collect();
let package_paths: Vec<_> = manifest_paths.into_iter().map(|path| path.parent().unwrap().to_owned()).collect();
let packages: Result<Vec<_>, _> = package_paths.into_iter().map(|package_path| {
let relative_path = package_path.strip_prefix(&self.directory).unwrap();
let components: Vec<_> = relative_path.components().collect();
assert_eq!(components.len(), 2);
let remote_path = PackagePath::GitHub {
organisation: components[0].as_os_str().to_str().unwrap().to_owned(),
repository: components[1].as_os_str().to_str().unwrap().to_owned(),
};
Package::open(remote_path, &package_path)
}).collect();
let packages = packages?;
Ok(packages.into_iter().map(|package| (package.remote_path.clone(), package)).collect())
}
}