diff options
author | Daniel Schadt <kingdread@gmx.de> | 2021-11-09 12:46:43 +0100 |
---|---|---|
committer | Daniel Schadt <kingdread@gmx.de> | 2021-11-09 12:50:33 +0100 |
commit | d21c5dc9c53b02620fce916ffc1a2695e9d3f698 (patch) | |
tree | fb17ba6ee4e0a1ed532ab6e570418abff5264616 /src/minemod.rs | |
parent | 919796ec954414ae16f113896bdf212381c96437 (diff) | |
download | modderbaas-d21c5dc9c53b02620fce916ffc1a2695e9d3f698.tar.gz modderbaas-d21c5dc9c53b02620fce916ffc1a2695e9d3f698.tar.bz2 modderbaas-d21c5dc9c53b02620fce916ffc1a2695e9d3f698.zip |
Separate the binary and library
This uses the workspace feature of cargo, with the benefit that
1) We can more cleanly group the binary (user facing) code from the
library
2) We can have dependencies that apply to the binary only
The first point could've been achieved without workspaces (Cargo
supports both binaries and libraries in a crate), but the second point
is what really makes this approach a lot better.
Diffstat (limited to 'src/minemod.rs')
-rw-r--r-- | src/minemod.rs | 242 |
1 files changed, 0 insertions, 242 deletions
diff --git a/src/minemod.rs b/src/minemod.rs deleted file mode 100644 index 456d1c6..0000000 --- a/src/minemod.rs +++ /dev/null @@ -1,242 +0,0 @@ -//! Module to interact with installed mods and modpacks. -//! -//! Due to technical reasons (`mod` being a Rust keyword), this module is called `minemod` and the -//! mod objects are called [`MineMod`]. -//! -//! Simple mods are represented by [`MineMod`], which can be opened by [`MineMod::open`]. Valid -//! mods are identified by their `mod.conf`, which contains metadata about the mod. -//! -//! Modpacks can be represented by [`Modpack`] and loaded through [`Modpack::open`]. A modpack is -//! just a collection of mods grouped together, and the modpack directory needs to have a -//! `modpack.conf` in its directory. -//! -//! # Mods and Packs United -//! -//! In some cases, we cannot know in advance whether we are dealing with a mod or a modpack (e.g. -//! when downloading content from ContentDB). Therefore, the trait [`ModContainer`] exists, which -//! can be used as a trait object (`Box<dyn ModContainer>`). It provides the most important methods -//! and allows downcasting through `Any`. -//! -//! If you want to work with the mods directly, you can use [`ModContainer::mods`], which returns -//! the mod itself for [`MineMod`]s, and all contained mods for [`Modpack`]s. -//! -//! If you want to open a directory as a [`ModContainer`], you can use [`open_mod_or_pack`]. - -use std::{ - any::Any, - collections::HashMap, - fmt, fs, - path::{Path, PathBuf}, -}; - -use super::{ - error::{Error, Result}, - kvstore, scan, util, -}; - -/// The type of the ID that is used to identify Minetest mods. -pub type ModId = String; - -/// A minemod is a mod that is saved somewhere on disk. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct MineMod { - path: PathBuf, -} - -impl MineMod { - /// Opens the given directory as a mod. - pub fn open<P: AsRef<Path>>(path: P) -> Result<MineMod> { - MineMod::open_path(path.as_ref()) - } - - fn open_path(path: &Path) -> Result<MineMod> { - let conf = path.join("mod.conf"); - if !conf.is_file() { - return Err(Error::InvalidModDir(path.into())); - } - - Ok(MineMod { path: path.into() }) - } - - /// Returns the path of this mod. - pub fn path(&self) -> &Path { - &self.path - } - - fn read_conf(&self) -> Result<HashMap<String, String>> { - let conf = self.path.join("mod.conf"); - kvstore::read(&conf) - } - - /// Read the mod ID. - pub fn mod_id(&self) -> Result<ModId> { - let conf = self.read_conf()?; - conf.get("name") - .map(Into::into) - .ok_or_else(|| Error::InvalidModDir(self.path.clone())) - } - - /// Returns all dependencies of this mod. - pub fn dependencies(&self) -> Result<Vec<ModId>> { - let conf = self.read_conf()?; - static EMPTY: String = String::new(); - let depstr = conf.get("depends").unwrap_or(&EMPTY); - Ok(depstr - .split(',') - .map(str::trim) - .filter(|s| !s.is_empty()) - .map(Into::into) - .collect()) - } - - /// Copies the mod to the given path. - /// - /// Note that the path should not include the mod directory, that will be appended - /// automatically. - /// - /// Returns a new [`MineMod`] object pointing to the copy. - pub fn copy_to<P: AsRef<Path>>(&self, path: P) -> Result<MineMod> { - let path = path.as_ref().join(self.mod_id()?); - fs::create_dir_all(&path)?; - util::copy_recursive(&self.path, &path)?; - MineMod::open(&path) - } -} - -impl fmt::Display for MineMod { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.mod_id().map_err(|_| fmt::Error)?) - } -} - -/// Represents an on-disk modpack. -/// -/// We don't support many modpack operations besides listing the modpack contents. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct Modpack { - path: PathBuf, -} - -impl Modpack { - /// Opens the given directory as a modpack. - pub fn open<P: AsRef<Path>>(path: P) -> Result<Modpack> { - Modpack::open_path(path.as_ref()) - } - - fn open_path(path: &Path) -> Result<Modpack> { - let conf = path.join("modpack.conf"); - if !conf.is_file() { - return Err(Error::InvalidModpackDir(path.into())); - } - - Ok(Modpack { path: path.into() }) - } - - /// Returns the path of this modpack. - pub fn path(&self) -> &Path { - &self.path - } - - fn conf(&self) -> Result<HashMap<String, String>> { - let conf = self.path.join("modpack.conf"); - kvstore::read(&conf) - } - - /// Returns the name of the modpack. - pub fn name(&self) -> Result<String> { - self.conf()? - .get("name") - .map(Into::into) - .ok_or_else(|| Error::InvalidModDir(self.path.clone())) - } - - /// Return all mods contained in this modpack. - pub fn mods(&self) -> Result<Vec<MineMod>> { - let mut mods = vec![]; - for container in scan(&self.path, |p| open_mod_or_pack(p))? { - mods.extend(container.mods()?); - } - Ok(mods) - } - - /// Copies the modpack to the given path. - /// - /// Note that the path should not include the modpack directory, that will be appended - /// automatically. - /// - /// Returns a new [`Modpack`] object pointing to the copy. - pub fn copy_to<P: AsRef<Path>>(&self, path: P) -> Result<Modpack> { - let path = path.as_ref().join(self.name()?); - fs::create_dir_all(&path)?; - util::copy_recursive(&self.path, &path)?; - Modpack::open(&path) - } -} - -impl fmt::Display for Modpack { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{} (pack)", self.name().map_err(|_| fmt::Error)?) - } -} - -/// A thing that can contain mods. -/// -/// This is useful for code that should deal with both mods and modpacks. -pub trait ModContainer: Any + fmt::Display { - /// Returns the name of the mod container. - fn name(&self) -> Result<String>; - - /// Returns the on-disk path of this mod container. - fn path(&self) -> &Path; - - /// Return all contained mods. - fn mods(&self) -> Result<Vec<MineMod>>; - - /// Copies the content to the given directory. - fn install_to(&self, path: &Path) -> Result<Box<dyn ModContainer>>; -} - -impl ModContainer for MineMod { - fn name(&self) -> Result<String> { - self.mod_id() - } - - fn path(&self) -> &Path { - self.path() - } - - fn mods(&self) -> Result<Vec<MineMod>> { - Ok(vec![self.clone()]) - } - - fn install_to(&self, path: &Path) -> Result<Box<dyn ModContainer>> { - self.copy_to(path) - .map(|x| Box::new(x) as Box<dyn ModContainer>) - } -} - -impl ModContainer for Modpack { - fn name(&self) -> Result<String> { - self.name() - } - - fn path(&self) -> &Path { - self.path() - } - - fn mods(&self) -> Result<Vec<MineMod>> { - self.mods() - } - - fn install_to(&self, path: &Path) -> Result<Box<dyn ModContainer>> { - self.copy_to(path) - .map(|x| Box::new(x) as Box<dyn ModContainer>) - } -} - -/// Attempts to open the given path as either a single mod or a modpack. -pub fn open_mod_or_pack<P: AsRef<Path>>(path: P) -> Result<Box<dyn ModContainer>> { - MineMod::open(path.as_ref()) - .map(|m| Box::new(m) as Box<dyn ModContainer>) - .or_else(|_| Modpack::open(path.as_ref()).map(|p| Box::new(p) as Box<dyn ModContainer>)) -} |