Added an add command

This commit is contained in:
Warren Hood 2024-08-14 23:32:45 +02:00
parent 52efc9780d
commit 077fadabbf
2 changed files with 99 additions and 13 deletions

View file

@ -14,7 +14,7 @@ struct Cli {
#[derive(Subcommand)] #[derive(Subcommand)]
enum Commands { enum Commands {
/// Initialises a new mcmpmgr project in the specified directory (or current dir if not specified) /// Initialise a new mcmpmgr project in the specified directory (or current dir if not specified)
Init { Init {
/// The root modpack project directory /// The root modpack project directory
directory: Option<PathBuf>, directory: Option<PathBuf>,
@ -28,7 +28,7 @@ enum Commands {
#[arg(long, default_value_t = modpack::ModLoader::Fabric)] #[arg(long, default_value_t = modpack::ModLoader::Fabric)]
modloader: modpack::ModLoader, modloader: modpack::ModLoader,
}, },
/// Creates and initialises a new mcmpmgr project in the current directory /// Create and initialise a new mcmpmgr project in the current directory
New { New {
/// Name of the new modpack project /// Name of the new modpack project
name: String, name: String,
@ -39,6 +39,17 @@ enum Commands {
#[arg(long, default_value_t = modpack::ModLoader::Fabric)] #[arg(long, default_value_t = modpack::ModLoader::Fabric)]
modloader: modpack::ModLoader, modloader: modpack::ModLoader,
}, },
/// Add a new mod to the modpack
Add {
/// Name of the mod to add to the project, optionally including a version
name: String,
/// Providers to download the mods from
#[arg(long)]
providers: Vec<ModProvider>,
/// URL to download the mod from
#[arg(long)]
url: Option<String>
}
} }
fn main() -> Result<(), Box<dyn Error>> { fn main() -> Result<(), Box<dyn Error>> {
@ -88,6 +99,19 @@ fn main() -> Result<(), Box<dyn Error>> {
let mc_modpack_meta: ModpackMeta = ModpackMeta::new(&name, &mc_version, modloader); let mc_modpack_meta: ModpackMeta = ModpackMeta::new(&name, &mc_version, modloader);
mc_modpack_meta.init_project(&dir)?; mc_modpack_meta.init_project(&dir)?;
} }
Commands::Add { name, providers, url } => {
let mut modpack_meta = ModpackMeta::load_from_current_directory()?;
let mut mod_meta = ModMeta::new(&name)?;
if let Some(url) = url {
mod_meta = mod_meta.url(&url);
}
for provider in providers.into_iter() {
mod_meta = mod_meta.provider(provider);
}
modpack_meta = modpack_meta.add_mod(mod_meta);
modpack_meta.save_current_dir_project()?;
},
} }
}; };

View file

@ -2,7 +2,9 @@ use std::{borrow::BorrowMut, error::Error, path::PathBuf};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, PartialEq)] const MODPACK_FILENAME: &str = "modpack.toml";
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
pub enum ModProvider { pub enum ModProvider {
/// Get mods from CurseForge /// Get mods from CurseForge
CurseForge, CurseForge,
@ -12,6 +14,19 @@ pub enum ModProvider {
Raw, Raw,
} }
impl std::str::FromStr for ModProvider {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"curseforge" => Ok(ModProvider::CurseForge),
"modrinth" => Ok(ModProvider::Modrinth),
"raw" => Ok(ModProvider::Raw),
_ => Err(format!("Invalid mod launcher: {}", s)),
}
}
}
#[derive(Debug, Serialize, Deserialize, PartialEq)] #[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct ModMeta { pub struct ModMeta {
mod_name: String, mod_name: String,
@ -29,11 +44,22 @@ struct ModMetaBuilder {
} }
impl ModMeta { impl ModMeta {
pub fn new(mod_name: &str) -> Self { pub fn new(mod_name: &str) -> Result<Self, Box<dyn Error>> {
Self { if mod_name.contains("@") {
let mod_name_and_version: Vec<&str> = mod_name.split("@").collect();
if mod_name_and_version.len() != 2 {
return Err(format!("Invalid mod with version constraint: '{}'", &mod_name).into());
}
return Ok(Self {
mod_name: mod_name_and_version[0].into(),
version: mod_name_and_version[1].into(),
..Default::default()
});
}
Ok(Self {
mod_name: mod_name.into(), mod_name: mod_name.into(),
..Default::default() ..Default::default()
} })
} }
pub fn provider(mut self, provider: ModProvider) -> Self { pub fn provider(mut self, provider: ModProvider) -> Self {
@ -116,6 +142,23 @@ impl ModpackMeta {
} }
} }
pub fn load_from_directory(directory: &PathBuf) -> Result<Self, Box<dyn Error>> {
let modpack_meta_file_path = directory.clone().join(PathBuf::from(MODPACK_FILENAME));
if !modpack_meta_file_path.exists() {
return Err(format!(
"Directory '{}' does not seem to be a valid modpack project directory.",
directory.display()
)
.into());
};
let modpack_contents = std::fs::read_to_string(modpack_meta_file_path)?;
Ok(toml::from_str(&modpack_contents)?)
}
pub fn load_from_current_directory() -> Result<Self, Box<dyn Error>> {
Self::load_from_directory(&std::env::current_dir()?)
}
pub fn provider(mut self, provider: ModProvider) -> Self { pub fn provider(mut self, provider: ModProvider) -> Self {
if !self.default_providers.contains(&provider) { if !self.default_providers.contains(&provider) {
self.default_providers.push(provider); self.default_providers.push(provider);
@ -125,13 +168,22 @@ impl ModpackMeta {
pub fn add_mod(mut self, mod_meta: ModMeta) -> Self { pub fn add_mod(mut self, mod_meta: ModMeta) -> Self {
if !self.mods.contains(&mod_meta) { if !self.mods.contains(&mod_meta) {
self.mods = self
.mods
.into_iter()
.filter(|m| m.mod_name != mod_meta.mod_name)
.collect();
println!(
"Adding {}@{} to modpack '{}'...",
mod_meta.mod_name, mod_meta.version, self.pack_name
);
self.mods.push(mod_meta); self.mods.push(mod_meta);
} }
self self
} }
pub fn init_project(&self, directory: &PathBuf) -> Result<(), Box<dyn Error>> { pub fn init_project(&self, directory: &PathBuf) -> Result<(), Box<dyn Error>> {
let modpack_meta_file_path = directory.clone().join(PathBuf::from("mcmodpack.toml")); let modpack_meta_file_path = directory.clone().join(PathBuf::from(MODPACK_FILENAME));
if modpack_meta_file_path.exists() { if modpack_meta_file_path.exists() {
return Err(format!( return Err(format!(
"mcmodpack.toml already exists at {}", "mcmodpack.toml already exists at {}",
@ -140,15 +192,25 @@ impl ModpackMeta {
.into()); .into());
} }
std::fs::write( self.save_to_file(&modpack_meta_file_path)?;
modpack_meta_file_path,
toml::to_string(self)
.expect("MC Modpack Meta should be serializable"),
)?;
println!("MC modpack project initialized at {}", directory.display()); println!("MC modpack project initialized at {}", directory.display());
Ok(()) Ok(())
} }
pub fn save_to_file(&self, path: &PathBuf) -> Result<(), Box<dyn Error>> {
std::fs::write(
path,
toml::to_string(self).expect("MC Modpack Meta should be serializable"),
)?;
println!("Saved modpack metadata to {}", path.display());
Ok(())
}
pub fn save_current_dir_project(&self) -> Result<(), Box<dyn Error>> {
let modpack_meta_file_path = std::env::current_dir()?.join(PathBuf::from(MODPACK_FILENAME));
self.save_to_file(&modpack_meta_file_path)?;
Ok(())
}
} }
impl std::default::Default for ModpackMeta { impl std::default::Default for ModpackMeta {