diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/main.rs | 183 |
1 files changed, 83 insertions, 100 deletions
diff --git a/src/main.rs b/src/main.rs index 87f8847..5b8c19c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,7 +12,9 @@ use log::debug; use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor}; use modderbaas::{ - minemod::ModContainer, util, Baas, ContentDb, Downloader, MineMod, Snapshot, Source, World, + baas::{Baas, Installer}, + minemod::{self, ModContainer}, + util, ContentDb, Downloader, MineMod, Snapshot, Source, World, }; fn main() -> Result<()> { @@ -96,7 +98,7 @@ fn main() -> Result<()> { if let Some(install) = matches.subcommand_matches("install") { let mods = install.values_of("mod").unwrap().collect::<Vec<_>>(); - install_mods(&mut stdout, &snapshot, &world, &mods, install)?; + install_mods(&mut stdout, &baas, &world, &mods, install)?; } Ok(()) @@ -207,121 +209,88 @@ fn enable_mods( /// Install the given mods, installing dependencies if needed. fn install_mods( output: &mut StandardStream, - snapshot: &Snapshot, + baas: &Baas, world: &World, mods: &[&str], matches: &ArgMatches, ) -> Result<()> { let target_dir = Path::new(matches.value_of("target").unwrap()); let dry_run = matches.is_present("dry-run"); - let chown_after = matches.is_present("chown"); + let chown = matches.is_present("chown"); let content_db = ContentDb::new(); let downloader = Downloader::new()?; - let mut wanted = mods + let sources = mods .iter() .map(|&s| Source::from_str(s)) .collect::<Result<Vec<_>, _>>()?; - let mut to_install = Vec::<Box<dyn ModContainer>>::new(); - let mut to_enable = Vec::<MineMod>::new(); + let installer = InteractiveInstaller { + output, + content_db, + target_dir, + dry_run, + chown, + }; + baas.install(installer, &downloader, world, sources)?; - let game = snapshot - .games() - .get(&world.game_id()?) - .ok_or_else(|| anyhow!("The game definition was not found"))?; - let game_mods = game - .mods()? - .into_iter() - .map(|m| m.mod_id()) - .collect::<Result<Vec<_>, _>>()?; + writeln!(output, "Done!")?; - 'modloop: while !wanted.is_empty() { - let next_mod = wanted.remove(0); + Ok(()) +} - // Special handling for mods specified by their ID, as those could already exist. - if let Source::ModId(ref id) = next_mod { - if let Some(m) = snapshot.mods().get(id) { - // We have that one, just enable it and its dependencies! - to_enable.push(m.clone()); - wanted.extend(m.dependencies()?.into_iter().map(Source::ModId)); - continue; - } else if game_mods.contains(id) { - // This mod is already contained in the game, nothing for us to do - continue; - } +/// The installer that interactively asks the user about choices. +struct InteractiveInstaller<'o, 'p> { + output: &'o mut StandardStream, + content_db: ContentDb, + target_dir: &'p Path, + dry_run: bool, + chown: bool, +} - // Is this a mod that is already queued for installation? - for mod_or_pack in &to_install { - for m in mod_or_pack.mods()? { - if &m.name()? == id { - continue 'modloop; - } - } - } +impl<'o, 'p> Installer for InteractiveInstaller<'o, 'p> { + type Error = anyhow::Error; - // This mod is not available, so we search the content DB - writeln!(output, "Searching for candidates: {}", id)?; - let candidates = content_db.resolve(id)?; - if candidates.is_empty() { - bail!("Could not find a suitable mod for '{}'", id); - } else if candidates.len() == 1 { - wanted.push(Source::Http(candidates.into_iter().next().unwrap().url)); - } else { - let items = candidates - .into_iter() - .map(|c| { - ( - format!("{} by {} - {}", c.title, c.author, c.short_description), - c, - ) - }) - .collect::<Vec<_>>(); - writeln!( - output, - "{} candidates found, please select one:", - items.len() - )?; - let candidate = user_choice(&items, output)?; - wanted.push(Source::Http(candidate.url.clone())); - } + fn resolve(&mut self, mod_id: &str) -> Result<Source> { + writeln!(&mut self.output, "Searching for candidates: {}", mod_id)?; + + let candidates = self.content_db.resolve(mod_id)?; + if candidates.is_empty() { + bail!("Could not find a suitable mod for '{}'", mod_id); + } else if candidates.len() == 1 { + Ok(Source::Http(candidates.into_iter().next().unwrap().url)) } else { - let downloaded = downloader - .download(&next_mod) - .context("Failed to download mod")?; - for m in downloaded.mods()? { - wanted.extend(m.dependencies()?.into_iter().map(Source::ModId)); - } - to_install.push(downloaded); + let items = candidates + .into_iter() + .map(|c| { + ( + format!("{} by {} - {}", c.title, c.author, c.short_description), + c, + ) + }) + .collect::<Vec<_>>(); + writeln!( + &mut self.output, + "{} candidates found, please select one:", + items.len() + )?; + let candidate = user_choice(&items, self.output)?; + Ok(Source::Http(candidate.url.clone())) } } - writeln!(output, "Installing {} new mods:", to_install.len())?; - writeln!(output, "{}", to_install.iter().join(", "))?; - - ask_continue(output)?; + fn install_mod(&mut self, mod_or_pack: &dyn ModContainer) -> Result<Box<dyn ModContainer>> { + let mod_id = mod_or_pack.name()?; + writeln!(&mut self.output, "Installing {}", mod_id)?; - if dry_run { - for m in to_install { - let mod_id = m.name()?; - let mod_dir = target_dir.join(&mod_id); - writeln!(output, "Installing {} to {:?}", m, mod_dir)?; - to_enable.extend(m.mods()?); + if self.dry_run { + // Re-open so we get a fresh Box<> + return Ok(minemod::open_mod_or_pack(mod_or_pack.path())?); } - for m in to_enable { - let mod_id = m.mod_id()?; - writeln!(output, "Enabling {}", mod_id)?; - } - return Ok(()); - } - for m in to_install { - let mod_id = m.name()?; - writeln!(output, "Installing {}", mod_id)?; - let installed = m - .install_to(target_dir) + let installed = mod_or_pack + .install_to(self.target_dir) .context(format!("Error installing '{}'", mod_id))?; - to_enable.extend(installed.mods()?); #[cfg(unix)] { @@ -329,24 +298,38 @@ fn install_mods( sys::stat, unistd::{Gid, Uid}, }; - if chown_after { - let perms = stat::stat(target_dir)?; + if self.chown { + let perms = stat::stat(self.target_dir)?; let (uid, gid) = (Uid::from_raw(perms.st_uid), Gid::from_raw(perms.st_gid)); util::chown_recursive(installed.path(), Some(uid), Some(gid))?; } } + + Ok(installed) } - for m in to_enable { - let mod_id = m.mod_id()?; - world - .enable_mod(&mod_id) - .context(format!("Error enabling '{}'", mod_id))?; + fn display_changes(&mut self, to_install: &[Box<dyn ModContainer>]) -> Result<()> { + writeln!( + &mut self.output, + "Installing {} new mods:", + to_install.len() + )?; + writeln!(&mut self.output, "{}", to_install.iter().join(", "))?; + + ask_continue(self.output) } - writeln!(output, "Done!")?; + fn enable_mod(&mut self, world: &World, minemod: &MineMod) -> Result<()> { + let mod_id = minemod.mod_id()?; + writeln!(&mut self.output, "Enabling {}", mod_id)?; - Ok(()) + if !self.dry_run { + world + .enable_mod(&mod_id) + .context(format!("Error enabling '{}'", mod_id))?; + } + Ok(()) + } } /// Presents the user with a choice of items and awaits a selection. |