diff --git a/.fqntqpack b/.fqntqpack deleted file mode 100644 index e69de29..0000000 diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..360c9b9 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/v2.iml b/.idea/v2.iml new file mode 100644 index 0000000..cf84ae4 --- /dev/null +++ b/.idea/v2.iml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index b240594..813159e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,6 +19,15 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + [[package]] name = "base64ct" version = "1.6.0" @@ -288,12 +297,24 @@ dependencies = [ "libc", ] +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "libc" version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + [[package]] name = "lzma-sys" version = "0.1.20" @@ -305,6 +326,21 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + [[package]] name = "miniz_oxide" version = "0.7.1" @@ -314,6 +350,16 @@ dependencies = [ "adler", ] +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num_cpus" version = "1.16.0" @@ -324,12 +370,24 @@ dependencies = [ "libc", ] +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + [[package]] name = "option-ext" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "pack_sync" version = "0.1.0" @@ -338,6 +396,9 @@ dependencies = [ "num_cpus", "serde", "serde_yaml", + "toml", + "tracing", + "tracing-subscriber", "walkdir", "zip", "zip_archive", @@ -366,6 +427,12 @@ dependencies = [ "sha2", ] +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + [[package]] name = "pkg-config" version = "0.3.27" @@ -425,6 +492,50 @@ dependencies = [ "thiserror", ] +[[package]] +name = "regex" +version = "1.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.6", + "regex-syntax 0.8.2", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.2", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + [[package]] name = "ryu" version = "1.0.15" @@ -460,6 +571,15 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_spanned" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +dependencies = [ + "serde", +] + [[package]] name = "serde_yaml" version = "0.9.25" @@ -495,6 +615,21 @@ dependencies = [ "digest", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "smallvec" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" + [[package]] name = "subprocess" version = "0.2.9" @@ -553,6 +688,16 @@ dependencies = [ "syn", ] +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + [[package]] name = "time" version = "0.3.27" @@ -570,6 +715,101 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" +[[package]] +name = "toml" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af06656561d28735e9c1cd63dfd57132c8155426aa6af24f36a00a351f88c48e" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18769cd1cec395d70860ceb4d932812a0b4d06b1a4bb336745a4d21b9496e992" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + [[package]] name = "typenum" version = "1.16.0" @@ -588,6 +828,12 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f28467d3e1d3c6586d8f25fa243f544f5800fec42d97032474e17222c2b75cfa" +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "version_check" version = "0.9.4" @@ -707,6 +953,15 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +[[package]] +name = "winnow" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8" +dependencies = [ + "memchr", +] + [[package]] name = "xattr" version = "1.0.1" diff --git a/Cargo.toml b/Cargo.toml index a2ce002..9422898 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,14 @@ name = "pack_sync" version = "0.1.0" edition = "2021" +[[bin]] +name = "pack_sync_cli" +path = "src/cli.rs" + +[[bin]] +name = "pack_sync_server" +path = "src/server.rs" + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] @@ -10,6 +18,9 @@ dirs = "5.0.1" num_cpus = "1.16.0" serde = { version = "1.0.188", features = ["derive"] } serde_yaml = "0.9.25" +toml = "0.8.11" +tracing = "0.1.40" +tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } walkdir = "2.3.3" zip = "0.6.6" zip_archive = "1.2.2" diff --git a/README.md b/README.md index 093f616..059b5e2 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,28 @@ -# pack deployer -- a simple utility to automatically archive and move your resourcepacks into a folder. +# Pack Sync +### A simple utility for resoucepack developers who like to stay organised. -## use cases +### Use Case: +- You have a folder full of all your projects working on minecraft resource packs +- You want to be able to automatically archive these files and move them to your resourcepacks folders for testing +- You make modular packs with different themes that need to be kept organised -this may be useful if you regularly create resourcepacks and want to organise your projects in different folders. -the utility recursively scans directories in the project folder and organises packs into a folder \ No newline at end of file +### How pack sync can help +The pack sync client is a command line utility that allows you to automatically +archive and load all of your resourcepacks into correct folders. + + +### Usage: + +- start by creating a config.toml file +```toml +repository = "." +resourcepacks = "_resourcepacks" +ignore_folder_prefix = "_" + +[versions.1-8-9] +resourcepacks = "mc_instances/1.8.9/resourcepacks" + +[versions.1-20-1] +resourcepacks = "mc_instances/1.20.4/resourcepacks" +``` \ No newline at end of file diff --git a/src/cli.rs b/src/cli.rs new file mode 100644 index 0000000..92a23ff --- /dev/null +++ b/src/cli.rs @@ -0,0 +1,354 @@ + + +use num_cpus; +use std::{ + fs::{self, OpenOptions}, + io::{self, Error, ErrorKind, Write}, + path::{Path, PathBuf}, +}; +use std::collections::HashMap; +use std::slice::SliceIndex; +use tracing::{event, info, span, Level, warn, error}; +use tracing_subscriber::{ + filter::{EnvFilter, LevelFilter}, + fmt::{self, fmt}, + prelude::*, +}; + +use toml; + +use walkdir::WalkDir; +use zip::write::{FileOptions, ZipWriter}; +use zip::CompressionMethod; +use zip_archive::{get_dir_list, Archiver}; + + +fn main() { + tracing_subscriber::registry() + .with( + fmt::layer() + .pretty() + .with_target(false) + .with_thread_ids(false) + .with_thread_names(false) + .with_file(false) + .with_line_number(false), + ) + .with( + EnvFilter::builder() + .with_default_directive(LevelFilter::DEBUG.into()) + .from_env_lossy(), + ) + .init(); + + let config = Config::load().expect("failed to load config"); + let pack_repo = PathBuf::from(&config.repository); + match clear_resourcepacks(&config) { + Ok(_) => {}, + Err(e) => return, + }; + search_dir(&pack_repo, &config).unwrap(); +} + +fn clear_resourcepacks(conf: &Config) -> io::Result<()> { + let span = span!(Level::INFO, "clear_packs"); + let _guard = span.enter(); + + info!("Clearing existing resourcepacks folder"); + match fs::remove_dir_all(&conf.resourcepacks) { + Ok(_) => {} + Err(e) => warn!("No existing packs folder detected. Creating new folder."), + }; + match fs::create_dir(&conf.resourcepacks) { + Ok(_) => {} + Err(e) => { + error!("{}", e); + return Err(e); + } + }; + info!("Successfully reset resourcepacks folder"); + Ok(()) +} + +fn search_dir(path: &PathBuf, config: &Config) -> io::Result<()> { + let span = span!(Level::INFO, "Searching for packs"); + let _guard = span.enter(); + + // info!("Searching for packs in {}", path.display()); + // check if file is a valid pack + + if path.clone().join("pack.toml").exists() { + println!("Found pack {}", path.display()); + Pack::load(&path).unwrap().archive(&path, config).unwrap(); + return Ok(()); + } else { + println!("1c| {}", path.display()); + + for entry in WalkDir::new(path.clone()) + .into_iter() + .filter_map(|e| e.ok()) + .filter(|e| e.file_type().is_dir() && e.file_name() != path) + .filter(|e| !e.file_name().to_str().unwrap().to_string().contains("/_")) + { + println!("PATH {}", entry.path().join(entry.file_name()).to_str().unwrap()); + // search_dir(&entry.path().join(entry.file_name()).to_path_buf(), config).unwrap(); + } + } + + + Ok(()) +} + + + +#[derive(Debug, Clone, Deserialize)] +struct Pack { + release: bool, + name: String, + version: String, + description: String, + packs: HashMap, +} + +impl Pack { + fn load(path: &PathBuf) -> io::Result { + let span = span!(Level::INFO, "Loading Pack Config file"); + let _guard = span.enter(); + + let contents = fs::read_to_string(path.join("pack.toml"))?; + let pack = toml::from_str::(&contents).map_err(|e| Error::new(ErrorKind::Other, e))?; + + Ok(pack) + } + + fn archive(&self, path: &PathBuf, config: &Config) -> Result<(), Box> { + let span = span!(Level::INFO, "Archiving Pack"); + let _guard = span.enter(); + + let resourcepacks = if config.versions.contains_key(&self.version) { + match config.versions.get(&self.version).unwrap().resourcepacks.clone() { + Some(folder) => folder, + None => config.resourcepacks.clone() + } + } else { + config.resourcepacks.clone() + }; + let zip_file = fs::File::create(format!("{}/{}.zip", resourcepacks, self.name))?; + let mut zip_writer = ZipWriter::new(zip_file); + + let options = FileOptions::default() + .compression_method(CompressionMethod::Deflated) + .unix_permissions(0o755); + + for entry in WalkDir::new(path.clone()) + .into_iter() + .filter_map(|e| e.ok()) + { + let file_path = entry.path(); + if file_path.is_file() { + let mut file = fs::File::open(file_path)?; + let relpath = file_path.strip_prefix(path.clone())?; + let mut zip_path = PathBuf::new(); + zip_path.push(relpath); + + zip_writer.start_file(zip_path.to_string_lossy().into_owned(), options)?; + std::io::copy(&mut file, &mut zip_writer)?; + } + } + zip_writer.finish()?; + + Ok(()) + } +} + + +#[derive(Debug, Clone, Deserialize)] +struct PackComponent { + prefix: String, + release: bool, +} + +// +// fn scan_directory(path: PathBuf, config: &Config) -> io::Result<()> { +// let mut pack_mcmeta = path.clone(); +// pack_mcmeta.push("pack.mcmeta"); +// if pack_mcmeta.exists() { +// // if pack metadata exists, put the pack into a zip folder. +// get_archive(path, config).map_err(|_| Error::new(ErrorKind::Other, "failed to archive zip"))?; +// return Ok(()); +// } else { +// // recursively scans subdirectories for other packs +// for entry in fs::read_dir(path)? { +// let entry = entry?; +// +// if entry +// .path() +// .file_name() +// .unwrap() +// .to_os_string() +// .into_string() +// .map_err(|_| Error::new(ErrorKind::Other, "failed to convert os_str to string"))? +// .starts_with("_") +// { +// println!("found _"); +// continue; +// } +// if let Some(zip_str) = entry.path().extension() { +// if zip_str == "zip" { +// fs::copy( +// entry.path(), +// format!( +// "{}/{}", +// config.resourcepacks, +// entry +// .path() +// .file_name() +// .unwrap() +// .to_os_string() +// .into_string() +// .map_err(|_| Error::new( +// ErrorKind::Other, +// "failed to convert os_str to string" +// ))? +// ), +// )?; +// } +// } +// +// if let Ok(file_type) = entry.file_type() { +// if file_type.is_dir() { +// scan_directory(entry.path(), &config)?; +// } +// } +// } +// } +// Ok(()) +// } +// +// fn get_archive(path: PathBuf, config: &Config) -> Result<(), Box> { +// println!("CREATING ARCHIVE: {}", &path.display()); +// if let Some(f) = path.file_stem() { +// let mut filename = f +// .to_os_string() +// .into_string() +// .map_err(|_| Error::new(ErrorKind::Other, "failed to convert os_str to string"))?; +// filename.push_str("_0"); +// loop { +// if !Path::new(&format!("{}/{}.zip", config.resourcepacks, filename.to_string())).exists() { +// // checks if zip file already exists +// let zip_file = fs::File::create(format!("{}/{}.zip", config.resourcepacks, filename))?; +// let mut zip_writer = ZipWriter::new(zip_file); +// +// let options = FileOptions::default() +// .compression_method(CompressionMethod::Deflated) +// .unix_permissions(0o755); +// +// for entry in WalkDir::new(path.clone()) +// .into_iter() +// .filter_map(|e| e.ok()) +// { +// let file_path = entry.path(); +// if file_path.is_file() { +// let mut file = fs::File::open(file_path)?; +// let relpath = file_path.strip_prefix(path.clone())?; +// let mut zip_path = PathBuf::new(); +// zip_path.push(relpath); +// +// zip_writer.start_file(zip_path.to_string_lossy().into_owned(), options)?; +// std::io::copy(&mut file, &mut zip_writer)?; +// } +// } +// zip_writer.finish()?; +// break; +// } else { +// // 10 increments the last digit of the filename by 1 +// // TODO: add support for more than one digit +// let i: u32 = filename.pop().unwrap().to_digit(10).unwrap(); +// filename.push(char::from_digit(i + 1, 10).unwrap()); +// } +// } +// } +// Ok(()) +// } + + +#[derive(Debug, Deserialize, Serialize)] +pub struct VersionConfig { + pub pack_repo: Option, + pub resourcepacks: Option, + pub ignore_folder_prefix: Option, +} + +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Deserialize, Serialize)] +pub struct Config { + pub repository: String, + pub resourcepacks: String, + pub ignore_folder_prefix: String, + pub versions: HashMap +} + +impl Config { + pub fn default() -> Config { + Config { + repository: ".".to_string(), + resourcepacks: "_resourcepacks".to_string(), + ignore_folder_prefix: "_".to_string(), + versions: HashMap::new() + } + } + + pub fn load() -> Result { + let span = span!(Level::INFO, "Loading Configuration File"); + let _guard = span.enter(); + + let mut conf_path = Config::path().ok_or(Error::new(ErrorKind::Other, "error getting config path"))?; + + if conf_path.exists() { + let config: Config = toml::from_str(fs::read_to_string(&conf_path)?.as_str()) + .map_err(|_| Error::new(ErrorKind::Other, "failed to deserialise"))?; + info!( + "Loaded Config File: \n\trepo: {}\n\tpacks folder: {}\n\tignore with prefix: {}", + config.repository, config.resourcepacks, config.ignore_folder_prefix + ); + Ok(config) + } else { + warn!( + "No config file detected. Creating config file with default values at:\n\t{}", + conf_path.display() + ); + create_config_file(&conf_path)?; + Ok(Config::default()) + } + } + #[cfg(debug_assertions)] + fn path() -> Option { + Some(PathBuf::from(".").join("config.toml")) + } + #[cfg(not(debug_assertions))] + fn path() -> Option { + dirs::config_dir()?.join("pack_sync").join("config.toml") + } + +} + + +fn create_config_file(path: &PathBuf) -> Result<(), Error> { + println!("{}", path.display()); + + fs::create_dir_all(path.parent().unwrap())?; + fs::File::create(path)?; + + let yaml = serde_yaml::to_string(&Config::default()) + .map_err(|_| Error::new(ErrorKind::Other, "failed to serialise"))?; + + let mut file = OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .open(path)? + .write_all(yaml.as_bytes())?; + + Ok(()) +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index d77117f..0000000 --- a/src/main.rs +++ /dev/null @@ -1,106 +0,0 @@ -use std::{fs, io::{self, Error, ErrorKind}, path::{Path, PathBuf}}; -use zip::write::{FileOptions, ZipWriter}; -use zip::CompressionMethod; -use walkdir::WalkDir; -use zip_archive::{Archiver, get_dir_list}; -use num_cpus; - -pub mod utils; - -const PACK_REPO: &str = "./"; -const RESOURCEPACKS: &str = "./_resourcepacks"; - -const IGNORE_FOLDERS_PREFIX: &str = "_"; - -fn main() { - let conf = utils::load_config().unwrap(); - let pack_repo = PathBuf::from(PACK_REPO); - clear_resourcepacks().unwrap(); - scan_directory(pack_repo).unwrap(); -} - -fn clear_resourcepacks() -> io::Result<()> { - println!("[removing existing packs]"); - fs::remove_dir_all(RESOURCEPACKS)?; - fs::create_dir(RESOURCEPACKS)?; - println!(" => cleared resourcepacks folder"); - Ok(()) -} - -fn scan_directory(path: PathBuf) -> io::Result<()> { - let mut pack_mcmeta = path.clone(); - pack_mcmeta.push("pack.mcmeta"); - if pack_mcmeta.exists() { - // if pack metadata exists, put the pack into a zip folder. - get_archive(path).map_err(|_| Error::new(ErrorKind::Other, "failed to archive zip"))?; - return Ok(()) - } else { - // recursively scans subdirectories for other packs - for entry in fs::read_dir(path)? { - let entry = entry?; - - if entry.path() - .file_name() - .unwrap() - .to_os_string() - .into_string() - .map_err(|_| Error::new(ErrorKind::Other, "failed to convert os_str to string"))? - .starts_with("_") { - println!("found _"); - continue; - } - if let Some(zip_str) = entry.path().extension() { - if zip_str == "zip" { - fs::copy(entry.path(), format!("{}/{}", RESOURCEPACKS, entry.path().file_name().unwrap().to_os_string().into_string().map_err(|_| Error::new(ErrorKind::Other, "failed to convert os_str to string"))?))?; - } - } - - if let Ok(file_type) = entry.file_type() { - if file_type.is_dir() { - scan_directory(entry.path())?; - } - } - } - } - Ok(()) -} - -fn get_archive(path: PathBuf) -> Result<(), Box> { - println!("CREATING ARCHIVE: {}", &path.display()); - if let Some(f) = path.file_stem() { - let mut filename = f.to_os_string().into_string().map_err(|_| Error::new(ErrorKind::Other, "failed to convert os_str to string"))?; - filename.push_str("_0"); - loop { - if !Path::new(&format!("{}/{}.zip", RESOURCEPACKS, filename.to_string())).exists() { // checks if zip file already exists - let zip_file = fs::File::create(format!("{}/{}.zip", RESOURCEPACKS, filename))?; - let mut zip_writer = ZipWriter::new(zip_file); - - let options = FileOptions::default() - .compression_method(CompressionMethod::Deflated) - .unix_permissions(0o755); - - for entry in WalkDir::new(path.clone()).into_iter().filter_map(|e| e.ok()) { - let fpath = entry.path(); - if fpath.is_file() { - let mut file = fs::File::open(fpath)?; - let relpath = fpath.strip_prefix(path.clone())?; - let mut zip_path = PathBuf::new(); - zip_path.push(relpath); - - zip_writer.start_file(zip_path.to_string_lossy().into_owned(), options)?; - std::io::copy(&mut file, &mut zip_writer)?; - } - } - zip_writer.finish()?; - break; - - } else { - // 10 increments the last digit of the filename by 1 - // TODO: add support for more than one digit - let i: u32 = filename.pop().unwrap().to_digit(10).unwrap(); - filename.push(char::from_digit(i+1, 10).unwrap()); - } - } - } - Ok(()) -} \ No newline at end of file diff --git a/src/server.rs b/src/server.rs new file mode 100644 index 0000000..e813a5e --- /dev/null +++ b/src/server.rs @@ -0,0 +1,3 @@ +fn main() { + println!("hello world"); +} \ No newline at end of file diff --git a/src/utils.rs b/src/utils.rs deleted file mode 100644 index f1de0b4..0000000 --- a/src/utils.rs +++ /dev/null @@ -1,74 +0,0 @@ -use std::{fs::{self, OpenOptions}, io::{self, Error, ErrorKind, Write}, path::{Path, PathBuf}}; -use crate::IGNORE_FOLDERS_PREFIX; - -use serde::{Serialize, Deserialize}; - -#[derive(Debug, Deserialize, Serialize)] -pub struct Config { - pack_repo: String, - resourcepacks: String, - ignore_folder_prefix: String, -} - -fn config_path() -> Option { - dirs::config_dir() -} - -fn create_config_dir(path: &PathBuf) -> Result<(), Error> { - fs::create_dir_all(path)?; - create_config_file(&path.join("config.yml"))?; - Ok(()) -} - -fn write_config(path: &PathBuf, config: Config) -> Result<(), Error> { - fs::File::create(path)?; - Ok(()) -} - -fn create_config_file(path: &PathBuf) -> Result<(), Error> { - fs::File::create(path)?; - - let conf_file = Config { - pack_repo: ".".to_string(), - resourcepacks: "./_resourcepacks".to_string(), - ignore_folder_prefix: "_".to_string(), - }; - let yaml = serde_yaml::to_string(&conf_file).map_err(|_| Error::new(ErrorKind::Other, "failed to serialise"))?; - - let mut file = OpenOptions::new() - .write(true) - .create(true) - .truncate(true) - .open(path)?; - - file.write_all(yaml.as_bytes())?; - Ok(()) -} - -pub fn load_config() -> Result { - let mut conf_path = config_path().ok_or(Error::new(ErrorKind::Other, "error getting config path"))?; - if !conf_path.exists() { - return Err(Error::new(ErrorKind::Other, "No config directory found")); - }; - - conf_path.push("fqntqpacks"); - if !conf_path.exists() { - create_config_dir(&conf_path)?; - }; - - conf_path.push("config.yml"); - if !conf_path.exists() { - create_config_file(&conf_path)?; - }; - - let config: Config = serde_yaml::from_reader(fs::File::open(&conf_path)?) - .map_err(|_| Error::new(ErrorKind::Other, "failed to deserialise"))?; - - println!( -"----------------------------------------------------------------- -WARNING: no previous config file detected, created config file at -{} ------------------------------------------------------------------", conf_path.display()); - - Ok(config) -} \ No newline at end of file