aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main.rs183
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.