From a6cc2615355be9fba7b6fd132576d333d9e66428 Mon Sep 17 00:00:00 2001 From: cruzerngz <61926974+cruzerngz@users.noreply.github.com> Date: Mon, 4 Jul 2022 00:08:25 +0800 Subject: [PATCH 01/10] sync changes no updates --- Cargo.lock | 186 ++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 2 + src/args.rs | 6 +- src/export.rs | 62 ++++++++++++++ src/factorio_structs.rs | 5 +- src/main.rs | 6 +- src/progress.rs | 3 + 7 files changed, 261 insertions(+), 9 deletions(-) create mode 100644 src/progress.rs diff --git a/Cargo.lock b/Cargo.lock index f3bf4d7..93194ae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -37,6 +37,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + [[package]] name = "clap" version = "3.2.8" @@ -76,6 +82,31 @@ dependencies = [ "os_str_bytes", ] +[[package]] +name = "crossterm" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab9f7409c70a38a56216480fba371ee460207dd8926ccf5b4160591759559170" +dependencies = [ + "bitflags", + "crossterm_winapi", + "libc", + "mio", + "parking_lot", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ae1b35a484aa10e07fe0638d02301c5ad24de82d310ccbd2f3693da5f09bf1c" +dependencies = [ + "winapi", +] + [[package]] name = "deflate" version = "1.0.0" @@ -91,6 +122,7 @@ version = "0.1.0" dependencies = [ "base64", "clap", + "crossterm", "deflate", "inflate", "serde", @@ -149,6 +181,37 @@ version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" +[[package]] +name = "lock_api" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "mio" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys", +] + [[package]] name = "once_cell" version = "1.12.0" @@ -161,6 +224,29 @@ version = "6.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa" +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -203,12 +289,27 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "redox_syscall" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +dependencies = [ + "bitflags", +] + [[package]] name = "ryu" version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + [[package]] name = "serde" version = "1.0.137" @@ -240,6 +341,42 @@ dependencies = [ "serde", ] +[[package]] +name = "signal-hook" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" +dependencies = [ + "libc", + "mio", + "signal-hook", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + +[[package]] +name = "smallvec" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" + [[package]] name = "strsim" version = "0.10.0" @@ -284,6 +421,12 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "winapi" version = "0.3.9" @@ -314,3 +457,46 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +dependencies = [ + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" + +[[package]] +name = "windows_i686_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" + +[[package]] +name = "windows_i686_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" diff --git a/Cargo.toml b/Cargo.toml index 4768a43..0947848 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,8 +13,10 @@ base64 = "0.13.0" inflate = "0.4.5" deflate = "1.0.0" clap = {version = "3.2.7", features = ["derive"]} +crossterm = "0.24.0" [profile.release] +opt-level = "s" codegen-units = 1 strip = "symbols" lto = true diff --git a/src/args.rs b/src/args.rs index fab084e..d98946e 100644 --- a/src/args.rs +++ b/src/args.rs @@ -32,14 +32,10 @@ pub enum SubCommands { /// Export a single file or JSON tree as a blueprint string #[clap(arg_required_else_help = true)] Export { - /// Source directory or JSON file + /// Source directory or single JSON file #[clap(value_parser)] source: Option, - /// Infile, for single blueprints - #[clap(short, long)] - infile: Option, - /// Outfile containing blueprint string #[clap(short, long)] outfile: Option diff --git a/src/export.rs b/src/export.rs index 974b2da..5eaa072 100644 --- a/src/export.rs +++ b/src/export.rs @@ -1,4 +1,10 @@ +use std::io::Error; use std::path::PathBuf; +use std::fs; + +use serde_json::Value; + +use crate::factorio_structs; pub struct Worker { pub source: Option, @@ -10,6 +16,62 @@ impl Worker { /// Main calling method for struct pub fn exec(&self) { + todo!("Write the export module"); + } + + /// Returns the complete blueprint JSON, given a file name + /// Returns the blueprint name if an error occurs + fn get_blueprint(bp_file_path: &PathBuf) -> Result { + + assert!(bp_file_path.is_file()); + assert_eq!(bp_file_path.extension().unwrap(), "json"); + + let bp_file = fs::read_to_string(bp_file_path); + + match bp_file { + Ok(file_contents) => { + let json_contents: serde_json::Value = serde_json::from_str(&file_contents.as_str()) + .expect("failed to serialize JSON data."); + + return Ok(json_contents); + }, + Err(_) => {return Err(());} + } + + } + + /// Recursively searches the directory to rebuild the blueprint book + /// Returns the blueprint book name if an error occurs + fn get_book_recursive(bp_book_dir_path: &PathBuf) -> Result { + + let bp_book_name = bp_book_dir_path.file_name().unwrap(); + let mut bp_book_file: PathBuf = bp_book_dir_path.clone(); + bp_book_file.push(bp_book_name); + bp_book_file.set_extension("json"); + + let mut bp_book_json: Value; + if bp_book_file.exists() { + bp_book_json = serde_json::from_str( + fs::read_to_string(&bp_book_file) + .expect("Error reading file") + .as_str() + ) + .expect("Error serializing string"); + } else { + return Err(bp_book_name.to_str().unwrap().to_string()); + } + + // This variable contains the typed vector of UnknownBlueprintType + // that is used to reconstruct the book + let bp_book: factorio_structs::Book = serde_json::from_value(bp_book_json.clone()).unwrap(); + + // the "order" key is consumed internally + let bp_obj = bp_book_json.as_object_mut() + .unwrap(); + + bp_obj.remove("order"); + + todo!(); } } diff --git a/src/factorio_structs.rs b/src/factorio_structs.rs index b81555a..41e51a4 100644 --- a/src/factorio_structs.rs +++ b/src/factorio_structs.rs @@ -23,7 +23,10 @@ pub struct Book { pub label: String, pub label_color: Option, pub active_index: u32, - pub version: u64 + pub version: u64, + + /// Blueprint order in book, not part of factorio spec. + pub order: Option> } /// Blueprint parameters except arrays diff --git a/src/main.rs b/src/main.rs index 0908145..98d80b1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,7 @@ mod import; mod export; mod common; mod factorio_structs; +mod progress; use std::{fs, process::exit}; @@ -43,9 +44,8 @@ fn main() { }, SubCommands::Export { - infile, - outfile, - source + source, + outfile } => { println!( "TODO: Export JSON tree from {:?} and write blueprint string to {:?}", diff --git a/src/progress.rs b/src/progress.rs new file mode 100644 index 0000000..35d6c55 --- /dev/null +++ b/src/progress.rs @@ -0,0 +1,3 @@ +//! This module handles writing progress to stdout +//! + From 3a31d7836eff3e5ba9ee91206fff9f3db467c5bd Mon Sep 17 00:00:00 2001 From: cruzerngz Date: Mon, 18 Jul 2022 22:01:37 +0800 Subject: [PATCH 02/10] export working + new progress visual - import file structure is most definitely finalised - export works with JSON files or directories created through import - exported books do not work with teoxoy's online FBE, but exported blueprints do - the factorio_structs file is a mess --- Cargo.lock | 1 + Cargo.toml | 2 +- src/args.rs | 2 +- src/common.rs | 18 +++ src/export.rs | 332 ++++++++++++++++++++++++++++++++++++---- src/factorio_structs.rs | 104 ++++++++++++- src/import.rs | 109 ++++++++----- src/main.rs | 19 +-- src/progress.rs | 151 +++++++++++++++++- 9 files changed, 643 insertions(+), 95 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 93194ae..e68adfd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -336,6 +336,7 @@ version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7" dependencies = [ + "indexmap", "itoa", "ryu", "serde", diff --git a/Cargo.toml b/Cargo.toml index 0947848..9bd6c1c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ edition = "2021" [dependencies] serde = {version = "1.0.137", features = ["derive"]} -serde_json = "1.0.82" +serde_json = {version = "1.0.82", features = ["preserve_order"]} base64 = "0.13.0" inflate = "0.4.5" deflate = "1.0.0" diff --git a/src/args.rs b/src/args.rs index d98946e..b7c2cbe 100644 --- a/src/args.rs +++ b/src/args.rs @@ -36,7 +36,7 @@ pub enum SubCommands { #[clap(value_parser)] source: Option, - /// Outfile containing blueprint string + /// Outfile (optional), containing blueprint string #[clap(short, long)] outfile: Option } diff --git a/src/common.rs b/src/common.rs index 64ab56e..05b95c4 100644 --- a/src/common.rs +++ b/src/common.rs @@ -5,6 +5,8 @@ use serde_json::Value; use crate::factorio_structs; +pub const INVALID_CHARS: &str = r#"/\<>:"|?*"#; + /// Various types of paths the program may encounter #[derive(Debug)] pub enum PathType { @@ -104,6 +106,22 @@ pub fn factorio_deflate(bp_string_json: &str) -> String { return result; } +/// Replaces all invalid characters in file names with underscores +pub fn file_rename(file_name: String) -> String { + let mut new_file_name: String = String::new(); + + for character in file_name.chars() { + if INVALID_CHARS.contains(character) { + println!("invalid character {}", &character); + new_file_name.push('_'); + } else { + new_file_name.push(character); + } + } + + return new_file_name; +} + /// Remove any pretty-printed indentations from the json string pub fn json_remove_indents(json_indent_str: &str) -> String { let json_object: serde_json::Value = serde_json::from_str(json_indent_str) diff --git a/src/export.rs b/src/export.rs index 5eaa072..63eaee2 100644 --- a/src/export.rs +++ b/src/export.rs @@ -1,27 +1,109 @@ -use std::io::Error; use std::path::PathBuf; use std::fs; +use std::process::exit; use serde_json::Value; use crate::factorio_structs; +use crate::common; +use crate::progress::{self, ProgressType}; pub struct Worker { - pub source: Option, + pub source: String, pub out_file: Option, - pub source_path: Option + // pub source_path: Option } impl Worker { /// Main calling method for struct pub fn exec(&self) { - todo!("Write the export module"); + let source_path = PathBuf::from(&self.source); + let root_bp_dir = source_path.file_name(); + + let mut progress_tracker = progress::Tracker::new(progress::CommandType::Export); + let mut read_json_value = serde_json::json!({}); + + match source_path.extension() { + Some(ext) => { // read single file + if ext.eq_ignore_ascii_case("json") { + match Worker::read_blueprint(&source_path) { + Ok(json_object) => { + read_json_value = json_object; + progress_tracker.ok( + ProgressType::Blueprint( + source_path + .to_str() + .unwrap() + .to_string() + ) + ) + }, + Err(err_msg) => { + progress_tracker.error( + ProgressType::Blueprint(source_path + .to_str() + .unwrap() + .to_string() + ), + Some(err_msg) + ) + } + } + } else { + progress_tracker.error_additional("file given is not JSON!".to_string()); + progress_tracker.complete(); + exit(1); + } + }, + + None => { // read blueprint book (recursive) + match Worker::read_book_recursive(&mut progress_tracker, &source_path) { + Ok(json_object) => { + read_json_value = json_object; + }, + Err(err_msg) => { + progress_tracker.error( + ProgressType::Book(source_path + .to_str() + .unwrap() + .to_string()), + Some(err_msg) + ) + } + } + } + } + // println!("final constructed: {:?}", &read_json_value); + + match self.write_blueprint_to_file(&read_json_value) { + Ok(()) => (), + Err(err_msg) => { + progress_tracker.error_additional(err_msg); + } + } + progress_tracker.complete(); } /// Returns the complete blueprint JSON, given a file name - /// Returns the blueprint name if an error occurs - fn get_blueprint(bp_file_path: &PathBuf) -> Result { + /// Returns an error message if an error occurs + fn read_blueprint(bp_file_path: &PathBuf) -> Result { + + // println!("reading: {:?}", &bp_file_path); + + if !bp_file_path.is_file() { + return Err(bp_file_path.to_str().unwrap().to_string()); + } + match bp_file_path.extension() { + None => { + return Err(bp_file_path.to_str().unwrap().to_string()) + } + Some(file_ext) => { + if !file_ext.eq_ignore_ascii_case("json") { + return Err(bp_file_path.to_str().unwrap().to_string()) + } + } + } assert!(bp_file_path.is_file()); assert_eq!(bp_file_path.extension().unwrap(), "json"); @@ -35,43 +117,227 @@ impl Worker { return Ok(json_contents); }, - Err(_) => {return Err(());} + Err(_) => {return Err( + bp_file_path + .to_str() + .unwrap() + .to_string() + ); + } } } /// Recursively searches the directory to rebuild the blueprint book - /// Returns the blueprint book name if an error occurs - fn get_book_recursive(bp_book_dir_path: &PathBuf) -> Result { - - let bp_book_name = bp_book_dir_path.file_name().unwrap(); - let mut bp_book_file: PathBuf = bp_book_dir_path.clone(); - bp_book_file.push(bp_book_name); - bp_book_file.set_extension("json"); - - let mut bp_book_json: Value; - if bp_book_file.exists() { - bp_book_json = serde_json::from_str( - fs::read_to_string(&bp_book_file) - .expect("Error reading file") - .as_str() - ) - .expect("Error serializing string"); - } else { - return Err(bp_book_name.to_str().unwrap().to_string()); + /// Returns an error message if an error occurs + fn read_book_recursive( + prog_tracker: &mut progress::Tracker, + bp_book_dir_path: &PathBuf + ) -> Result { + // println!("reading: {:?}", &bp_book_dir_path); + + let book_name = bp_book_dir_path.file_name().unwrap().to_str().unwrap(); + let mut dot_file_path = bp_book_dir_path.clone(); + let current_dir_path = bp_book_dir_path.clone(); // used in recursion + // set the dotfile path + dot_file_path.push(format!(".{}.json", book_name)); + + let dot_file_contents: String; + match fs::read_to_string(&dot_file_path) { + Ok(_file) => dot_file_contents = _file, + Err(_) => return Err(format!("failed to read {}", &dot_file_path.to_string_lossy())) + } + + let mut book_object: factorio_structs::BookDotFileHead; + + match serde_json::from_str(dot_file_contents.as_ref()) { + Ok(_book) => book_object = _book, + Err(_) => return Err(format!("failed to deserialize contents of {}", &dot_file_path.to_string_lossy())) + } + + book_object.blueprint_book.blueprints = Some(vec![]); + + // println!("{:#?}", book_object); + + // iterate through the list of stored blueprints + match &book_object.blueprint_book.order { + Some(unknown_bps) => { + for unknown_blueprint in unknown_bps.iter() { + + match &unknown_blueprint.blueprint { + Some(known_bp) => { + + let mut known_bp_path = current_dir_path.clone(); + known_bp_path.push(&known_bp.label); + known_bp_path.set_extension("json"); + + let known_bp_object: Option; + match Worker::read_blueprint(&known_bp_path) { + Ok(_bp_obj) => { + known_bp_object = Some(_bp_obj); + prog_tracker.ok( + ProgressType::Blueprint(known_bp_path + .to_str() + .unwrap() + .to_string() + ) + ) + }, + Err(err_msg) => { + known_bp_object = None; + prog_tracker.error( + ProgressType::Blueprint(known_bp_path + .to_str() + .unwrap() + .to_string() + ), + Some(err_msg) + ) + } + } + + match known_bp_object { + Some(_bp_obj) => { + if let Some(blueprint_vec) = &mut book_object.blueprint_book.blueprints { + blueprint_vec.push(_bp_obj); + } + }, + None => () + } + + }, + None => () + } + match &unknown_blueprint.blueprint_book { + Some(known_book) => { + + let mut known_book_path = current_dir_path.clone(); + known_book_path.push(&known_book.label); + + let known_book_object: Option; + match Worker::read_book_recursive(prog_tracker, &known_book_path) { + Ok(_book_obj) => { + known_book_object = Some(_book_obj); + prog_tracker.ok(ProgressType::Book( + known_book_path + .to_str() + .unwrap() + .to_string() + ) + ) + }, + Err(err_msg) => { + known_book_object = None; + prog_tracker.error(ProgressType::Book( + known_book_path + .to_str() + .unwrap() + .to_string() + ), + Some(err_msg) + ); + } + } + + match known_book_object { + Some(_book_obj) => { + if let Some(blueprint_vec) = &mut book_object.blueprint_book.blueprints { + blueprint_vec.push(_book_obj); + } + }, + None => () + } + }, + None => () + } + } + } + None => () } - // This variable contains the typed vector of UnknownBlueprintType - // that is used to reconstruct the book - let bp_book: factorio_structs::Book = serde_json::from_value(bp_book_json.clone()).unwrap(); + match serde_json::to_value(book_object) { + Ok(_val) => { + Ok(_val) + }, + Err(_) => { + Err("failed to convert typed struct to serde_json::Value".to_string()) + } + } + // todo!() + // let bp_book_name = bp_book_dir_path.file_name().unwrap(); + // let mut bp_book_file: PathBuf = bp_book_dir_path.clone(); + // bp_book_file.push(bp_book_name); + // bp_book_file.set_extension("json"); + + // let mut bp_book_json: Value; + // if bp_book_file.exists() { + // bp_book_json = serde_json::from_str( + // fs::read_to_string(&bp_book_file) + // .expect("Error reading file") + // .as_str() + // ) + // .expect("Error serializing string"); + // } else { + // return Err(format!("{:?} does not exist", bp_book_name)); + // } + + // // This variable contains the typed vector of UnknownBlueprintType + // // that is used to reconstruct the book + // let bp_book: factorio_structs::Book = serde_json::from_value(bp_book_json.clone()).unwrap(); + + // // the "order" key is consumed internally + // let bp_obj = bp_book_json.as_object_mut() + // .unwrap(); - // the "order" key is consumed internally - let bp_obj = bp_book_json.as_object_mut() - .unwrap(); + // bp_obj.remove("order"); - bp_obj.remove("order"); - todo!(); + + } + + /// Takes the blueprint and writes it to a destination + /// Returns an error message if it occurs + pub fn write_blueprint_to_file( + &self, + blueprint_json: &Value, + ) -> Result<(),String> { + + let mut destination: String; + match &self.out_file { + Some(dest) => destination = dest.to_owned(), + None => { + match common::BlueprintType::classify(&blueprint_json) { + common::BlueprintType::Invalid => { + return Err("failed to determine blueprint type".to_string()) + }, + common::BlueprintType::Blueprint(name) => { + destination = name; + }, + common::BlueprintType::Book(name) => { + destination = name; + } + } + destination = format!("blueprint_{}", destination); + } + } + + // println!("dest: {}", destination); + + match serde_json::to_string(blueprint_json) { + Ok(blueprint_string) => { + let blueprint_string_deflated = common::factorio_deflate(blueprint_string.as_ref()); + match fs::write(destination, blueprint_string_deflated.as_bytes()) { + Ok(_) => {return Ok(())}, + Err(_) => { + return Err("file write error".to_string()); + } + } + }, + Err(_) => return Err("serde_json serialize error".to_string()) + } + + + // todo!(); } } diff --git a/src/factorio_structs.rs b/src/factorio_structs.rs index 41e51a4..add494c 100644 --- a/src/factorio_structs.rs +++ b/src/factorio_structs.rs @@ -4,19 +4,65 @@ use serde::{Deserialize, Serialize}; +/// Structs defined here have a subset of attributes of their factorio equivalents. +pub mod fragments { + use super::*; + + /// Blueprint parameters except arrays + #[derive(Serialize, Deserialize, Debug)] + pub struct BlueprintFragment { + pub item: Option, + pub label: String, + pub label_color: Option, + pub version: u64 + } + + /// Blueprint book + #[derive(Serialize, Deserialize, Debug)] + pub struct BookFragment { + pub item: Option, + pub label: String, + pub label_color: Option, + pub active_index: u32, + pub version: u64 + } +} + +/// Structs that are valid for use in import only. +/// Importing is converting a blueprint string to files. +pub mod importable { + use super::*; + +} + +/// Structs that are valid for use in export only. +/// Exporting is converting a directory or a JSON file to a blueprint string. +pub mod exportable { + use super::*; + +} + /// Head of the blueprint book #[derive(Serialize, Deserialize, Debug)] pub struct BookHead { pub blueprint_book: Book } -/// Head of blueprint +#[derive(Serialize, Deserialize, Debug)] +pub struct BookDotFileHead { + pub blueprint_book: BookDotFile +} + +/// Head of blueprint, to factorio spec #[derive(Serialize, Deserialize, Debug)] pub struct BlueprintHead { - pub blueprint: Blueprint + pub blueprint: Blueprint, + + #[serde(skip_serializing)] + pub index: u16 } -/// Blueprint book parameters +/// Blueprint book with additional parameter containing the order of it's child blueprints #[derive(Serialize, Deserialize, Debug)] pub struct Book { pub item: Option, @@ -25,17 +71,58 @@ pub struct Book { pub active_index: u32, pub version: u64, + #[serde(skip_deserializing)] + #[serde(skip_serializing_if = "Option::is_none")] + pub blueprints: Option>, + /// Blueprint order in book, not part of factorio spec. + /// Used for storing the blueprint order inside a book + /// Contains the child blueprints/books, renamed to "order" + #[serde(rename(serialize = "order", deserialize = "blueprints"))] + pub order: Option> +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct BookDotFile { + #[serde(skip_serializing_if = "Option::is_none")] + pub item: Option, + pub label: String, + + #[serde(skip_serializing_if = "Option::is_none")] + pub label_color: Option, + pub active_index: u32, + pub version: u64, + + #[serde(skip_serializing_if = "Option::is_none")] + pub blueprints: Option>, + + /// Blueprint order in book, not part of factorio spec. + /// This attribute is not serialized + #[serde(skip_serializing)] pub order: Option> } -/// Blueprint parameters except arrays #[derive(Serialize, Deserialize, Debug)] pub struct Blueprint { + #[serde(skip_serializing_if = "Option::is_none")] pub item: Option, pub label: String, + + #[serde(skip_serializing_if = "Option::is_none")] pub label_color: Option, - pub version: u64 + pub version: u64, + + #[serde(skip_serializing_if = "Option::is_none")] + pub entities: Option>, + + #[serde(skip_serializing_if = "Option::is_none")] + pub tiles: Option>, + + #[serde(skip_serializing_if = "Option::is_none")] + pub icons: Option>, + + #[serde(skip_serializing_if = "Option::is_none")] + pub schedules: Option>, } #[derive(Serialize, Deserialize, Debug)] @@ -48,6 +135,9 @@ pub struct Color { #[derive(Serialize, Deserialize, Debug)] pub struct UnknownBlueprintType { - pub blueprint_book: Option, - pub blueprint: Option + #[serde(skip_serializing_if = "Option::is_none")] + pub blueprint_book: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub blueprint: Option } diff --git a/src/import.rs b/src/import.rs index b20e336..04101d8 100644 --- a/src/import.rs +++ b/src/import.rs @@ -4,8 +4,10 @@ use std::path::PathBuf; use std::process::exit; use std::fs::File; +use crate::factorio_structs::UnknownBlueprintType; use crate::{common, factorio_structs}; use crate::common::{BlueprintType, PathType}; +use crate::progress::{self, ProgressType}; pub struct Worker { pub in_file: String, @@ -39,75 +41,99 @@ impl Worker { } } + // create new progress tracker instance + let mut progress_tracker = progress::Tracker::new(progress::CommandType::Import); + // convert the string to a json value let blueprint_obj: serde_json::Value = serde_json::from_str(blueprint_inflated.as_str()) .expect("JSON parse error. Check that the blueprint string is valid."); - let blueprint_file_name: String; + // let blueprint_file_name: String; match BlueprintType::classify(&blueprint_obj) { BlueprintType::Invalid => { println!("Invalid blueprint!"); exit(1); } - BlueprintType::Blueprint(bp_name) => { + BlueprintType::Blueprint(_bp_name) => { match Worker::blueprint_write(&blueprint_obj, &PathBuf::from(&self.dest)) { - Ok(_) => {}, - Err(_) => {} + Ok(()) => progress_tracker.ok(ProgressType::Blueprint(_bp_name)), + Err(err_msg) => progress_tracker.error(ProgressType::Blueprint(_bp_name), Some(err_msg)) } } - BlueprintType::Book(book_name) => { - - match Worker::recursive_book_write(&blueprint_obj, &PathBuf::from(&self.dest)) { - Ok(_) => {}, - Err(_) => {} + BlueprintType::Book(_book_name) => { + match Worker::recursive_book_write(&mut progress_tracker, &blueprint_obj, &PathBuf::from(&self.dest)) { + Ok(()) => progress_tracker.ok(ProgressType::Book(_book_name)), + Err(err_msg) => progress_tracker.error(ProgressType::Book(_book_name), Some(err_msg)) } - - // blueprint_file_name = book_name; } } + progress_tracker.complete(); + } /// Writes a blueprint to file given the file path and blueprint object - fn blueprint_write(blueprint: &serde_json::Value, dir_path: &PathBuf) -> Result<(), ()> { + /// Returns an error message if encountered + fn blueprint_write( + blueprint: &serde_json::Value, + dir_path: &PathBuf + ) -> Result<(), String> { let mut full_bp_path = dir_path.clone(); - let bp_name = blueprint.get("blueprint") + let mut bp_name = blueprint.get("blueprint") .and_then(|value| value.get("label")) .and_then(|value| value.as_str()) .unwrap() .to_string(); - full_bp_path.push(bp_name); + // remove "index" key from the blueprint object + let blueprint_compliant: factorio_structs::BlueprintHead; + match serde_json::from_value(blueprint.to_owned()) { + Ok(result) => blueprint_compliant = result, + Err(_) => return Err("Error deserializing to compliant blueprint".to_string()) + } + + bp_name = common::file_rename(bp_name); + + full_bp_path.push(&bp_name); full_bp_path.set_extension("json"); let mut bp_file = File::create(&full_bp_path) .expect("File creation error. Check the file path given"); match bp_file.write( - serde_json::to_string_pretty(&blueprint) + serde_json::to_string_pretty(&blueprint_compliant) .unwrap() .as_bytes() ) { Ok(_) => { - println!("Created {}", &full_bp_path.to_string_lossy()); + // println!("Created {}", &full_bp_path.to_string_lossy()); return Ok(()); }, Err(_) => { - println!("Error creating {}", &full_bp_path.to_string_lossy()); - return Err(()); + // println!("Error creating {}", &full_bp_path.to_string_lossy()); + return Err(format!("Error creating {}", &full_bp_path.to_string_lossy())); + // return Err(bp_name); } } + } /// Recursively writes the book and its contents to file, given a known starting dir - fn recursive_book_write(bp_book: &serde_json::Value, dir_path: &PathBuf) -> Result<(), ()> { + /// Returns an error message if an error is encountered + fn recursive_book_write( + prog_tracker: &mut progress::Tracker, + bp_book: &serde_json::Value, + dir_path: &PathBuf + ) -> Result<(), String> { // println!("Blueprint book full: {:?}", bp_book); // Extract out only the relavant blueprint contents - let book_details: factorio_structs::BookHead = serde_json::from_value(bp_book.clone()) + let mut book_details: factorio_structs::BookHead = serde_json::from_value(bp_book.clone()) .expect("Failed to deserialize blueprint book"); + let bp_book_name = book_details.blueprint_book.label.clone(); + // println!("Blueprint book contents: {:?}", &book_details); // create new starting dir (for next recursion level) @@ -115,10 +141,11 @@ impl Worker { new_starting_dir.push(&book_details.blueprint_book.label); match fs::create_dir_all(&new_starting_dir) { Ok(_) => { - println!("Created dir {}", &new_starting_dir.to_string_lossy()); + // println!("Created dir {}", &new_starting_dir.to_string_lossy()); }, Err(_) => { - println!("Error creating dir {}", &new_starting_dir.to_string_lossy()); + // println!("Error creating dir {}", &new_starting_dir.to_string_lossy()); + return Err(format!("error creating dir {}", &new_starting_dir.to_string_lossy())); } } @@ -126,9 +153,11 @@ impl Worker { // blueprint book details are stored in a dotfile that matches the dir name let mut bp_book_path = dir_path.clone(); let mut bp_book_name = ".".to_string(); + + bp_book_name = common::file_rename(bp_book_name); bp_book_name.push_str(&book_details.blueprint_book.label); bp_book_path.push(&book_details.blueprint_book.label); - bp_book_path.push(bp_book_name); + bp_book_path.push(&bp_book_name); bp_book_path.set_extension("json"); let mut bp_book_file = File::create(&bp_book_path) @@ -140,41 +169,49 @@ impl Worker { .as_bytes() ) { Ok(_) => { - println!("Created {}", &bp_book_path.to_string_lossy()); + // println!("Created {}", &bp_book_path.to_string_lossy()); } Err(_) => { - println!("Error creating {}", &bp_book_path.to_string_lossy()); + // println!("Error creating {}", &bp_book_path.to_string_lossy()); + return Err(format!("error creating {}", &bp_book_path.to_string_lossy())); } } + book_details.blueprint_book.order; + // get the arr of blueprints let book_contents = bp_book.get("blueprint_book") .and_then(|value| value.get("blueprints")) .unwrap(); + book_details.blueprint_book.order = Some(vec!()); + // recurse match book_contents { serde_json::Value::Array(bp_arr) => { for bp_arr_item in bp_arr.iter() { + // store the order of blueprint book items + // bp_book_order = + // then recurse match BlueprintType::classify(bp_arr_item) { BlueprintType::Invalid => (), // ignore - BlueprintType::Book(_) => { + BlueprintType::Book(_book_name) => { + // set order to none in child blueprints + // bp_arr_item.get("order") + // .and_then(|val| val.get()); + // perform recursive write - match Worker::recursive_book_write(bp_arr_item, &new_starting_dir) { - Ok(_) => (), - Err(_) => { - println!("Book write error: {}", &book_details.blueprint_book.label); - } + match Worker::recursive_book_write(prog_tracker, bp_arr_item, &new_starting_dir) { + Ok(()) => prog_tracker.ok(ProgressType::Book(_book_name)), + Err(err_msg) => prog_tracker.error(ProgressType::Book(_book_name), Some(err_msg)) } }, - BlueprintType::Blueprint(sub_bp_name) => { + BlueprintType::Blueprint(_bp_name) => { match Worker::blueprint_write(bp_arr_item, &new_starting_dir) { - Ok(_) => (), - Err(_) => { - println!("Blueprint write error: {}", &sub_bp_name); - } + Ok(()) => prog_tracker.ok(ProgressType::Blueprint(_bp_name)), + Err(err_msg) => prog_tracker.error(ProgressType::Blueprint(_bp_name), Some(err_msg)) } } } diff --git a/src/main.rs b/src/main.rs index 98d80b1..0398bb5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,7 @@ mod common; mod factorio_structs; mod progress; -use std::{fs, process::exit}; +use std::process::exit; use args::*; use clap::Parser; @@ -47,23 +47,10 @@ fn main() { source, outfile } => { - println!( - "TODO: Export JSON tree from {:?} and write blueprint string to {:?}", - &source, - &outfile - ); - - let file_contents = fs::read(source.as_ref().unwrap()).expect("Invalid file path!"); - let blueprint_str = common::factorio_deflate( - std::str::from_utf8(&file_contents).unwrap() - ); - - println!("Blueprint string:\n{:?}", blueprint_str); let export_args = export::Worker { - source: source.clone(), - out_file: outfile.clone(), - source_path: None + source: source.clone().unwrap(), + out_file: outfile.clone() }; export_args.exec(); diff --git a/src/progress.rs b/src/progress.rs index 35d6c55..67e7f7e 100644 --- a/src/progress.rs +++ b/src/progress.rs @@ -1,3 +1,152 @@ //! This module handles writing progress to stdout -//! +use std::{io::Write}; + +use crossterm::{ + terminal, + ExecutableCommand, + QueueableCommand, + cursor +}; +use crossterm::style::Stylize; + +/// Type of subcommand: import or export +pub enum CommandType { + Import, + Export +} + +/// Type of blueprint: book or single blueprint +pub enum ProgressType { + Book(String), + Blueprint(String) +} + +/// Possible errors that can be encountered +pub enum ErrorType { + +} + +/// Progress tracker for data display. +pub struct Tracker { + std_out: std::io::Stdout, + pub command: CommandType, + pub read_blueprints: u16, + pub read_books: u16, + pub errors: u16, +} + +impl Tracker { + pub fn new(command: CommandType) -> Tracker { + + let mut _stdout = std::io::stdout(); + _stdout.execute(cursor::Hide).unwrap(); + + Tracker { + std_out: _stdout, + command, + read_blueprints: 0, + read_books: 0, + errors: 0, + } + } + + /// Called when no error occurs + pub fn ok(&mut self, progress_type: ProgressType) { + let file_name: String; + match progress_type { + ProgressType::Book(_book) => { + file_name = _book; + self.read_books += 1; + }, + ProgressType::Blueprint(_blueprint) => { + file_name = _blueprint; + self.read_blueprints += 1 + } + } + + self.std_out.queue(terminal::Clear(terminal::ClearType::CurrentLine)).unwrap(); + self.std_out.write(format!("{}\t{}\n", "ok".green().bold(), file_name).as_bytes()).unwrap(); + self.std_out.queue(cursor::MoveToPreviousLine(1)).unwrap(); + + self.std_out.flush().unwrap(); + } + + /// Called when encountering an error + pub fn error(&mut self, progress_type: ProgressType, err_msg: Option) { + let file_name: String; + match progress_type { + ProgressType::Book(_book) => { + file_name = _book; + self.read_books += 1; + }, + ProgressType::Blueprint(_blueprint) => { + file_name = _blueprint; + self.read_blueprints += 1 + } + } + self.errors += 1; + + self.std_out.queue(terminal::Clear(terminal::ClearType::CurrentLine)).unwrap(); + self.std_out.write(format!("{}\t{}\n", "err".red().bold(), file_name).as_bytes()).unwrap(); + match err_msg { + Some(message) => { + self.std_out.write(format!("{}\t{}\n", "msg".red().bold(), message).as_bytes()).unwrap(); + }, + None => () + } + self.std_out.queue(cursor::MoveToNextLine(1)).unwrap(); + + self.std_out.flush().unwrap(); + } + + /// Custom error, does not modify internal struct attributes + pub fn error_additional(&mut self, err_msg: String) { + self.std_out.queue(terminal::Clear(terminal::ClearType::CurrentLine)).unwrap(); + self.std_out.write(format!("{}\t{}\n", "err".red().bold(), err_msg).as_bytes()).unwrap(); + self.std_out.queue(cursor::MoveToNextLine(1)).unwrap(); + + self.std_out.flush().unwrap(); + } + + /// Updates stdout with final progress statistics + pub fn complete(&mut self) { + self.std_out.queue(terminal::Clear(terminal::ClearType::CurrentLine)).unwrap(); + self.std_out.write(format!( + "{}\t{}\n{}\t\t{}\n{}\t\t{}\n", + "blueprints".green().bold(), + self.read_blueprints, + "books".green().bold(), + self.read_books, + "errors".green().bold(), + self.errors + ).as_bytes()).unwrap(); + + self.std_out.queue(cursor::Show).unwrap(); + self.std_out.flush().unwrap(); + } +} + +#[cfg(test)] +mod test { + use super::*; + use ProgressType::*; + use std::{thread, time}; + + #[test] + fn progress_loop() { + let mut progress_indicator = Tracker::new(CommandType::Import); + + for i in 1..50 { + if i % 7 == 0 { + progress_indicator.error(Book(format!("what? {}", i)), Some("Idk man some error occured".to_string())); + } else { + progress_indicator.ok(Blueprint(format!("everyting is going well: {}", i))); + } + thread::sleep(time::Duration::from_millis(50)); + } + + progress_indicator.complete(); + } + +} From b0bae122242faffde5cadcecb04acca52d840bbc Mon Sep 17 00:00:00 2001 From: cruzerngz <61926974+cruzerngz@users.noreply.github.com> Date: Fri, 22 Jul 2022 19:15:06 +0800 Subject: [PATCH 03/10] fix file name error dotfiles now contain the correct file name for renamed blueprints --- src/common.rs | 12 +---- src/export.rs | 48 +++++------------- src/import.rs | 134 ++++++++++++++++++++++++++------------------------ 3 files changed, 84 insertions(+), 110 deletions(-) diff --git a/src/common.rs b/src/common.rs index 05b95c4..ef4916b 100644 --- a/src/common.rs +++ b/src/common.rs @@ -1,4 +1,3 @@ -use std::fs; use std::path::Path; use serde_json::Value; @@ -112,7 +111,7 @@ pub fn file_rename(file_name: String) -> String { for character in file_name.chars() { if INVALID_CHARS.contains(character) { - println!("invalid character {}", &character); + // println!("invalid character {}", &character); new_file_name.push('_'); } else { new_file_name.push(character); @@ -121,12 +120,3 @@ pub fn file_rename(file_name: String) -> String { return new_file_name; } - -/// Remove any pretty-printed indentations from the json string -pub fn json_remove_indents(json_indent_str: &str) -> String { - let json_object: serde_json::Value = serde_json::from_str(json_indent_str) - .expect("JSON parse error. Check that blueprint string is valid."); - - return json_object.to_string(); -} - diff --git a/src/export.rs b/src/export.rs index 63eaee2..b9f17c6 100644 --- a/src/export.rs +++ b/src/export.rs @@ -90,17 +90,25 @@ impl Worker { fn read_blueprint(bp_file_path: &PathBuf) -> Result { // println!("reading: {:?}", &bp_file_path); + // let mut bp_mod_path = bp_file_path.clone(); + // for mut sub_path in &mut bp_mod_path.iter() { + // let mut sub_path_string = sub_path.to_str().unwrap().to_string(); + // sub_path_string = common::file_rename(sub_path_string); + + // sub_path = std::ffi::OsStr::new(&sub_path_string); + // } if !bp_file_path.is_file() { - return Err(bp_file_path.to_str().unwrap().to_string()); + println!("{:?}", bp_file_path); + return Err("not a file".to_string()); } match bp_file_path.extension() { None => { - return Err(bp_file_path.to_str().unwrap().to_string()) + return Err("no file extension".to_string()) } Some(file_ext) => { if !file_ext.eq_ignore_ascii_case("json") { - return Err(bp_file_path.to_str().unwrap().to_string()) + return Err("wrong file extension".to_string()) } } } @@ -145,14 +153,14 @@ impl Worker { let dot_file_contents: String; match fs::read_to_string(&dot_file_path) { Ok(_file) => dot_file_contents = _file, - Err(_) => return Err(format!("failed to read {}", &dot_file_path.to_string_lossy())) + Err(_) => return Err("failed to read file".to_string()) } let mut book_object: factorio_structs::BookDotFileHead; match serde_json::from_str(dot_file_contents.as_ref()) { Ok(_book) => book_object = _book, - Err(_) => return Err(format!("failed to deserialize contents of {}", &dot_file_path.to_string_lossy())) + Err(_) => return Err("failed to deserialize contents".to_string()) } book_object.blueprint_book.blueprints = Some(vec![]); @@ -263,36 +271,6 @@ impl Worker { Err("failed to convert typed struct to serde_json::Value".to_string()) } } - // todo!() - // let bp_book_name = bp_book_dir_path.file_name().unwrap(); - // let mut bp_book_file: PathBuf = bp_book_dir_path.clone(); - // bp_book_file.push(bp_book_name); - // bp_book_file.set_extension("json"); - - // let mut bp_book_json: Value; - // if bp_book_file.exists() { - // bp_book_json = serde_json::from_str( - // fs::read_to_string(&bp_book_file) - // .expect("Error reading file") - // .as_str() - // ) - // .expect("Error serializing string"); - // } else { - // return Err(format!("{:?} does not exist", bp_book_name)); - // } - - // // This variable contains the typed vector of UnknownBlueprintType - // // that is used to reconstruct the book - // let bp_book: factorio_structs::Book = serde_json::from_value(bp_book_json.clone()).unwrap(); - - // // the "order" key is consumed internally - // let bp_obj = bp_book_json.as_object_mut() - // .unwrap(); - - // bp_obj.remove("order"); - - - } diff --git a/src/import.rs b/src/import.rs index 04101d8..ee94c2f 100644 --- a/src/import.rs +++ b/src/import.rs @@ -4,7 +4,6 @@ use std::path::PathBuf; use std::process::exit; use std::fs::File; -use crate::factorio_structs::UnknownBlueprintType; use crate::{common, factorio_structs}; use crate::common::{BlueprintType, PathType}; use crate::progress::{self, ProgressType}; @@ -86,13 +85,14 @@ impl Worker { .to_string(); // remove "index" key from the blueprint object - let blueprint_compliant: factorio_structs::BlueprintHead; + let mut blueprint_compliant: factorio_structs::BlueprintHead; match serde_json::from_value(blueprint.to_owned()) { Ok(result) => blueprint_compliant = result, Err(_) => return Err("Error deserializing to compliant blueprint".to_string()) } bp_name = common::file_rename(bp_name); + blueprint_compliant.blueprint.label = bp_name.clone(); full_bp_path.push(&bp_name); full_bp_path.set_extension("json"); @@ -123,97 +123,103 @@ impl Worker { fn recursive_book_write( prog_tracker: &mut progress::Tracker, bp_book: &serde_json::Value, - dir_path: &PathBuf + bp_book_dir: &PathBuf ) -> Result<(), String> { - // println!("Blueprint book full: {:?}", bp_book); - - // Extract out only the relavant blueprint contents - let mut book_details: factorio_structs::BookHead = serde_json::from_value(bp_book.clone()) - .expect("Failed to deserialize blueprint book"); - - let bp_book_name = book_details.blueprint_book.label.clone(); - - // println!("Blueprint book contents: {:?}", &book_details); - - // create new starting dir (for next recursion level) - let mut new_starting_dir = dir_path.clone(); - new_starting_dir.push(&book_details.blueprint_book.label); - match fs::create_dir_all(&new_starting_dir) { - Ok(_) => { - // println!("Created dir {}", &new_starting_dir.to_string_lossy()); + // local_book_copy contains dotfile information + let mut book_dot_file: factorio_structs::BookHead; + match serde_json::from_value(bp_book.clone()) { + Ok(_val) => { + book_dot_file = _val }, - Err(_) => { - // println!("Error creating dir {}", &new_starting_dir.to_string_lossy()); - return Err(format!("error creating dir {}", &new_starting_dir.to_string_lossy())); - } + Err(_) => return Err("failed to deserialize blueprint book".to_string()) } - // write blueprint book details inside starting dir - // blueprint book details are stored in a dotfile that matches the dir name - let mut bp_book_path = dir_path.clone(); - let mut bp_book_name = ".".to_string(); + // remove invalid characters from book by renaming + book_dot_file.blueprint_book.label = common::file_rename(book_dot_file.blueprint_book.label); + // book dotfile name, resides in book directory + let mut book_dot_file_name = ".".to_string(); + book_dot_file_name.push_str(&book_dot_file.blueprint_book.label); + + // new starting dir for next recursion level + let mut current_dir_path = bp_book_dir.clone(); + current_dir_path.push(&book_dot_file.blueprint_book.label); + + // iterator for the contents of dotfile book + // rename all names in dotfile (remove invalid chars) + if let Some(ref mut _order) = book_dot_file.blueprint_book.order { + for _unknown in _order.iter_mut() { + match _unknown.blueprint.as_mut() { + Some(_bp) => { + _bp.label = common::file_rename(_bp.label.clone()); + }, + None => (), + } + match _unknown.blueprint_book.as_mut() { + Some(_book) => { + _book.label = common::file_rename(_book.label.clone()); + }, + None => (), + } + } + } - bp_book_name = common::file_rename(bp_book_name); - bp_book_name.push_str(&book_details.blueprint_book.label); - bp_book_path.push(&book_details.blueprint_book.label); - bp_book_path.push(&bp_book_name); - bp_book_path.set_extension("json"); + // write the dotfile first, then constituent blueprints/books + match fs::create_dir_all(¤t_dir_path) { + Ok(_) => (), + Err(_) => return Err("error creating blueprint book directory".to_string()) + } - let mut bp_book_file = File::create(&bp_book_path) - .expect("File creation error."); + let mut dot_file_path = current_dir_path.clone(); + dot_file_path.push(book_dot_file_name); + dot_file_path.set_extension("json"); + let mut dot_file: File; + match File::create(dot_file_path) { + Ok(_f) => { + dot_file = _f; + }, + Err(_) => return Err("dotfile unable to be created".to_string()) + } - match bp_book_file.write( - serde_json::to_string_pretty(&book_details) - .unwrap() - .as_bytes() + match dot_file.write( + serde_json::to_string_pretty(&book_dot_file) + .unwrap(). + as_bytes() ) { - Ok(_) => { - // println!("Created {}", &bp_book_path.to_string_lossy()); - } - Err(_) => { - // println!("Error creating {}", &bp_book_path.to_string_lossy()); - return Err(format!("error creating {}", &bp_book_path.to_string_lossy())); - } + Ok(_) => {}, + Err(_) => return Err("error writing to dotfile".to_string()), } - book_details.blueprint_book.order; - - // get the arr of blueprints + // get the vec of stuff let book_contents = bp_book.get("blueprint_book") .and_then(|value| value.get("blueprints")) .unwrap(); - book_details.blueprint_book.order = Some(vec!()); - - // recurse + // recurse for all constituent blueprints/books match book_contents { serde_json::Value::Array(bp_arr) => { - for bp_arr_item in bp_arr.iter() { - // store the order of blueprint book items - // bp_book_order = - // then recurse - match BlueprintType::classify(bp_arr_item) { - BlueprintType::Invalid => (), // ignore + for unknown_bp in bp_arr.iter() { + match BlueprintType::classify(&unknown_bp) { + + BlueprintType::Invalid => (), BlueprintType::Book(_book_name) => { - // set order to none in child blueprints - // bp_arr_item.get("order") - // .and_then(|val| val.get()); + // if let Some(bp) = dot_file_book.blueprint_book { + // bp.label = common::file_rename(bp.label.clone()); + // } - // perform recursive write - match Worker::recursive_book_write(prog_tracker, bp_arr_item, &new_starting_dir) { + match Worker::recursive_book_write(prog_tracker, unknown_bp, ¤t_dir_path) { Ok(()) => prog_tracker.ok(ProgressType::Book(_book_name)), Err(err_msg) => prog_tracker.error(ProgressType::Book(_book_name), Some(err_msg)) } }, BlueprintType::Blueprint(_bp_name) => { - match Worker::blueprint_write(bp_arr_item, &new_starting_dir) { + match Worker::blueprint_write(unknown_bp, ¤t_dir_path) { Ok(()) => prog_tracker.ok(ProgressType::Blueprint(_bp_name)), Err(err_msg) => prog_tracker.error(ProgressType::Blueprint(_bp_name), Some(err_msg)) } - } + }, } } } From e809d2e455764b21f9d767cba1a3bb55cab45c6d Mon Sep 17 00:00:00 2001 From: cruzerngz <61926974+cruzerngz@users.noreply.github.com> Date: Fri, 22 Jul 2022 19:25:01 +0800 Subject: [PATCH 04/10] fix windows problem - spaces in file/dir names are now also replaced with underscores --- src/common.rs | 2 +- src/import.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common.rs b/src/common.rs index ef4916b..3ae68dd 100644 --- a/src/common.rs +++ b/src/common.rs @@ -4,7 +4,7 @@ use serde_json::Value; use crate::factorio_structs; -pub const INVALID_CHARS: &str = r#"/\<>:"|?*"#; +pub const INVALID_CHARS: &str = r#" /\<>:"|?*"#; /// Various types of paths the program may encounter #[derive(Debug)] diff --git a/src/import.rs b/src/import.rs index ee94c2f..9851882 100644 --- a/src/import.rs +++ b/src/import.rs @@ -174,7 +174,7 @@ impl Worker { dot_file_path.push(book_dot_file_name); dot_file_path.set_extension("json"); let mut dot_file: File; - match File::create(dot_file_path) { + match File::create(&dot_file_path) { Ok(_f) => { dot_file = _f; }, From 9540bb0dc9d859874aa0173e60fddd8561f13cae Mon Sep 17 00:00:00 2001 From: cruzerngz <61926974+cruzerngz@users.noreply.github.com> Date: Fri, 22 Jul 2022 21:39:15 +0800 Subject: [PATCH 05/10] Update export.rs - change export file prefix --- src/export.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/export.rs b/src/export.rs index b9f17c6..f56c014 100644 --- a/src/export.rs +++ b/src/export.rs @@ -8,6 +8,9 @@ use crate::factorio_structs; use crate::common; use crate::progress::{self, ProgressType}; +/// Prefix for exported blueprints +const PREFIX_OUT: &str = "fbpconvert-bp_"; + pub struct Worker { pub source: String, pub out_file: Option, @@ -296,7 +299,7 @@ impl Worker { destination = name; } } - destination = format!("blueprint_{}", destination); + destination = format!("{}{}",PREFIX_OUT, destination); } } From 36e9f3e387c3bf01f20cb8c9e77bd92ca607e9aa Mon Sep 17 00:00:00 2001 From: cruzerngz <61926974+cruzerngz@users.noreply.github.com> Date: Fri, 22 Jul 2022 23:37:09 +0800 Subject: [PATCH 06/10] add tests - remove all .expect() clauses - add some tests for common.rs --- src/common.rs | 62 +++++++++++++++++++++++++++++++++++++++++++++++++ src/export.rs | 19 ++++----------- src/import.rs | 30 ++++++++++++++++++------ src/progress.rs | 2 +- 4 files changed, 91 insertions(+), 22 deletions(-) diff --git a/src/common.rs b/src/common.rs index 3ae68dd..c36161c 100644 --- a/src/common.rs +++ b/src/common.rs @@ -120,3 +120,65 @@ pub fn file_rename(file_name: String) -> String { return new_file_name; } + +#[cfg(test)] +mod test { + use super::*; + use serde_json::json; + + #[test] + fn test_classify_invalid_empty() { + assert!(matches!( + BlueprintType::classify( + &json!({}) + ), + BlueprintType::Invalid + )); + } + + #[test] + fn test_classify_invalid_nonsense() { + assert!(matches!( + BlueprintType::classify( + &json!({ + "blueprints": { + "asd": "xyz" + } + }) + ), + BlueprintType::Invalid + )); + } + + #[test] + fn test_classify_valid_bp() { + assert!(matches!( + BlueprintType::classify( + &json!({ + "blueprint": { + "item": "asd", + "label": "blueprint_thang", + "version": 1234567890 + } + }) + ), + BlueprintType::Blueprint(_) + )); + } + #[test] + fn test_classify_valid_book() { + assert!(matches!( + BlueprintType::classify( + &json!({ + "blueprint_book": { + "item": "asd", + "label": "blueprint_thang", + "active_index": 0, + "version": 1234567890 + } + }) + ), + BlueprintType::Book(_) + )); + } +} diff --git a/src/export.rs b/src/export.rs index f56c014..2f56f52 100644 --- a/src/export.rs +++ b/src/export.rs @@ -22,7 +22,6 @@ impl Worker { /// Main calling method for struct pub fn exec(&self) { let source_path = PathBuf::from(&self.source); - let root_bp_dir = source_path.file_name(); let mut progress_tracker = progress::Tracker::new(progress::CommandType::Export); let mut read_json_value = serde_json::json!({}); @@ -92,15 +91,6 @@ impl Worker { /// Returns an error message if an error occurs fn read_blueprint(bp_file_path: &PathBuf) -> Result { - // println!("reading: {:?}", &bp_file_path); - // let mut bp_mod_path = bp_file_path.clone(); - // for mut sub_path in &mut bp_mod_path.iter() { - // let mut sub_path_string = sub_path.to_str().unwrap().to_string(); - // sub_path_string = common::file_rename(sub_path_string); - - // sub_path = std::ffi::OsStr::new(&sub_path_string); - // } - if !bp_file_path.is_file() { println!("{:?}", bp_file_path); return Err("not a file".to_string()); @@ -123,8 +113,11 @@ impl Worker { match bp_file { Ok(file_contents) => { - let json_contents: serde_json::Value = serde_json::from_str(&file_contents.as_str()) - .expect("failed to serialize JSON data."); + let json_contents: serde_json::Value; + match serde_json::from_str(&file_contents.as_str()) { + Ok(_contents) => json_contents = _contents, + Err(_) => return Err("failed to serialize blueprint data".to_string()), + } return Ok(json_contents); }, @@ -318,7 +311,5 @@ impl Worker { Err(_) => return Err("serde_json serialize error".to_string()) } - - // todo!(); } } diff --git a/src/import.rs b/src/import.rs index 9851882..8795e7f 100644 --- a/src/import.rs +++ b/src/import.rs @@ -44,8 +44,19 @@ impl Worker { let mut progress_tracker = progress::Tracker::new(progress::CommandType::Import); // convert the string to a json value - let blueprint_obj: serde_json::Value = serde_json::from_str(blueprint_inflated.as_str()) - .expect("JSON parse error. Check that the blueprint string is valid."); + let blueprint_obj: serde_json::Value; + match serde_json::from_str(blueprint_inflated.as_str()) { + Ok(_obj) => { + blueprint_obj = _obj; + }, + Err(_) => { + progress_tracker.error_additional( + "json parse error. check if blueprint string is valid".to_string() + ); + progress_tracker.complete(); + exit(1); + }, + } // let blueprint_file_name: String; match BlueprintType::classify(&blueprint_obj) { @@ -97,8 +108,16 @@ impl Worker { full_bp_path.push(&bp_name); full_bp_path.set_extension("json"); - let mut bp_file = File::create(&full_bp_path) - .expect("File creation error. Check the file path given"); + let mut bp_file: File; + + match File::create(&full_bp_path) { + Ok(_file) => { + bp_file = _file + }, + Err(_) => { + return Err("file creation error. check the file path".to_string()) + }, + } match bp_file.write( serde_json::to_string_pretty(&blueprint_compliant) @@ -204,9 +223,6 @@ impl Worker { BlueprintType::Invalid => (), BlueprintType::Book(_book_name) => { - // if let Some(bp) = dot_file_book.blueprint_book { - // bp.label = common::file_rename(bp.label.clone()); - // } match Worker::recursive_book_write(prog_tracker, unknown_bp, ¤t_dir_path) { Ok(()) => prog_tracker.ok(ProgressType::Book(_book_name)), diff --git a/src/progress.rs b/src/progress.rs index 67e7f7e..d1f2297 100644 --- a/src/progress.rs +++ b/src/progress.rs @@ -143,7 +143,7 @@ mod test { } else { progress_indicator.ok(Blueprint(format!("everyting is going well: {}", i))); } - thread::sleep(time::Duration::from_millis(50)); + thread::sleep(time::Duration::from_millis(15)); } progress_indicator.complete(); From 769437c5eb7161bed479d73c86647173f4ce799d Mon Sep 17 00:00:00 2001 From: cruzerngz <61926974+cruzerngz@users.noreply.github.com> Date: Sun, 24 Jul 2022 00:13:53 +0800 Subject: [PATCH 07/10] shorten progress tracker printout - shorten full path -> final path component --- src/export.rs | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/export.rs b/src/export.rs index 2f56f52..d1bec94 100644 --- a/src/export.rs +++ b/src/export.rs @@ -180,20 +180,24 @@ impl Worker { Ok(_bp_obj) => { known_bp_object = Some(_bp_obj); prog_tracker.ok( - ProgressType::Blueprint(known_bp_path - .to_str() - .unwrap() - .to_string() + ProgressType::Blueprint( + known_bp_path.file_name() + .unwrap() + .to_str() + .unwrap() + .to_string() ) ) }, Err(err_msg) => { known_bp_object = None; prog_tracker.error( - ProgressType::Blueprint(known_bp_path - .to_str() - .unwrap() - .to_string() + ProgressType::Blueprint( + known_bp_path.file_name() + .unwrap() + .to_str() + .unwrap() + .to_string() ), Some(err_msg) ) @@ -223,7 +227,8 @@ impl Worker { Ok(_book_obj) => { known_book_object = Some(_book_obj); prog_tracker.ok(ProgressType::Book( - known_book_path + known_book_path.file_name() + .unwrap() .to_str() .unwrap() .to_string() @@ -233,7 +238,8 @@ impl Worker { Err(err_msg) => { known_book_object = None; prog_tracker.error(ProgressType::Book( - known_book_path + known_book_path.file_name() + .unwrap() .to_str() .unwrap() .to_string() From 60576b94bf3b6faaab13f1f8e4081d01bb285c63 Mon Sep 17 00:00:00 2001 From: cruzerngz Date: Mon, 25 Jul 2022 16:10:41 +0800 Subject: [PATCH 08/10] preserve index (non consecutive) - preserves not only the order, but index of blueprints. - blueprint books with gaps (jumps in index of its blueprints by >1) are now preserved when doing an import -> export loop - now compatible with teoxoy's FBE for exported books and blueprints --- src/common.rs | 15 +++++++++-- src/export.rs | 4 +-- src/factorio_structs.rs | 57 +++++++++++++++++++++++------------------ src/import.rs | 17 +++++++----- 4 files changed, 58 insertions(+), 35 deletions(-) diff --git a/src/common.rs b/src/common.rs index c36161c..febcd76 100644 --- a/src/common.rs +++ b/src/common.rs @@ -45,8 +45,19 @@ impl BlueprintType { /// Determines the blueprint type, returning an enum with the enclosing blueprint's name pub fn classify(given_bp: &Value) -> BlueprintType { - let unknown_bp_type:factorio_structs::UnknownBlueprintType = serde_json::from_value(given_bp.clone()) - .expect("failed to serialize unknown blueprint type"); + // let unknown_bp_type:factorio_structs::UnknownBlueprintType = serde_json::from_value(given_bp.clone()) + // .expect("failed to serialize unknown blueprint type"); + + let unknown_bp_type: factorio_structs::UnknownBlueprintType; + match serde_json::from_value(given_bp.clone()) { + Ok(_val) => { + unknown_bp_type = _val; + }, + Err(e) => { + println!("Error: {}", e); + return BlueprintType::Invalid + }, + } match unknown_bp_type.blueprint_book { Some(bp_book) => { diff --git a/src/export.rs b/src/export.rs index d1bec94..2dbbdfc 100644 --- a/src/export.rs +++ b/src/export.rs @@ -4,7 +4,7 @@ use std::process::exit; use serde_json::Value; -use crate::factorio_structs; +use crate::factorio_structs::exportable; use crate::common; use crate::progress::{self, ProgressType}; @@ -152,7 +152,7 @@ impl Worker { Err(_) => return Err("failed to read file".to_string()) } - let mut book_object: factorio_structs::BookDotFileHead; + let mut book_object: exportable::BookDotFileHead; match serde_json::from_str(dot_file_contents.as_ref()) { Ok(_book) => book_object = _book, diff --git a/src/factorio_structs.rs b/src/factorio_structs.rs index add494c..534502d 100644 --- a/src/factorio_structs.rs +++ b/src/factorio_structs.rs @@ -33,6 +33,23 @@ pub mod fragments { pub mod importable { use super::*; + /// Head of the blueprint book + #[derive(Serialize, Deserialize, Debug)] + pub struct BookHead { //used in import + pub blueprint_book: Book, + + #[serde(skip_serializing_if = "Option::is_none")] + pub index: Option + } + + /// Head of blueprint, to factorio spec + #[derive(Serialize, Deserialize, Debug)] + pub struct BlueprintHead { // used in import + pub blueprint: Blueprint, + + #[serde(skip_serializing_if = "Option::is_none")] + pub index: Option + } } /// Structs that are valid for use in export only. @@ -40,31 +57,18 @@ pub mod importable { pub mod exportable { use super::*; -} - -/// Head of the blueprint book -#[derive(Serialize, Deserialize, Debug)] -pub struct BookHead { - pub blueprint_book: Book -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct BookDotFileHead { - pub blueprint_book: BookDotFile -} - -/// Head of blueprint, to factorio spec -#[derive(Serialize, Deserialize, Debug)] -pub struct BlueprintHead { - pub blueprint: Blueprint, + #[derive(Serialize, Deserialize, Debug)] + pub struct BookDotFileHead { //used in export + pub blueprint_book: BookDotFile, - #[serde(skip_serializing)] - pub index: u16 + #[serde(skip_serializing_if = "Option::is_none")] + pub index: Option + } } /// Blueprint book with additional parameter containing the order of it's child blueprints #[derive(Serialize, Deserialize, Debug)] -pub struct Book { +pub struct Book { // used internally pub item: Option, pub label: String, pub label_color: Option, @@ -83,7 +87,7 @@ pub struct Book { } #[derive(Serialize, Deserialize, Debug)] -pub struct BookDotFile { +pub struct BookDotFile { //used internally #[serde(skip_serializing_if = "Option::is_none")] pub item: Option, pub label: String, @@ -103,7 +107,7 @@ pub struct BookDotFile { } #[derive(Serialize, Deserialize, Debug)] -pub struct Blueprint { +pub struct Blueprint { //used internally #[serde(skip_serializing_if = "Option::is_none")] pub item: Option, pub label: String, @@ -126,7 +130,7 @@ pub struct Blueprint { } #[derive(Serialize, Deserialize, Debug)] -pub struct Color { +pub struct Color { //used internally pub r: f32, pub g: f32, pub b: f32, @@ -134,10 +138,13 @@ pub struct Color { } #[derive(Serialize, Deserialize, Debug)] -pub struct UnknownBlueprintType { +pub struct UnknownBlueprintType { //used in common #[serde(skip_serializing_if = "Option::is_none")] pub blueprint_book: Option, #[serde(skip_serializing_if = "Option::is_none")] - pub blueprint: Option + pub blueprint: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub index: Option } diff --git a/src/import.rs b/src/import.rs index 8795e7f..14d645d 100644 --- a/src/import.rs +++ b/src/import.rs @@ -5,7 +5,8 @@ use std::process::exit; use std::fs::File; use crate::{common, factorio_structs}; -use crate::common::{BlueprintType, PathType}; +use factorio_structs::importable; +use crate::common::BlueprintType; use crate::progress::{self, ProgressType}; pub struct Worker { @@ -18,10 +19,15 @@ impl Worker { /// Main calling method for struct pub fn exec(&self) { + + // create new progress tracker instance + let mut progress_tracker = progress::Tracker::new(progress::CommandType::Import); + // make the destination dir (if it doesnt exist) match fs::create_dir_all(&self.dest) { Err(_) => { println!("Error creating deestination directory!"); + progress_tracker.complete(); exit(1); }, Ok(_) => () @@ -36,13 +42,11 @@ impl Worker { }, Err(e) => { println!("{}", e); + progress_tracker.complete(); exit(1); } } - // create new progress tracker instance - let mut progress_tracker = progress::Tracker::new(progress::CommandType::Import); - // convert the string to a json value let blueprint_obj: serde_json::Value; match serde_json::from_str(blueprint_inflated.as_str()) { @@ -62,6 +66,7 @@ impl Worker { match BlueprintType::classify(&blueprint_obj) { BlueprintType::Invalid => { println!("Invalid blueprint!"); + progress_tracker.complete(); exit(1); } BlueprintType::Blueprint(_bp_name) => { @@ -96,7 +101,7 @@ impl Worker { .to_string(); // remove "index" key from the blueprint object - let mut blueprint_compliant: factorio_structs::BlueprintHead; + let mut blueprint_compliant: importable::BlueprintHead; match serde_json::from_value(blueprint.to_owned()) { Ok(result) => blueprint_compliant = result, Err(_) => return Err("Error deserializing to compliant blueprint".to_string()) @@ -146,7 +151,7 @@ impl Worker { ) -> Result<(), String> { // local_book_copy contains dotfile information - let mut book_dot_file: factorio_structs::BookHead; + let mut book_dot_file: importable::BookHead; match serde_json::from_value(bp_book.clone()) { Ok(_val) => { book_dot_file = _val From 268854e0e9bb8e5d07f0a3fe42f10da66c85acc6 Mon Sep 17 00:00:00 2001 From: cruzerngz Date: Mon, 25 Jul 2022 17:00:46 +0800 Subject: [PATCH 09/10] add more export flags - modify destination dir - modify file name --- src/args.rs | 10 +++++++--- src/export.rs | 45 +++++++++++++++++++++++---------------------- src/main.rs | 6 ++++-- src/progress.rs | 6 +++--- 4 files changed, 37 insertions(+), 30 deletions(-) diff --git a/src/args.rs b/src/args.rs index b7c2cbe..5fd9322 100644 --- a/src/args.rs +++ b/src/args.rs @@ -24,7 +24,7 @@ pub enum SubCommands { #[clap(value_parser)] infile: Option, - /// Destination directory (optional), for blueprint books/single blueprints + /// Destination directory (optional) #[clap(short, long)] destination: Option }, @@ -36,8 +36,12 @@ pub enum SubCommands { #[clap(value_parser)] source: Option, - /// Outfile (optional), containing blueprint string + /// Outfile name (optional) #[clap(short, long)] - outfile: Option + outfile: Option, + + /// Destination directory (optional) + #[clap(short, long)] + destination: Option } } diff --git a/src/export.rs b/src/export.rs index 2dbbdfc..6d188b2 100644 --- a/src/export.rs +++ b/src/export.rs @@ -14,16 +14,16 @@ const PREFIX_OUT: &str = "fbpconvert-bp_"; pub struct Worker { pub source: String, pub out_file: Option, - // pub source_path: Option + pub dest: Option } impl Worker { /// Main calling method for struct pub fn exec(&self) { - let source_path = PathBuf::from(&self.source); - let mut progress_tracker = progress::Tracker::new(progress::CommandType::Export); + + let source_path = PathBuf::from(&self.source); let mut read_json_value = serde_json::json!({}); match source_path.extension() { @@ -276,38 +276,39 @@ impl Worker { } - /// Takes the blueprint and writes it to a destination + /// Takes the blueprint and writes it to a destination. /// Returns an error message if it occurs pub fn write_blueprint_to_file( &self, blueprint_json: &Value, ) -> Result<(),String> { - let mut destination: String; - match &self.out_file { - Some(dest) => destination = dest.to_owned(), - None => { - match common::BlueprintType::classify(&blueprint_json) { - common::BlueprintType::Invalid => { - return Err("failed to determine blueprint type".to_string()) - }, - common::BlueprintType::Blueprint(name) => { - destination = name; - }, - common::BlueprintType::Book(name) => { - destination = name; - } + let mut write_dest: PathBuf = PathBuf::new(); + if let Some(_dir) = &self.dest { + write_dest.push(_dir); + } + if let Some(_file) = &self.out_file { + write_dest.push(_file); + } else { + let file_name: String; + match common::BlueprintType::classify(&blueprint_json) { + common::BlueprintType::Invalid => { + return Err("failed to determine blueprint type".to_string()) + }, + common::BlueprintType::Blueprint(name) => { + file_name = name; + }, + common::BlueprintType::Book(name) => { + file_name = name; } - destination = format!("{}{}",PREFIX_OUT, destination); } + write_dest.push(format!("{}{}",PREFIX_OUT, file_name)); } - // println!("dest: {}", destination); - match serde_json::to_string(blueprint_json) { Ok(blueprint_string) => { let blueprint_string_deflated = common::factorio_deflate(blueprint_string.as_ref()); - match fs::write(destination, blueprint_string_deflated.as_bytes()) { + match fs::write(write_dest, blueprint_string_deflated.as_bytes()) { Ok(_) => {return Ok(())}, Err(_) => { return Err("file write error".to_string()); diff --git a/src/main.rs b/src/main.rs index 0398bb5..b0fcc6d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -45,12 +45,14 @@ fn main() { SubCommands::Export { source, - outfile + outfile, + destination } => { let export_args = export::Worker { source: source.clone().unwrap(), - out_file: outfile.clone() + out_file: outfile.clone(), + dest: destination.clone() }; export_args.exec(); diff --git a/src/progress.rs b/src/progress.rs index d1f2297..0877cd9 100644 --- a/src/progress.rs +++ b/src/progress.rs @@ -22,10 +22,10 @@ pub enum ProgressType { Blueprint(String) } -/// Possible errors that can be encountered -pub enum ErrorType { +// /// Possible errors that can be encountered +// pub enum ErrorType { -} +// } /// Progress tracker for data display. pub struct Tracker { From c84b2c017c7ace6a8dd9e2df14c3df7f48f81dc6 Mon Sep 17 00:00:00 2001 From: cruzerngz <61926974+cruzerngz@users.noreply.github.com> Date: Mon, 25 Jul 2022 17:59:19 +0800 Subject: [PATCH 10/10] Update factorio_structs.rs - add descriptions for each blueprint --- src/factorio_structs.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/factorio_structs.rs b/src/factorio_structs.rs index 534502d..be2f487 100644 --- a/src/factorio_structs.rs +++ b/src/factorio_structs.rs @@ -11,6 +11,8 @@ pub mod fragments { /// Blueprint parameters except arrays #[derive(Serialize, Deserialize, Debug)] pub struct BlueprintFragment { + #[serde(skip_serializing_if = "Option::is_none")] + pub description: Option, pub item: Option, pub label: String, pub label_color: Option, @@ -20,6 +22,8 @@ pub mod fragments { /// Blueprint book #[derive(Serialize, Deserialize, Debug)] pub struct BookFragment { + #[serde(skip_serializing_if = "Option::is_none")] + pub description: Option, pub item: Option, pub label: String, pub label_color: Option, @@ -69,6 +73,8 @@ pub mod exportable { /// Blueprint book with additional parameter containing the order of it's child blueprints #[derive(Serialize, Deserialize, Debug)] pub struct Book { // used internally + #[serde(skip_serializing_if = "Option::is_none")] + pub description: Option, pub item: Option, pub label: String, pub label_color: Option, @@ -88,6 +94,8 @@ pub struct Book { // used internally #[derive(Serialize, Deserialize, Debug)] pub struct BookDotFile { //used internally + #[serde(skip_serializing_if = "Option::is_none")] + pub description: Option, #[serde(skip_serializing_if = "Option::is_none")] pub item: Option, pub label: String, @@ -108,6 +116,8 @@ pub struct BookDotFile { //used internally #[derive(Serialize, Deserialize, Debug)] pub struct Blueprint { //used internally + #[serde(skip_serializing_if = "Option::is_none")] + pub description: Option, #[serde(skip_serializing_if = "Option::is_none")] pub item: Option, pub label: String,