diff --git a/Cargo.lock b/Cargo.lock index 634a26e..fbee9d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -85,6 +85,15 @@ version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + [[package]] name = "bumpalo" version = "3.13.0" @@ -181,6 +190,15 @@ dependencies = [ "syn 2.0.27", ] +[[package]] +name = "cpufeatures" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" +dependencies = [ + "libc", +] + [[package]] name = "crossterm" version = "0.25.0" @@ -206,6 +224,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + [[package]] name = "cstr" version = "0.2.11" @@ -216,6 +244,16 @@ dependencies = [ "quote", ] +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + [[package]] name = "encoding_rs" version = "0.8.32" @@ -267,7 +305,7 @@ checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" [[package]] name = "flowupdater-json-creator" -version = "1.4.0" +version = "1.5.0" dependencies = [ "fujc-api 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "openssl", @@ -275,9 +313,12 @@ dependencies = [ "reqwest", "serde", "serde_json", + "sha1", + "sha1_smol", "thiserror", "time", "tokio", + "walkdir", ] [[package]] @@ -375,6 +416,16 @@ dependencies = [ "pin-utils", ] +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "getrandom" version = "0.2.10" @@ -574,9 +625,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.147" +version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" [[package]] name = "linux-raw-sys" @@ -968,6 +1019,15 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "schannel" version = "0.1.22" @@ -1055,6 +1115,23 @@ dependencies = [ "serde", ] +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha1_smol" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" + [[package]] name = "shell-words" version = "1.1.0" @@ -1324,6 +1401,12 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + [[package]] name = "unicode-bidi" version = "0.3.13" @@ -1396,6 +1479,16 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "walkdir" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "want" version = "0.3.1" diff --git a/projects/cli/Cargo.toml b/projects/cli/Cargo.toml index 43ec8b8..ac5bbbf 100644 --- a/projects/cli/Cargo.toml +++ b/projects/cli/Cargo.toml @@ -3,7 +3,7 @@ name = "flowupdater-json-creator" description = "Create JSON for flow-updater in a TUI" authors = ["Zuygui", "Bricklou"] license = "MIT" -version = "1.4.0" +version = "1.5.0" edition = "2021" readme = "README.md" homepage = "https://github.com/zuygui/flowupdater-json-creator" @@ -34,7 +34,9 @@ thiserror = { version = "1" } time = { version = "0.3", features = ["serde", "serde-well-known"] } openssl = { version = "0.10", features = ["vendored"] } fujc-api = "1.1.1" - +sha1 = "0.10.6" +walkdir = "2" +sha1_smol = "1.0" [profile.release] codegen-units = 1 strip = true diff --git a/projects/cli/src/json_creator.rs b/projects/cli/src/json_creator.rs index 64dbaea..dfb2436 100644 --- a/projects/cli/src/json_creator.rs +++ b/projects/cli/src/json_creator.rs @@ -1,6 +1,8 @@ use std::io::Write; use fujc::curse_api::mods::CurseMod; +use crate::questions::local_mods::LocalMod; + /** * * Structure of the output JSON file @@ -15,15 +17,24 @@ use fujc::curse_api::mods::CurseMod; * } */ -pub fn compile_mods_to_json(mod_list: Vec) { +pub fn compile_mods_to_json(curse_mod_list: Vec, local_mod_list: Vec) { let json = serde_json::json!({ - "curseFiles": mod_list.iter().map(|mod_| { + "curseFiles": curse_mod_list.iter().map(|mod_| { serde_json::json!({ "projectId": mod_.mod_id, "fileId": mod_.file_id }) - }).collect::>() - }); + }).collect::>(), + // A "mods" field empty for now + "mods": local_mod_list.iter().map(|mod_| { + serde_json::json!({ + "name": mod_.name, + "downloadUrl": mod_.download_url, + "sha1": mod_.sha1, + "size": mod_.size + }) + }).collect::>() //, + }); let mut file = std::fs::File::create("mods_list.json").unwrap(); file.write_all(json.to_string().as_bytes()).unwrap(); diff --git a/projects/cli/src/local_files_checker.rs b/projects/cli/src/local_files_checker.rs new file mode 100644 index 0000000..e69de29 diff --git a/projects/cli/src/main.rs b/projects/cli/src/main.rs index 8596dfe..eb694fb 100644 --- a/projects/cli/src/main.rs +++ b/projects/cli/src/main.rs @@ -6,6 +6,7 @@ mod constants; mod errors; mod questions; mod json_creator; +mod local_files_checker; #[tokio::main] async fn main() -> Result<(), Error> { @@ -15,9 +16,10 @@ async fn main() -> Result<(), Error> { questions.ask_modloader().await?; let mods = questions.ask_mods().await?; + let local_mods = questions.ask_local_mods()?; // compile mods to json - json_creator::compile_mods_to_json(mods); + json_creator::compile_mods_to_json(mods, local_mods); println!("――――――――――――――――――――――――――――――――――"); println!("Your mods list has been generated!"); diff --git a/projects/cli/src/questions/local_mods.rs b/projects/cli/src/questions/local_mods.rs new file mode 100644 index 0000000..8205d51 --- /dev/null +++ b/projects/cli/src/questions/local_mods.rs @@ -0,0 +1,123 @@ +use std::path::PathBuf; + +use serde::{Serialize, Deserialize}; + + +use crate::errors::Error; + +use super::Questions; + + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct LocalMod { + /// Name of the mod (ex: "jei-1.20.2.jar") + pub name: String, + /// The Download URL (serverUrl + name) + #[serde(rename = "downloadUrl")] + pub download_url: String, + /// The SHA1 of the mod + pub sha1: String, + /// The size of the mod + /// (ex: 123456) + pub size: u64, +} + +pub fn dir_to_local_mods(dir_path: PathBuf, server_url: String) -> Result, Error> { + let mut mods = Vec::new(); + + // Parse the directory to get all .jar files (**/*.jar) + let walker = walkdir::WalkDir::new(dir_path) + .into_iter() + .filter_map(|e| e.ok()) + .filter(|e| !e.file_type().is_dir()) + .filter(|e| e.path().extension().unwrap() == "jar"); + + for entry in walker { + let path = entry.path(); + let name = path.file_name().unwrap().to_str().unwrap().to_string(); + let file_content = std::fs::read(path).unwrap(); + + let mut hasher = sha1_smol::Sha1::new(); + + hasher.update(&file_content); + + let size = path.metadata().unwrap().len(); + let local_mod = LocalMod { + name: name.clone(), + download_url: format!("{}/{}", server_url, name), + sha1: hasher.digest().to_string(), + size, + }; + + mods.push(local_mod); + } + + + Ok(mods) +} + +impl Questions { + + /** + * Questions are + * - Do you want to add a local mods from a directory ? + * - If yes, what is the directory ? + * - If yes, what is the server url ? + */ + + fn check_if_user_wanna_add_local_mods(&self) -> bool { + let add_local_mods_question = requestty::Question::confirm("add_local_mods") + .message("Do you want to add local mods ?") + .build(); + + let binding = requestty::prompt_one(add_local_mods_question) + .unwrap() + .as_bool() + .unwrap(); + return binding; + } + + fn ask_local_mods_dir(&self) -> PathBuf { + let local_mods_dir_question = requestty::Question::input("local_mods_dir") + .message("What is the directory of the local mods ?") + .build(); + + let binding = requestty::prompt_one(local_mods_dir_question).unwrap(); + let local_mods_dir = &binding.as_string(); + PathBuf::from(local_mods_dir.unwrap()) + } + + fn ask_server_url(&self) -> String { + let server_url_question = requestty::Question::input("server_url") + .message("What is the server url ?") + .build(); + + let binding = requestty::prompt_one(server_url_question).unwrap(); + let server_url = &binding.as_string(); + server_url.unwrap().to_string() + } + + pub fn ask_local_mods(&self) -> Result, Error> { + + let mut local_mods: Vec = Vec::new(); + + let add_local_mods: bool = self.check_if_user_wanna_add_local_mods(); + + if add_local_mods { + let local_mods_dir: PathBuf = self.ask_local_mods_dir(); + let server_url: String = self.ask_server_url(); + + // If the URL ends with a slash, remove it + let server_url = if server_url.ends_with("/") { + server_url[..server_url.len() - 1].to_string() + } else { + server_url + }; + + local_mods = dir_to_local_mods(local_mods_dir, server_url)?; + } + + Ok(local_mods) + } + +} \ No newline at end of file diff --git a/projects/cli/src/questions/mod.rs b/projects/cli/src/questions/mod.rs index bf658df..6de76d3 100644 --- a/projects/cli/src/questions/mod.rs +++ b/projects/cli/src/questions/mod.rs @@ -6,6 +6,7 @@ use fujc::{curse_api::CurseApi, minecraft::modloader::ModLoaderType}; mod mc_versions; mod modloader; +pub mod local_mods; pub mod mods; pub struct Questions { diff --git a/projects/cli/src/questions/mods.rs b/projects/cli/src/questions/mods.rs index 7cccd81..8e7a589 100644 --- a/projects/cli/src/questions/mods.rs +++ b/projects/cli/src/questions/mods.rs @@ -25,7 +25,7 @@ impl Questions { } let choices = requestty::Question::select("mod") - .message("Which mod do you want to add?") + .message("Which curseforge mod do you want to add?") .choices( mods.iter() .map(|mod_| mod_.name.clone()) @@ -50,7 +50,7 @@ impl Questions { fn check_if_user_wanna_add_mod(&self) -> bool { let add_mod_question = requestty::Question::confirm("add_mod") - .message("Do you want to add a mod ?") + .message("Do you want to add a curseforge mod ?") .build(); let binding = requestty::prompt_one(add_mod_question) @@ -62,7 +62,7 @@ impl Questions { fn add_mod(&self) -> String { let what_mod = requestty::Question::input("what_mod") - .message("What mod do you want to add ?") + .message("What curseforge mod do you want to add ?") .build(); let binding = requestty::prompt_one(what_mod).unwrap(); @@ -89,10 +89,6 @@ impl Questions { break; } } - - if !mod_added { - return Err(Error::NoModAdded); - } Ok(mods) }