From a91bd3bafc28be96d778b665ee35aeed812cc095 Mon Sep 17 00:00:00 2001 From: ScuroNeko Date: Mon, 20 Oct 2025 12:34:04 +0300 Subject: [PATCH] v0.3 --- Cargo.lock | 245 ++++++++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 5 + README.md | 15 +++ src/utils.rs | 187 ++++++++++++++++++++++++++++++++++--- src/zsdiff.rs | 40 ++++---- src/zspatch.rs | 125 ++++++++++++++++++++----- 6 files changed, 560 insertions(+), 57 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 90405f9..ea3cd4b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -52,6 +52,24 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "base16ct" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b59d472eab27ade8d770dcb11da7201c11234bef9f82ce7aa517be028d462b" + +[[package]] +name = "base64ct" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" + [[package]] name = "bytes" version = "1.10.1" @@ -76,6 +94,17 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "chacha20" +version = "0.10.0-rc.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd162f2b8af3e0639d83f28a637e4e55657b7a74508dba5a9bf4da523d5c9e9" +dependencies = [ + "cfg-if", + "cpufeatures", + "rand_core", +] + [[package]] name = "clap" version = "4.5.49" @@ -122,6 +151,75 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" +[[package]] +name = "const-oid" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dabb6555f92fb9ee4140454eb5dcd14c7960e1225c6d1a6cc361f032947713e" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-bigint" +version = "0.7.0-rc.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4b0fda9462026d53a3ef37c5ec283639ee8494a1a5401109c0e2a3fb4d490c" +dependencies = [ + "num-traits", + "rand_core", + "serdect", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.2.0-rc.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8235645834fbc6832939736ce2f2d08192652269e11010a6240f61b908a1c6" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "crypto-primes" +version = "0.7.0-pre.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25f2523fbb68811c8710829417ad488086720a6349e337c38d12fa81e09e50bf" +dependencies = [ + "crypto-bigint", + "libm", + "rand_core", +] + +[[package]] +name = "der" +version = "0.8.0-rc.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9d8dd2f26c86b27a2a8ea2767ec7f9df7a89516e4794e54ac01ee618dda3aa4" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "digest" +version = "0.11.0-rc.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dac89f8a64533a9b0eaa73a68e424db0fb1fd6271c74cc0125336a05f090568d" +dependencies = [ + "const-oid", + "crypto-common", +] + [[package]] name = "find-msvc-tools" version = "0.1.4" @@ -146,6 +244,15 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hybrid-array" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f471e0a81b2f90ffc0cb2f951ae04da57de8baa46fa99112b062a5173a5088d0" +dependencies = [ + "typenum", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -174,6 +281,12 @@ version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" +[[package]] +name = "libm" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" + [[package]] name = "md5" version = "0.8.0" @@ -186,18 +299,56 @@ version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "once_cell_polyfill" version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" +[[package]] +name = "pem-rfc7468" +version = "1.0.0-rc.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8e58fab693c712c0d4e88f8eb3087b6521d060bcaf76aeb20cb192d809115ba" +dependencies = [ + "base64ct", +] + [[package]] name = "pin-project-lite" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +[[package]] +name = "pkcs1" +version = "0.8.0-rc.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "986d2e952779af96ea048f160fd9194e1751b4faea78bcf3ceb456efe008088e" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.11.0-rc.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93eac55f10aceed84769df670ea4a32d2ffad7399400d41ee1c13b1cd8e1b478" +dependencies = [ + "der", + "spki", +] + [[package]] name = "pkg-config" version = "0.3.32" @@ -228,6 +379,44 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "rand" +version = "0.10.0-rc.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ec474812b9de55111b29da8a1559f1718ef3dc20fa36f031f1b5d9e3836ad6c" +dependencies = [ + "chacha20", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rsa" +version = "0.10.0-rc.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf8955ab399f6426998fde6b76ae27233cce950705e758a6c17afd2f6d0e5d52" +dependencies = [ + "const-oid", + "crypto-bigint", + "crypto-primes", + "digest", + "pkcs1", + "pkcs8", + "rand_core", + "signature", + "spki", + "subtle", + "zeroize", +] + [[package]] name = "ryu" version = "1.0.20" @@ -286,12 +475,42 @@ dependencies = [ "serde_core", ] +[[package]] +name = "serdect" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3ef0e35b322ddfaecbc60f34ab448e157e48531288ee49fafbb053696b8ffe2" +dependencies = [ + "base16ct", + "serde", +] + [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signature" +version = "3.0.0-rc.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc280a6ff65c79fbd6622f64d7127f32b85563bca8c53cd2e9141d6744a9056d" +dependencies = [ + "digest", + "rand_core", +] + +[[package]] +name = "spki" +version = "0.8.0-rc.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8baeff88f34ed0691978ec34440140e1572b68c7dd4a495fd14a3dc1944daa80" +dependencies = [ + "base64ct", + "der", +] + [[package]] name = "strsim" version = "0.11.1" @@ -299,10 +518,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] -name = "syn" -version = "2.0.106" +name = "subtle" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a26dbd934e5451d21ef060c018dae56fc073894c5a7896f882928a76e6d081b" dependencies = [ "proc-macro2", "quote", @@ -331,6 +556,12 @@ dependencies = [ "syn", ] +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + [[package]] name = "unicode-ident" version = "1.0.19" @@ -466,12 +697,20 @@ version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + [[package]] name = "zsdiff_all" version = "0.1.0" dependencies = [ "clap", "md5", + "rand", + "rsa", "serde", "serde_json", "tokio", diff --git a/Cargo.toml b/Cargo.toml index 65d0b3b..75d45e5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,9 @@ name = "zsdiff_all" version = "0.1.0" edition = "2024" +[profile.dev] +opt-level = 3 + [[bin]] name = "zsdiff" path = "src/zsdiff.rs" @@ -19,3 +22,5 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" walkdir = "2.5" clap = { version = "4.5", features = ["derive"] } +rsa = { version = "0.10.0-rc.9", features = ["std"] } +rand = { version = "0.10.0-rc.0", features = ["thread_rng", "std"] } diff --git a/README.md b/README.md index d447e78..db11428 100644 --- a/README.md +++ b/README.md @@ -27,4 +27,19 @@ Options: -d, --dest-dir -h, --hash-check --help Print help +``` + +``` +metadata version: uint16 (2 bytes) +metadata size: uint32 (4 bytes) +n = 6 +metadata content: bytes (n;m bytes) +m = n+6 +(content -> rsa -> zstd (bytes) -> bytes) (m;o) +content entry: { + size: uint32 (4 bytes) + i = 4 + j = i + size + content: (i;j bytes) +} ``` \ No newline at end of file diff --git a/src/utils.rs b/src/utils.rs index dd8c849..452f4d3 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,12 +1,23 @@ +use crate::utils; use md5; +use rsa::pkcs1::{DecodeRsaPrivateKey, EncodeRsaPrivateKey, EncodeRsaPublicKey}; +use rsa::pkcs8::LineEnding; +use rsa::traits::PublicKeyParts; +use rsa::{Pkcs1v15Encrypt, RsaPrivateKey, RsaPublicKey}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; -use std::{fs, io}; +use std::io; +use tokio::fs; +use tokio::io::AsyncWriteExt; use zstd::{Decoder, Encoder}; const METADATA_VERSION: u16 = 1; const SUPPORTED_VERSION: [u16; 1] = [1]; +pub fn is_metadata_supported(version: u16) -> bool { + SUPPORTED_VERSION.contains(&version) +} + pub struct Zsdiff { pub content: HashMap>, pub metadata: Metadata, @@ -31,8 +42,27 @@ impl Zsdiff { index += meta_size; println!(">>> File count: {}", metadata.file_count); - let data = _data; + let mut data = _data[index..].to_vec(); + if metadata.encrypted { + println!(">>> Decrypting file"); + let key_content = fs::read("./private.pem").await?; + let private_key = RsaPrivateKey::from_pkcs1_pem( + String::from_utf8(key_content) + .expect("Can't load key") + .as_str(), + ) + .expect("Can't load private key"); + let encrypter = Encrypter::from_private_key(private_key); + data = encrypter.decrypt(data).await; + println!(">>> Decrypting done"); + } + println!(">>> Decompressing file"); + let compressor = Compressor::new(); + data = compressor.decompress(data).await?; + println!(">>> Decompressing done"); + + index = 0; let mut content = HashMap::new(); while index < data.len() { let filename_size = @@ -52,12 +82,11 @@ impl Zsdiff { Ok(Zsdiff { content, metadata }) } - pub async fn to_vec(&self) -> Vec { + pub async fn to_vec(&self, compress_level: i32, encrypt: bool) -> Vec { let mut meta_bytes: Vec = Vec::new(); - - meta_bytes.extend(METADATA_VERSION.to_be_bytes()); + meta_bytes.extend(METADATA_VERSION.to_be_bytes()); // u16 let meta = serde_json::to_vec(&self.metadata).unwrap(); - meta_bytes.extend((meta.len() as u32).to_be_bytes()); + meta_bytes.extend((meta.len() as u32).to_be_bytes()); // u32 meta_bytes.extend(meta); let mut parts: Vec = Vec::new(); @@ -71,9 +100,43 @@ impl Zsdiff { parts.extend(part) } + let size_before = parts.len(); + parts = self.compress(parts, compress_level).await; + if encrypt { + parts = self.encrypt(parts).await; + } + + let size_after = parts.len(); + println!(">>> Size before: {:.1?}KB", size_before / 1024); + println!(">>> Size after: {:.1?}KB", size_after / 1024); + println!( + ">>> Compress ratio: {:.2?}%", + size_after as f64 / size_before as f64 * 100.0 + ); + let out = vec![meta_bytes, parts].concat(); out } + + async fn compress(&self, data: Vec, level: i32) -> Vec { + let compressor = Compressor::new(); + println!(">>> Compressing"); + let _data = compressor + .compress(data, level) + .await + .expect("Can't compress data"); + println!(">>> Compressing done"); + _data + } + + async fn encrypt(&self, data: Vec) -> Vec { + println!(">>> Encrypting"); + let encrypter = Encrypter::new_pair(); + let _data = encrypter.encrypt(data).await.expect("Can't encrypt data"); + encrypter.export().await.expect("Can't export keys"); + println!(">>> Encrypting done"); + _data + } } #[derive(Serialize, Deserialize, Debug)] @@ -84,6 +147,7 @@ pub struct Metadata { pub remove_folders: Vec, pub file_count: u32, pub compress_level: i32, + pub encrypted: bool, } pub async fn get_hash(data: Vec) -> String { @@ -91,15 +155,108 @@ pub async fn get_hash(data: Vec) -> String { format!("{:x}", hash) } -pub async fn compress(input: Vec, output: &fs::File, level: i32) { - let mut encoder = Encoder::new(output, level).unwrap(); - io::copy(&mut input.as_slice(), &mut encoder).unwrap(); - encoder.finish().unwrap(); +struct Compressor {} +impl Compressor { + pub(crate) fn new() -> Self { + Compressor {} + } + + pub async fn compress(&self, input: Vec, level: i32) -> Result, io::Error> { + let buf = Vec::new(); + let mut encoder = Encoder::new(buf, level)?; + io::copy(&mut &input[..], &mut encoder)?; + encoder.finish() + } + + pub async fn decompress(&self, input: Vec) -> Result, io::Error> { + let mut decoder = Decoder::new(&input[..])?; + let mut buf = Vec::new(); + io::copy(&mut decoder, &mut buf)?; + Ok(buf) + } } -pub async fn decompress(input: Vec) -> Result, io::Error> { - let mut decoder = Decoder::new(&input[..])?; - let mut buf = Vec::new(); - io::copy(&mut decoder, &mut buf)?; - Ok(buf) +pub struct Encrypter { + private_key: RsaPrivateKey, + public_key: RsaPublicKey, + key_size: usize, +} +impl Encrypter { + pub fn new_pair() -> Self { + let mut rng = rand::rng(); + let bits = 2048; + let private_key = RsaPrivateKey::new(&mut rng, bits).expect("failed to generate a key"); + let public_key = RsaPublicKey::from(&private_key); + Self { + private_key, + public_key, + key_size: bits, + } + } + + pub fn from_private_key(private_key: RsaPrivateKey) -> Self { + let public_key = RsaPublicKey::from(&private_key); + Self { + key_size: public_key.size(), + public_key, + private_key, + } + } + + pub fn new(private_key: RsaPrivateKey, public_key: RsaPublicKey) -> Self { + Self { + key_size: public_key.size(), + private_key, + public_key, + } + } + + pub async fn encrypt(&self, data: Vec) -> Result, io::Error> { + let mut rng = rand::rng(); + let mut out = Vec::new(); + let segment_size = self.public_key.size() - 11; + + for seg in data.chunks(segment_size) { + let segment = self + .public_key + .encrypt(&mut rng, Pkcs1v15Encrypt, seg) + .unwrap(); + out.extend(segment) + } + Ok(out) + } + + pub async fn decrypt(&self, data: Vec) -> Vec { + let mut out = Vec::new(); + let segment_size = self.public_key.size(); + for seg in data.chunks(segment_size) { + let segment = self + .private_key + .decrypt(Pkcs1v15Encrypt, seg) + .expect("failed to decrypt"); + out.extend(segment) + } + out + } + + pub async fn export(&self) -> Result<(), io::Error> { + let private_bytes = self + .private_key + .to_pkcs1_pem(LineEnding::CRLF) + .expect("failed to pem private key"); + let public_bytes = self + .public_key + .to_pkcs1_pem(LineEnding::CRLF) + .expect("failed to pem public key"); + + fs::File::create("private.pem") + .await? + .write_all(private_bytes.as_bytes()) + .await?; + fs::File::create("public.pem") + .await? + .write_all(public_bytes.as_bytes()) + .await?; + Ok(()) + } } diff --git a/src/zsdiff.rs b/src/zsdiff.rs index 38545c6..5277eec 100644 --- a/src/zsdiff.rs +++ b/src/zsdiff.rs @@ -48,6 +48,7 @@ async fn compare_hashes( old: HashMap, new: HashMap, compress_level: i32, + encrypted: bool, ) -> Zsdiff { let mut diff_files: HashMap> = HashMap::new(); let mut remove_files: Vec = vec![]; @@ -89,6 +90,7 @@ async fn compare_hashes( remove_folders, compress_level, file_count: diff_files.len() as u32, + encrypted, }, } } @@ -98,29 +100,24 @@ pub async fn zsdiff( old: String, new: String, level: i32, + encrypt: bool, ) -> Result<(), io::Error> { + let now = time::Instant::now(); let output_filename = &format!("{}.zdiff", filename); let old_hashes = walk_dir(old).await; let new_hashes = walk_dir(new).await; - let compare_hashes = compare_hashes(old_hashes, new_hashes, level).await; - let parts = compare_hashes.to_vec().await; - let size_before = parts.len(); - let now = time::Instant::now(); - utils::compress(parts, &fs::File::create(output_filename)?, level).await; - let output_data = fs::read(output_filename)?; - let size_after = output_data.len(); - let hash = get_hash(output_data).await; + let diff = compare_hashes(old_hashes, new_hashes, level, encrypt).await; + + let mut file = fs::File::create(output_filename)?; + let data = diff.to_vec(level, encrypt).await; + file.write_all(&data[..])?; + + let hash = get_hash(data).await; let output_hash = format!("{} {}", hash.clone(), output_filename); fs::File::create(format!("{}.md5", output_filename))?.write_all(output_hash.as_bytes())?; let elapsed = now.elapsed(); - println!("Zsdiff hash: {}", hash); - println!("Size before: {:.1?}KB", size_before / 1024); - println!("Size after: {:.1?}KB", size_after / 1024); - println!( - "Compress ratio: {:.2?}%", - size_after as f64 / size_before as f64 * 100.0 - ); - print!("Time elapsed: {:.2?}", elapsed); + println!(">>> Zsdiff hash: {}", hash); + println!("Time elapsed: {:.2?}", elapsed); Ok(()) } @@ -134,10 +131,19 @@ struct Args { old: String, #[arg(short, long)] new: String, + #[arg(short, long)] + encrypt: bool, } #[tokio::main] async fn main() -> io::Result<()> { let args = Args::parse(); - zsdiff(args.filename, args.old, args.new, args.compress_level).await + zsdiff( + args.filename, + args.old, + args.new, + args.compress_level, + args.encrypt, + ) + .await } diff --git a/src/zspatch.rs b/src/zspatch.rs index b53777b..700d1e6 100644 --- a/src/zspatch.rs +++ b/src/zspatch.rs @@ -1,6 +1,7 @@ mod utils; -use clap::Parser; +use crate::utils::Metadata; +use clap::{Arg, ArgAction, ArgMatches, Command, Parser}; use std::path::{Path, PathBuf}; use std::{io, time}; use tokio::fs; @@ -21,10 +22,28 @@ async fn create_tmp_dir(dir_name: String) -> Result { }) } +async fn load_metadata(filename: String) -> Result { + let filepath = format!("{}.zdiff", filename); + let data = fs::read(&filepath).await?; + + let meta_version = u16::from_be_bytes(data[..2].try_into().unwrap()); + if !utils::is_metadata_supported(meta_version) { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "Metadata version not supported", + )); + } + let meta_size = u32::from_be_bytes(data[2..6].try_into().unwrap()) as usize; + let meta_data = data[6..meta_size + 6].to_vec(); + let metadata = serde_json::from_slice(&meta_data[..]) + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + + Ok(metadata) +} + async fn load_file(filename: String) -> Result { let full_filename = format!("{}.zdiff", filename); - let compressed_data = fs::read(&full_filename).await?; - let data = utils::decompress(compressed_data).await?; + let data = fs::read(&full_filename).await?; let zsdiff = Zsdiff::from_vec(data).await?; println!( ">>> Metadata files to remove: {}", @@ -77,15 +96,11 @@ async fn check_hash(filename: String) -> Result<(), io::Error> { } async fn zspatch(filename: String, dest_dir: String) -> Result<(), io::Error> { + let now = time::Instant::now(); let cloned = filename.clone(); let diff = load_file(cloned).await.ok().unwrap(); let tmp_dir_name = extract_files(&diff, filename).await?; - let now = time::Instant::now(); - fs::File::create("metadata.json") - .await? - .write_all(serde_json::to_vec(&diff.metadata).unwrap().as_slice()) - .await?; let files_to_copy: Vec = diff.content.keys().cloned().collect(); for (_, name) in files_to_copy.iter().enumerate() { let from_path = Path::new(&tmp_dir_name).join(name); @@ -156,7 +171,7 @@ async fn zspatch(filename: String, dest_dir: String) -> Result<(), io::Error> { } } - // fs::remove_dir_all(tmp_dir_name).await?; + fs::remove_dir_all(tmp_dir_name).await?; println!(">>> Patching done! <<<"); println!(">>> Elapsed time: {:.2?}", now.elapsed()); Ok(()) @@ -176,19 +191,85 @@ struct Args { #[tokio::main] async fn main() -> io::Result<()> { - let args = Args::parse(); + let m = Command::new("ZsPatch") + .author("ScuroNeko") + .version("0.3.0") + .about("Explains in brief what the program does") + .subcommand_required(true) + .arg_required_else_help(true) + .after_help("") + .subcommand( + Command::new("metadata") + .short_flag('m') + .long_flag("metadata") + .arg( + Arg::new("filename") + .short('f') + .long("filename") + .required(true) + .action(ArgAction::Set), + ), + ) + .subcommand( + Command::new("patch") + .short_flag('p') + .arg( + Arg::new("filename") + .short('f') + .required(true) + .action(ArgAction::Set), + ) + .arg( + Arg::new("dest") + .short('d') + .required(true) + .action(ArgAction::Set), + ) + .arg( + Arg::new("hash_check") + .long("hash_check") + .required(false) + .action(ArgAction::SetTrue), + ), + ) + .get_matches(); - let filename = args.filename.clone(); - let dest_dir = args.dest_dir.clone(); + match m.subcommand() { + Some(("metadata", meta_matches)) => { + let filename: &String = meta_matches.get_one("filename").unwrap(); + let metadata = load_metadata(filename.clone()).await?; + println!(">>> Compress level: {}", metadata.compress_level); + println!( + ">>> Encrypted?: {}", + if metadata.encrypted { "Yes" } else { "No" } + ); + return Ok(()); + } + Some(("patch", patch_matches)) => { + let filename: &String = patch_matches.get_one("filename").unwrap(); + let dest_dir: &String = patch_matches.get_one("dest").unwrap(); + if patch_matches.get_flag("hash_check") { + check_hash(filename.clone()).await.ok(); + } + zspatch(filename.clone(), dest_dir.clone()).await?; + } + _ => unreachable!("Subcommand is required"), + } + Ok(()) - if args.check_hash { - check_hash(args.filename.clone()).await.ok(); - } - if args.metadata { - let diff = load_file(filename).await?; - let metadata = diff.metadata; - println!(">>> Compress level: {}", metadata.compress_level); - return Ok(()); - } - zspatch(filename, dest_dir).await + // let args = Args::parse(); + // + // let filename = args.filename.clone(); + // let dest_dir = args.dest_dir.clone(); + // + // if args.check_hash { + // check_hash(args.filename.clone()).await.ok(); + // } + // if args.metadata { + // let diff = load_file(filename).await?; + // let metadata = diff.metadata; + // println!(">>> Compress level: {}", metadata.compress_level); + // return Ok(()); + // } + // zspatch(filename, dest_dir).await }