A2PPR5WVZ2J45NIQCKDB37QTR2TAORVC2B2WKZQALKDW6IUNQEKAC e.installed = installed.mods.iter().any(|f| f.package_name == e.name && f.version == e.version);
if let Ok(installed) = &installed {e.installed = installed.mods.iter().any(|(n, f)| n == &e.name && f.version == e.version);}if let Ok(glob) = &glob {e.global = glob.mods.iter().any(|(n, f)| n == &e.name && f.version == e.version);}
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(())}
cluster: Cluster::find().unwrap_or(None),}
cluster: Cluster::find().unwrap_or(None), //don't use `?` here so we don't crash everything if there's no cluster// local_installed: l_mods,// global_installed: g_mods,})
let mut installed = if let ManageMode::Client = ctx.config.mode {utils::get_installed(ctx.config.mod_dir())} else if let Some(c) = ctx.cluster.as_ref() {let mut chain = LocalIndex::new();for e in c.members.values() {chain.mods.extend(utils::get_installed(e)?.mods);}debug!("Chained clustered mods: {:#?}", chain);Ok(chain)
let mut installed = LocalIndex::load(ctx.config.mod_dir()).ok();let mut global = LocalIndex::load_or_create(ctx.dirs.data_local_dir());let outdated: Vec<&model::Mod> = if let Some(installed) = &installed {index.iter().filter(|e| {installed.mods.iter().any(|(n, i)| n.trim() == e.name.trim() && i.version.trim() != e.version.trim())}).collect()
Err(anyhow!("Failed to get clustered mods"))}?;let mut global = utils::get_installed(ctx.dirs.data_local_dir())?;let outdated: Vec<&model::Mod> = index.iter().filter(|e| {installed.mods.iter().any(|i| {i.package_name.trim() == e.name.trim() && i.version.trim() != e.version.trim()})}).collect();
vec![]};
//check if any link mods are being updatedlet relink = installed.linked.clone().into_iter().filter(|e| glob_outdated.iter().any(|f| e.package_name == f.name));
if let Some(installed) = installed.as_mut() {//check if any link mods are being updatedlet relink = installed.linked.clone().into_iter().filter(|(e, _)| glob_outdated.iter().any(|f| e == &f.name));
for r in relink {debug!("Relinking mod {}", r.package_name);//Update the submod linksfor p in r.mods.iter() {//delete the current link firstlet target = ctx.local_target.join(&p.name);if target.exists() {fs::remove_dir_all(&target)?;
for (name, r) in relink {debug!("Relinking mod {}", name);//Update the submod linksfor p in r.mods.iter() {//delete the current link firstlet target = ctx.local_target.join(&p.name);if target.exists() {fs::remove_dir_all(&target)?;}link_dir(&p.path, &target)?;
//replace the linked mod with the new mod infolet n = global.mods.iter().find(|e| e.package_name == r.package_name).ok_or_else(|| anyhow!("Unable to find linked mod in global index"))?;if !installed.linked.remove(&r) {debug!("Didn't find old linked mod to remove");
//replace the linked mod with the new mod infolet (n, m) = global.mods.iter().find(|(e, _)| *e == &r.package_name).ok_or_else(|| anyhow!("Unable to find linked mod in global index"))?;//Insert or update the mod in the linked setinstalled.linked.entry(n.to_owned()).and_modify(|v| *v = m.clone()).or_insert_with(|| m.clone());
}}}Ok(())}async fn cluster_update(ctx: &mut Ctx, yes: bool) -> Result<()> {if let Some(c) = ctx.cluster.clone() {println!("Updating server cluster{}...",if c.name.is_some() {format!(" {}", c.name.as_ref().unwrap())} else {"".to_string()});let index = utils::update_index(&ctx.local_target, &ctx.global_target).await;//update global mods firstlet mut to_relink = vec![];let mut global_installed = LocalIndex::load(&ctx.global_target)?;for g in index.iter().filter(|m| m.global) {if let Some(_o) = global_installed.mods.iter().find(|(n, e)| **n == g.name && e.version != g.version){to_relink.push((&g.name, g));
if !to_relink.is_empty() {let size: i64 = to_relink.iter().map(|f| f.1.file_size).sum::<i64>();println!("Updating global mod(s): \n");print!("\t");to_relink.iter().enumerate().for_each(|(i, f)| {//Only display 5 mods per rowif i > 0 && i % 5 == 0 {println!("\n");print!("\t");}print!(" \x1b[36m{}@{}\x1b[0m ", f.0, f.1.version);});println!("\n");if !yes {if let Ok(line) = ctx.rl.readline(&format!("Will download ~{:.2} MB (compressed), okay? (This will overwrite any changes made to mod files) [Y/n]: ",size as f64 / 1_048_576f64)) {if line.to_lowercase() == "n" {return Ok(());}} else {return Ok(());}}do_update(ctx,&to_relink.iter().map(|(_, m)| *m).collect(),&mut global_installed,&ctx.global_target.clone(),).await?;}for s in c.members.iter() {let _name = s.0;let path = s.1;let _installed = LocalIndex::load(path);}
let mut installed = utils::get_installed(ctx.config.mod_dir())?;let valid: Vec<InstalledMod> = mod_names
let mod_names = mod_names.into_iter().map(|n| n.to_lowercase()).collect::<String>();let installed = LocalIndex::load(ctx.config.mod_dir())?;let valid: Vec<InstalledMod> = installed.mods
.filter_map(|f| {installed.mods.clone().iter().find(|e| e.package_name.trim().to_lowercase() == f.trim().to_lowercase()).filter(|e| installed.mods.remove(e)).cloned()
.filter_map(|(n, v)| {if mod_names.contains(&n.to_lowercase()) {Some(v.clone())} else {None}
use anyhow::{anyhow, Result};use clap::Subcommand;use crate::{api::model::Profile, core::Ctx};#[derive(Subcommand)]pub enum ProfCommands {///Create a new mod profileCreate { name: String },///Add a mod to a profileAdd {name: String,///Profile to modify to. Defaults to the current profile#[clap(long, short)]profile: Option<String>,},///Remove a mod from the a profileRemove {name: String,///Profile to modify. Defaults to the current profile#[clap(long, short)]profile: Option<String>,},}pub fn profile(ctx: &mut Ctx, command: ProfCommands) -> Result<()> {match command {ProfCommands::Create { name } => {Profile::get(ctx.dirs.config_dir(), &name)?;println!("Created profile \"{}\"", name);}ProfCommands::Add { name, profile } => add_mod(ctx, name, profile)?,_ => {}}Ok(())}//Add a mod to the target profilefn add_mod(ctx: &mut Ctx, name: String, pname: Option<String>) -> Result<()> {let mut target = if let Some(p) = pname {Profile::get(ctx.dirs.config_dir(), &p)?} else {Profile::get(ctx.dirs.config_dir(), &ctx.config.profile)?};if let Some(m) = ctx.local_installed.mods.iter().find(|e| e.package_name.to_lowercase() == name.to_lowercase()){target.mods.insert(m.clone());} else if let Some(m) = ctx.global_installed.mods.iter().find(|e| e.package_name.to_lowercase() == name.to_lowercase()){target.mods.insert(m.clone());} else {println!("Unable to find mod '{}'", name);return Ok(());}println!("Added mod '{}' to profile {}", name, target.name);Ok(())}
let index = utils::update_index(target).await;let mut installed = utils::get_installed(target)?;
//Create the target dir if it doesn't existif !target.exists() {log::trace!("Creating dir {}", target.display());fs::create_dir_all(target)?;}let index = utils::update_index(target, &ctx.global_target).await;let mut installed = LocalIndex::load_or_create(target);
for i in installed.mods.clone().iter() {installed.mods.remove(i);let mut i = i.clone();if i.package_name.to_lowercase() == m {for sub in i.mods.iter_mut() {
for (_i, p) in installed.mods.iter_mut() {if p.package_name.to_lowercase() == m {for sub in p.mods.iter_mut() {
use crate::core::Ctx;use anyhow::Result;pub(crate) fn remove(ctx: &mut Ctx, name: String) -> Result<()> {if let Some(c) = ctx.cluster.as_mut() {if !c.members.contains_key(&name) {println!("Couldn't find member with name '{}'", name);return Ok(());}c.members.remove(&name);println!("Removed '{}' from cluster", name);} else {println!("There is no cluster set up!");}Ok(())}
use anyhow::Result;use crate::core::Ctx;pub(crate) fn list(ctx: &Ctx) -> Result<()> {if let Some(c) = ctx.cluster.as_ref() {c.members.iter().for_each(|(k, v)| {println!("{} at {}", k, v.display());});}Ok(())}
for p in mods.iter().map(|p| (&p.path, mods_dir.join(p.path.file_name().unwrap()))){if p.1.exists() {fs::remove_dir_all(&p.1)?;
// move the mod files from the temp dir to the real dirfor p in mods.iter() {let temp = temp_dir.join(&p.path);let perm = mods_dir.join(&p.path);if perm.exists() {fs::remove_dir_all(&perm)?;
pub fn new() -> Self {Self {mods: HashSet::new(),linked: HashSet::new(),
pub fn load(path: &Path) -> Result<Self> {if path.join(".papa.ron").exists() {let raw = fs::read_to_string(path.join(".papa.ron"))?;let mut parsed = ron::from_str::<Self>(&raw)?;parsed.path = Some(path.join(".papa.ron"));Ok(parsed)} else {Err(anyhow::anyhow!("No such file"))}}pub fn load_or_create(path: &Path) -> Self {match Self::load(path) {Ok(s) => s,Err(_) => Self::create(path),}}pub fn create(path: &Path) -> Self {let mut ind = Self::default();ind.path = Some(path.join(".papa.ron"));ind}pub fn save(&self) -> Result<()> {if let Some(p) = &self.path {let parsed = ron::to_string(self)?;if let Some(p) = p.parent() {fs::create_dir_all(p)?;}fs::write(&p, &parsed).context("Unable to write index")} else {Err(anyhow::anyhow!("Tried to save local index but the path wasn't set"))}}}impl Drop for LocalIndex {fn drop(&mut self) {if self.path.is_some() {self.save().expect("Failed to write index to disk");
Ok(())}}#[derive(Serialize, Deserialize, Clone, Debug)]pub struct Profile {#[serde(skip)]path: Option<PathBuf>,pub name: String,pub mods: HashSet<InstalledMod>,}#[allow(dead_code)]impl Profile {pub fn get(dir: &Path, name: &str) -> Result<Self> {let fname = format!("{}.ron", name);let path = dir.join(&fname);let raw = if path.exists() {fs::read_to_string(&path)?} else {String::new()};let mut p: Self = if raw.is_empty() {Profile {path: None,name: name.to_owned(),mods: HashSet::new(),}} else {ron::from_str(&raw).with_context(|| format!("Failed to parse profile {}", fname))?};p.path = Some(path);Ok(p)}
@draft t:@cargo build --release@cargo deb@gh release create {{t}} --notes "# Papa {{t}}" -d --title "Papa {{t}}"@fish -c "gh release upload {{t}} target/debian/papa_(string replace 'v' '' {{t}})_amd64.deb"@gh release upload {{t}} target/release/papa
build:cargo build@test *args='':cargo run --features="cluster" -- $@