Compare commits
6 Commits
5ad70e0a27
...
main
Author | SHA1 | Date | |
---|---|---|---|
dac3092502 | |||
846b3d0edf | |||
56f659a182 | |||
6f6c78d3ff | |||
d7c50ec8be | |||
20dddba0d5 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
/target
|
/target
|
||||||
|
.packium.toml
|
||||||
|
95
Cargo.lock
generated
95
Cargo.lock
generated
@@ -4,9 +4,9 @@ version = 3
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "addr2line"
|
name = "addr2line"
|
||||||
version = "0.21.0"
|
version = "0.22.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
|
checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gimli",
|
"gimli",
|
||||||
]
|
]
|
||||||
@@ -66,6 +66,12 @@ dependencies = [
|
|||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anyhow"
|
||||||
|
version = "1.0.86"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "atomic-waker"
|
name = "atomic-waker"
|
||||||
version = "1.1.2"
|
version = "1.1.2"
|
||||||
@@ -80,9 +86,9 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "backtrace"
|
name = "backtrace"
|
||||||
version = "0.3.71"
|
version = "0.3.72"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d"
|
checksum = "17c6a35df3749d2e8bb1b7b21a976d82b15548788d2735b9d82f329268f71a11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"addr2line",
|
"addr2line",
|
||||||
"cc",
|
"cc",
|
||||||
@@ -169,7 +175,7 @@ version = "4.5.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64"
|
checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck",
|
"heck 0.5.0",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn",
|
||||||
@@ -197,6 +203,18 @@ dependencies = [
|
|||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "comfy-table"
|
||||||
|
version = "7.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b34115915337defe99b2aff5c2ce6771e5fbc4079f4b506301f5cf394c8452f7"
|
||||||
|
dependencies = [
|
||||||
|
"crossterm 0.27.0",
|
||||||
|
"strum",
|
||||||
|
"strum_macros",
|
||||||
|
"unicode-width",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core-foundation"
|
name = "core-foundation"
|
||||||
version = "0.9.4"
|
version = "0.9.4"
|
||||||
@@ -229,6 +247,19 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossterm"
|
||||||
|
version = "0.27.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.5.0",
|
||||||
|
"crossterm_winapi",
|
||||||
|
"libc",
|
||||||
|
"parking_lot",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossterm_winapi"
|
name = "crossterm_winapi"
|
||||||
version = "0.9.1"
|
version = "0.9.1"
|
||||||
@@ -364,9 +395,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gimli"
|
name = "gimli"
|
||||||
version = "0.28.1"
|
version = "0.29.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
|
checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "h2"
|
name = "h2"
|
||||||
@@ -393,6 +424,12 @@ version = "0.14.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heck"
|
name = "heck"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
@@ -483,9 +520,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper-util"
|
name = "hyper-util"
|
||||||
version = "0.1.4"
|
version = "0.1.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3d8d52be92d09acc2e01dddb7fde3ad983fc6489c7db4837e605bc3fca4cb63e"
|
checksum = "7b875924a60b96e5d7b9ae7b066540b1dd1cbd90d1828f54c92e02a283351c56"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
@@ -528,7 +565,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "0fddf93031af70e75410a2511ec04d49e758ed2f26dad3404a934e0fb45cc12a"
|
checksum = "0fddf93031af70e75410a2511ec04d49e758ed2f26dad3404a934e0fb45cc12a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.5.0",
|
"bitflags 2.5.0",
|
||||||
"crossterm",
|
"crossterm 0.25.0",
|
||||||
"dyn-clone",
|
"dyn-clone",
|
||||||
"fuzzy-matcher",
|
"fuzzy-matcher",
|
||||||
"fxhash",
|
"fxhash",
|
||||||
@@ -670,9 +707,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "object"
|
name = "object"
|
||||||
version = "0.32.2"
|
version = "0.35.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
|
checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
@@ -731,13 +768,17 @@ dependencies = [
|
|||||||
name = "packium"
|
name = "packium"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
"colored",
|
"colored",
|
||||||
|
"comfy-table",
|
||||||
"inquire",
|
"inquire",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"tokio",
|
"tokio",
|
||||||
"toml",
|
"toml",
|
||||||
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -911,6 +952,12 @@ version = "1.7.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d"
|
checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustversion"
|
||||||
|
version = "1.0.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.18"
|
version = "1.0.18"
|
||||||
@@ -1068,6 +1115,25 @@ version = "0.11.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strum"
|
||||||
|
version = "0.26.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strum_macros"
|
||||||
|
version = "0.26.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946"
|
||||||
|
dependencies = [
|
||||||
|
"heck 0.4.1",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"rustversion",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.66"
|
version = "2.0.66"
|
||||||
@@ -1324,6 +1390,7 @@ dependencies = [
|
|||||||
"form_urlencoded",
|
"form_urlencoded",
|
||||||
"idna",
|
"idna",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1592,9 +1659,9 @@ checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winnow"
|
name = "winnow"
|
||||||
version = "0.6.8"
|
version = "0.6.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d"
|
checksum = "86c949fede1d13936a99f14fafd3e76fd642b556dd2ce96287fbe2e0151bfac6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
@@ -8,6 +8,10 @@ clap = { version = "4.5.4", features = [ "derive" ]}
|
|||||||
toml = "0.8.13"
|
toml = "0.8.13"
|
||||||
inquire = "0.7.5"
|
inquire = "0.7.5"
|
||||||
serde = { version = "1.0.203", features = [ "derive" ]}
|
serde = { version = "1.0.203", features = [ "derive" ]}
|
||||||
|
serde_json = "1.0.117"
|
||||||
colored = "2.1.0"
|
colored = "2.1.0"
|
||||||
reqwest = { version = "0.12.4", features = [ "json" ]}
|
reqwest = { version = "0.12.4", features = [ "json" ]}
|
||||||
tokio = { version = "1.37.0", features = [ "full" ] }
|
tokio = { version = "1.37.0", features = [ "full" ] }
|
||||||
|
comfy-table = "7.1.1"
|
||||||
|
url = { version = "2.5.0", features = [ "serde" ] }
|
||||||
|
anyhow = "1.0.86"
|
||||||
|
14
src/cli.rs
14
src/cli.rs
@@ -20,8 +20,14 @@ pub enum Commands {
|
|||||||
Info,
|
Info,
|
||||||
|
|
||||||
/// Add a mod to the modpack
|
/// Add a mod to the modpack
|
||||||
Add {
|
Add,
|
||||||
/// The name of the mod to add
|
|
||||||
name: String,
|
/// Remove a mod from the modpack
|
||||||
},
|
Remove,
|
||||||
|
|
||||||
|
/// Update a mod or all mods
|
||||||
|
Update,
|
||||||
|
|
||||||
|
/// Migrate to a other Minecraft version
|
||||||
|
Migrate,
|
||||||
}
|
}
|
||||||
|
24
src/commands/add.rs
Normal file
24
src/commands/add.rs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
use crate::config;
|
||||||
|
use crate::modrinth::project::get_project_versions;
|
||||||
|
use crate::modrinth::search::search;
|
||||||
|
use inquire::{Select, Text};
|
||||||
|
|
||||||
|
pub async fn add() {
|
||||||
|
let config = config::Config::get().unwrap();
|
||||||
|
|
||||||
|
let query = Text::new("Search").prompt().unwrap();
|
||||||
|
|
||||||
|
let response = search(&query, &config.game_version, &config.loader)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let project = Select::new("Select a mod", response.hits).prompt().unwrap();
|
||||||
|
|
||||||
|
let project_versions = get_project_versions(&project.project_id, &config.game_version)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let project_version = project_versions.get(0).unwrap();
|
||||||
|
|
||||||
|
config::Config::add(project.project_id, project_version.id.to_owned()).unwrap();
|
||||||
|
}
|
33
src/commands/init.rs
Normal file
33
src/commands/init.rs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
use crate::config;
|
||||||
|
use crate::modrinth::tags;
|
||||||
|
use inquire::Select;
|
||||||
|
|
||||||
|
pub async fn init(snaphots: bool) {
|
||||||
|
let mut game_versions = tags::get_minecraft_versions().await.unwrap();
|
||||||
|
|
||||||
|
if snaphots {
|
||||||
|
game_versions = game_versions
|
||||||
|
.into_iter()
|
||||||
|
.filter(|v| (&v.version_type == "snapshot" || &v.version_type == "release"))
|
||||||
|
.collect()
|
||||||
|
} else {
|
||||||
|
game_versions = game_versions
|
||||||
|
.into_iter()
|
||||||
|
.filter(|v| &v.version_type == "release")
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
let options = game_versions.iter().map(|v| &v.version).collect();
|
||||||
|
|
||||||
|
let game_version = Select::new("Select a Minecraft version", options)
|
||||||
|
.prompt()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let options = vec!["fabric", "quilt", "forge", "neoforge"];
|
||||||
|
|
||||||
|
let loader = Select::new("Select a mod loader", options)
|
||||||
|
.prompt()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
config::Config::init(game_version.to_string(), loader.to_string()).unwrap();
|
||||||
|
}
|
27
src/commands/list.rs
Normal file
27
src/commands/list.rs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
use crate::{config, pack::Modpack};
|
||||||
|
use colored::*;
|
||||||
|
use comfy_table::{presets::NOTHING, Table};
|
||||||
|
|
||||||
|
pub async fn list() {
|
||||||
|
let config = config::Config::get().unwrap();
|
||||||
|
|
||||||
|
let modpack = Modpack::from_config(&config).await.unwrap();
|
||||||
|
|
||||||
|
let rows: Vec<Vec<ColoredString>> = modpack
|
||||||
|
.mods
|
||||||
|
.values()
|
||||||
|
.into_iter()
|
||||||
|
.map(|p| {
|
||||||
|
vec![
|
||||||
|
p.title.bold(),
|
||||||
|
p.project_id.dimmed(),
|
||||||
|
p.version.as_ref().unwrap().version_number.normal(),
|
||||||
|
]
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut table = Table::new();
|
||||||
|
table.load_preset(NOTHING).add_rows(rows);
|
||||||
|
|
||||||
|
println!("{table}");
|
||||||
|
}
|
5
src/commands/mod.rs
Normal file
5
src/commands/mod.rs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
pub mod add;
|
||||||
|
pub mod init;
|
||||||
|
pub mod list;
|
||||||
|
pub mod remove;
|
||||||
|
pub mod update;
|
17
src/commands/remove.rs
Normal file
17
src/commands/remove.rs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
use crate::{config, pack::Modpack};
|
||||||
|
use colored::*;
|
||||||
|
use inquire::Select;
|
||||||
|
|
||||||
|
pub async fn remove() {
|
||||||
|
let mut config = config::Config::get().unwrap();
|
||||||
|
|
||||||
|
let modpack = Modpack::from_config(&config).await.unwrap();
|
||||||
|
|
||||||
|
let project = Select::new("Select a mod to remove", modpack.mods.values().collect())
|
||||||
|
.prompt()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
config.projects.remove(&project.project_id);
|
||||||
|
|
||||||
|
config.save().unwrap();
|
||||||
|
}
|
67
src/commands/update.rs
Normal file
67
src/commands/update.rs
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
use crate::{
|
||||||
|
config,
|
||||||
|
modrinth::{project::get_project_versions, versions::ProjectVersion},
|
||||||
|
pack::{Mod, Modpack},
|
||||||
|
};
|
||||||
|
use colored::*;
|
||||||
|
use inquire::MultiSelect;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
struct Diff {
|
||||||
|
r#mod: Mod,
|
||||||
|
new_version: ProjectVersion,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Diff {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{} {} -> {}",
|
||||||
|
self.r#mod.title.bold(),
|
||||||
|
self.r#mod.version.as_ref().unwrap().version_number.red(),
|
||||||
|
self.new_version.version_number.green()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn update() {
|
||||||
|
let mut config = config::Config::get().unwrap();
|
||||||
|
let modpack = Modpack::from_config(&config).await.unwrap();
|
||||||
|
|
||||||
|
let mut diffs: Vec<Diff> = vec![];
|
||||||
|
|
||||||
|
// Find the newest compatible version of the mod
|
||||||
|
for (project_id, r#mod) in modpack.mods {
|
||||||
|
let project_versions = get_project_versions(&project_id, &config.game_version)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let new_version = project_versions.get(0).unwrap();
|
||||||
|
|
||||||
|
if r#mod.version.as_ref().unwrap().version_number == new_version.version_number {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
diffs.push(Diff {
|
||||||
|
r#mod,
|
||||||
|
new_version: new_version.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if diffs.len() == 0 {
|
||||||
|
return println!("There are no mods left to update :)");
|
||||||
|
}
|
||||||
|
|
||||||
|
let updated_diffs = MultiSelect::new("Select mods to update", diffs)
|
||||||
|
.prompt()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Bumb the new versions to the config
|
||||||
|
for diff in updated_diffs {
|
||||||
|
config
|
||||||
|
.projects
|
||||||
|
.insert(diff.r#mod.project_id, diff.new_version.id.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
config.save().unwrap();
|
||||||
|
}
|
50
src/config.rs
Normal file
50
src/config.rs
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
use anyhow::Result;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::fs;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct Config {
|
||||||
|
pub game_version: String,
|
||||||
|
pub loader: String,
|
||||||
|
pub projects: HashMap<String, String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
pub fn get() -> Result<Self> {
|
||||||
|
let config_raw = fs::read_to_string(".packium.toml")?;
|
||||||
|
|
||||||
|
let config = toml::from_str(&config_raw)?;
|
||||||
|
|
||||||
|
Ok(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn save(&self) -> Result<()> {
|
||||||
|
let config_toml = toml::to_string_pretty(&self)?;
|
||||||
|
|
||||||
|
fs::write(".packium.toml", &config_toml)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(game_version: String, loader: String) -> Result<Self> {
|
||||||
|
let config = Self {
|
||||||
|
game_version,
|
||||||
|
loader,
|
||||||
|
projects: HashMap::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
config.save()?;
|
||||||
|
|
||||||
|
Ok(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add(project_id: String, version_id: String) -> Result<Self> {
|
||||||
|
let mut config = Self::get()?;
|
||||||
|
|
||||||
|
config.projects.insert(project_id, version_id);
|
||||||
|
config.save()?;
|
||||||
|
|
||||||
|
Ok(config)
|
||||||
|
}
|
||||||
|
}
|
16
src/main.rs
16
src/main.rs
@@ -1,4 +1,6 @@
|
|||||||
mod cli;
|
mod cli;
|
||||||
|
mod commands;
|
||||||
|
mod config;
|
||||||
mod modrinth;
|
mod modrinth;
|
||||||
mod pack;
|
mod pack;
|
||||||
|
|
||||||
@@ -10,7 +12,19 @@ async fn main() {
|
|||||||
|
|
||||||
match &args.command {
|
match &args.command {
|
||||||
cli::Commands::Init => {
|
cli::Commands::Init => {
|
||||||
pack::Pack::init().await;
|
commands::init::init(false).await;
|
||||||
|
}
|
||||||
|
cli::Commands::Add => {
|
||||||
|
commands::add::add().await;
|
||||||
|
}
|
||||||
|
cli::Commands::Remove => {
|
||||||
|
commands::remove::remove().await;
|
||||||
|
}
|
||||||
|
cli::Commands::List => {
|
||||||
|
commands::list::list().await;
|
||||||
|
}
|
||||||
|
cli::Commands::Update => {
|
||||||
|
commands::update::update().await;
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
};
|
};
|
||||||
|
@@ -1,16 +0,0 @@
|
|||||||
use serde::Deserialize;
|
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
|
||||||
pub struct MinecraftVersion {
|
|
||||||
pub version: String,
|
|
||||||
pub version_type: String,
|
|
||||||
pub date: String,
|
|
||||||
pub major: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn get_minecraft_versions() -> Result<Vec<MinecraftVersion>, reqwest::Error> {
|
|
||||||
reqwest::get("https://api.modrinth.com/v2/tag/game_version")
|
|
||||||
.await?
|
|
||||||
.json::<Vec<MinecraftVersion>>()
|
|
||||||
.await
|
|
||||||
}
|
|
6
src/modrinth/mod.rs
Normal file
6
src/modrinth/mod.rs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
pub mod project;
|
||||||
|
pub mod search;
|
||||||
|
pub mod tags;
|
||||||
|
pub mod versions;
|
||||||
|
|
||||||
|
pub const API_BASE_URL: &str = "https://api.modrinth.com/v2";
|
51
src/modrinth/project.rs
Normal file
51
src/modrinth/project.rs
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
use super::versions::ProjectVersion;
|
||||||
|
use super::API_BASE_URL;
|
||||||
|
use anyhow::{bail, Result};
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
type ProjectVersionResponse = Vec<ProjectVersion>;
|
||||||
|
|
||||||
|
pub async fn get_project_versions(
|
||||||
|
project_id: &str,
|
||||||
|
game_version: &str,
|
||||||
|
) -> Result<ProjectVersionResponse> {
|
||||||
|
let url = format!(
|
||||||
|
"{API_BASE_URL}/project/{}/version?game_versions=[\"{}\"]",
|
||||||
|
project_id, game_version
|
||||||
|
);
|
||||||
|
|
||||||
|
let response = reqwest::get(url)
|
||||||
|
.await?
|
||||||
|
.json::<ProjectVersionResponse>()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
match response {
|
||||||
|
Ok(r) => Ok(r),
|
||||||
|
Err(err) => bail!("Error with Modrinth API: {}", err.to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProjectResponse = Vec<Project>;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct Project {
|
||||||
|
slug: String,
|
||||||
|
pub title: String,
|
||||||
|
pub description: String,
|
||||||
|
pub downloads: i64,
|
||||||
|
pub id: String,
|
||||||
|
pub followers: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_multiple_projects(version_ids: Vec<&String>) -> Result<ProjectResponse> {
|
||||||
|
let version_ids_json = serde_json::to_string(&version_ids)?;
|
||||||
|
|
||||||
|
let url = format!("{API_BASE_URL}/projects?ids={}", version_ids_json);
|
||||||
|
|
||||||
|
let response = reqwest::get(url).await?.json::<ProjectResponse>().await;
|
||||||
|
|
||||||
|
match response {
|
||||||
|
Ok(r) => Ok(r),
|
||||||
|
Err(err) => bail!("Error with Modrinth API: {}", err.to_string()),
|
||||||
|
}
|
||||||
|
}
|
70
src/modrinth/search.rs
Normal file
70
src/modrinth/search.rs
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
use super::API_BASE_URL;
|
||||||
|
use anyhow::{bail, Result};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct Response {
|
||||||
|
pub hits: Vec<SearchHit>,
|
||||||
|
offset: i64,
|
||||||
|
limit: i64,
|
||||||
|
total_hits: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct SearchHit {
|
||||||
|
pub slug: String,
|
||||||
|
pub title: String,
|
||||||
|
pub description: String,
|
||||||
|
pub client_side: String,
|
||||||
|
pub server_side: String,
|
||||||
|
pub project_type: String,
|
||||||
|
pub downloads: i64,
|
||||||
|
pub project_id: String,
|
||||||
|
pub author: String,
|
||||||
|
pub versions: Vec<String>,
|
||||||
|
pub follows: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for SearchHit {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{}", self.title)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn search(query: &str, game_version: &str, loader: &str) -> Result<Response> {
|
||||||
|
let game_version_facet = format!("versions:{game_version}");
|
||||||
|
let loader_facet = format!("categories:{loader}");
|
||||||
|
|
||||||
|
let facets: Vec<Vec<&str>> = vec![
|
||||||
|
vec!["project_type:mod"],
|
||||||
|
vec![&game_version_facet],
|
||||||
|
vec![&loader_facet],
|
||||||
|
];
|
||||||
|
|
||||||
|
let facets_json = serde_json::to_string(&facets)?;
|
||||||
|
|
||||||
|
let url = format!(
|
||||||
|
"{API_BASE_URL}/search?query={}&facets={}",
|
||||||
|
query, facets_json
|
||||||
|
);
|
||||||
|
|
||||||
|
let response = reqwest::get(url).await?.json::<Response>().await;
|
||||||
|
|
||||||
|
match response {
|
||||||
|
Ok(r) => Ok(r),
|
||||||
|
Err(err) => bail!("Error with Modrinth API: {}", err.to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_search() {
|
||||||
|
let response = search("Fabric API", "1.20.4", "fabric").await;
|
||||||
|
|
||||||
|
assert!(response.is_ok())
|
||||||
|
}
|
||||||
|
}
|
36
src/modrinth/tags.rs
Normal file
36
src/modrinth/tags.rs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
use super::API_BASE_URL;
|
||||||
|
use anyhow::{bail, Result};
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
type Response = Vec<MinecraftVersion>;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct MinecraftVersion {
|
||||||
|
pub version: String,
|
||||||
|
pub version_type: String,
|
||||||
|
date: String,
|
||||||
|
major: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_minecraft_versions() -> Result<Response> {
|
||||||
|
let url = format!("{API_BASE_URL}/tag/game_version");
|
||||||
|
|
||||||
|
let response = reqwest::get(url).await?.json::<Response>().await;
|
||||||
|
|
||||||
|
match response {
|
||||||
|
Ok(r) => Ok(r),
|
||||||
|
Err(err) => bail!("Error with Modrinth API: {}", err.to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_game_versions() {
|
||||||
|
let response = get_minecraft_versions().await;
|
||||||
|
|
||||||
|
assert!(response.is_ok())
|
||||||
|
}
|
||||||
|
}
|
31
src/modrinth/versions.rs
Normal file
31
src/modrinth/versions.rs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
use super::API_BASE_URL;
|
||||||
|
use anyhow::{bail, Result};
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
type Response = Vec<ProjectVersion>;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
pub struct ProjectVersion {
|
||||||
|
pub name: String,
|
||||||
|
pub version_number: String,
|
||||||
|
pub game_versions: Vec<String>,
|
||||||
|
pub version_type: String,
|
||||||
|
pub loaders: Vec<String>,
|
||||||
|
pub featured: bool,
|
||||||
|
pub id: String,
|
||||||
|
pub project_id: String,
|
||||||
|
pub downloads: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_multiple_versions(version_ids: Vec<&String>) -> Result<Response> {
|
||||||
|
let version_ids_json = serde_json::to_string(&version_ids)?;
|
||||||
|
|
||||||
|
let url = format!("{API_BASE_URL}/versions?ids={}", version_ids_json);
|
||||||
|
|
||||||
|
let response = reqwest::get(url).await?.json::<Response>().await;
|
||||||
|
|
||||||
|
match response {
|
||||||
|
Ok(r) => Ok(r),
|
||||||
|
Err(err) => bail!("Error with Modrinth API: {}", err.to_string()),
|
||||||
|
}
|
||||||
|
}
|
97
src/pack.rs
97
src/pack.rs
@@ -1,59 +1,68 @@
|
|||||||
use crate::modrinth;
|
use crate::{
|
||||||
use colored::*;
|
config,
|
||||||
use inquire::Select;
|
modrinth::{
|
||||||
use serde::Serialize;
|
project::{get_multiple_projects, Project},
|
||||||
use std::fmt::{Display, Formatter};
|
versions::{get_multiple_versions, ProjectVersion},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use anyhow::Result;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
#[derive(Serialize)]
|
pub struct Modpack {
|
||||||
pub struct Pack {
|
pub game_version: String,
|
||||||
info: Info,
|
pub loader: String,
|
||||||
|
pub mods: HashMap<String, Mod>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
pub struct Mod {
|
||||||
pub struct Info {
|
pub project_id: String,
|
||||||
loader: Modloader,
|
pub title: String,
|
||||||
loader_version: String,
|
pub description: String,
|
||||||
minecraft_version: String,
|
pub version: Option<ProjectVersion>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Debug, Clone, Copy)]
|
impl Mod {
|
||||||
#[serde(untagged)]
|
fn from_project(project: Project) -> Self {
|
||||||
pub enum Modloader {
|
Self {
|
||||||
Fabric,
|
project_id: project.id,
|
||||||
Quilt,
|
title: project.title,
|
||||||
Forge,
|
description: project.description,
|
||||||
Neoforge,
|
version: None,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Modloader {
|
|
||||||
const VARIANTS: &'static [Modloader] =
|
|
||||||
&[Self::Fabric, Self::Quilt, Self::Forge, Self::Neoforge];
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Modloader {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
|
|
||||||
write!(f, "{self:?}")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pack {
|
impl fmt::Display for Mod {
|
||||||
pub async fn init() {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
println!("Fetching Minecraft information");
|
write!(f, "{}", self.title)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let versions = modrinth::get_minecraft_versions().await.unwrap();
|
impl Modpack {
|
||||||
|
pub async fn from_config(config: &config::Config) -> Result<Self> {
|
||||||
|
let projects = get_multiple_projects(config.projects.keys().collect())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let versions = get_multiple_versions(config.projects.values().collect())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let versions = versions
|
let mut projects_map: HashMap<String, Mod> = projects
|
||||||
.iter()
|
.into_iter()
|
||||||
.filter(|x| x.version_type == "release".to_string())
|
.map(|p| (p.id.clone(), Mod::from_project(p)))
|
||||||
.map(|x| x.version.clone())
|
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let modloader = Select::new("Modloader?", Modloader::VARIANTS.to_vec())
|
for version in versions {
|
||||||
.prompt()
|
if let Some(p) = projects_map.get_mut(&version.project_id) {
|
||||||
.unwrap();
|
p.version = Some(version)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let version = Select::new("Minecraft version?", versions)
|
Ok(Modpack {
|
||||||
.prompt()
|
game_version: config.game_version.clone(),
|
||||||
.unwrap();
|
loader: config.loader.clone(),
|
||||||
|
mods: projects_map,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user