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.  | 
