use crate::api;
use crate::api::model::LocalIndex;
use crate::api::model::SubMod;
use crate::model;
use crate::model::InstalledMod;
use crate::model::Mod;
use anyhow::{anyhow, Context, Result};
use directories::ProjectDirs;
use std::fs::{self, File, OpenOptions};
use std::io::Write;
use std::path::Path;
#[macro_export]
macro_rules! g2re {
($e:expr) => {{
let re = $e.replace('*', ".+");
regex::Regex::new(&re)
}};
}
pub async fn update_index(path: &Path) -> Vec<model::Mod> {
print!("Updating package index...");
let mut index = api::get_package_index().await.unwrap().to_vec();
let installed = get_installed(path).unwrap();
for e in index.iter_mut() {
e.installed = installed
.mods
.iter()
.any(|f| f.package_name == e.name && f.version == e.version);
}
println!(" Done!");
index
}
pub fn get_installed(path: &Path) -> Result<LocalIndex> {
let path = path.join(".papa.ron");
if path.exists() {
let raw = fs::read_to_string(path).context("Unable to read installed packages")?;
Ok(ron::from_str(&raw)?)
} else {
if let Some(p) = path.parent() {
if !p.exists() {
fs::create_dir_all(p)?;
}
}
File::create(path)
.context("Unable to create installed package index")?
.write_all(ron::to_string(&LocalIndex::new()).unwrap().as_bytes())?;
Ok(LocalIndex::new())
}
}
#[inline]
pub fn save_installed(path: &Path, installed: &LocalIndex) -> Result<()> {
let path = path.join(".papa.ron");
save_file(&path, ron::to_string(installed).unwrap())?;
Ok(())
}
#[inline]
pub fn check_cache(path: &Path) -> Option<File> {
if let Ok(f) = OpenOptions::new().read(true).open(path) {
Some(f)
} else {
None
}
}
pub fn ensure_dirs(dirs: &ProjectDirs) {
fs::create_dir_all(dirs.cache_dir()).unwrap();
fs::create_dir_all(dirs.config_dir()).unwrap();
}
pub fn remove_file(path: &Path) -> Result<()> {
fs::remove_file(path).context(format!("Unable to remove file {}", path.display()))
}
pub fn clear_cache(dir: &Path, force: bool) -> Result<()> {
for entry in fs::read_dir(dir).context(format!("unable to read directory {}", dir.display()))? {
let path = entry.context("Error reading directory entry")?.path();
if path.is_dir() {
clear_cache(&path, force)?;
fs::remove_dir(&path)
.context(format!("Unable to remove directory {}", path.display()))?;
} else if path.ends_with(".zip") || force {
fs::remove_file(&path).context(format!("Unable to remove file {}", path.display()))?;
}
}
Ok(())
}
#[inline]
pub fn save_file(file: &Path, data: String) -> Result<()> {
fs::write(file, data.as_bytes())?;
Ok(())
}
pub fn resolve_deps<'a>(
valid: &mut Vec<&'a Mod>,
base: &'a Mod,
installed: &'a Vec<InstalledMod>,
index: &'a Vec<Mod>,
) -> Result<()> {
for dep in &base.deps {
let dep_name = dep.split('-').collect::<Vec<&str>>()[1];
if !installed.iter().any(|e| e.package_name == dep_name) {
if let Some(d) = index.iter().find(|f| f.name == dep_name) {
resolve_deps(valid, d, installed, index)?;
valid.push(d);
} else {
return Err(anyhow!(
"Unable to resolve dependency {} of {}",
dep,
base.name
));
}
}
}
Ok(())
}
pub fn disable_mod(m: &mut SubMod) -> Result<bool> {
if m.disabled() {
return Ok(false);
}
let name = &m.name;
let old_path = m.path.clone();
let dir = m.path.parent().unwrap().join(".disabled");
if !dir.exists() {
fs::create_dir_all(&dir)?;
}
m.path = dir.join(name);
fs::rename(old_path, &m.path).context("Failed to rename mod")?;
Ok(true)
}
pub fn enable_mod(m: &mut SubMod, mods_dir: &Path) -> Result<bool> {
if !m.disabled() {
return Ok(false);
}
let old_path = m.path.clone();
m.path = mods_dir.join(&m.name);
fs::rename(old_path, &m.path).context("Failed to reanem mod")?;
Ok(true)
}