This commit is contained in:
2025-10-17 11:34:59 +03:00
parent 9d9beefe7f
commit 561400ae3c
6 changed files with 24 additions and 24 deletions

105
src/zsdiff.rs Normal file
View File

@@ -0,0 +1,105 @@
mod utils;
use clap::Parser;
use std::collections::HashMap;
use std::{fs, io};
use utils::{Metadata, Zsdiff, get_hash};
use walkdir::WalkDir;
struct FileInfo {
path: String,
relative_path: String, // Without dir prefix
hash: String,
}
async fn walk_dir(dir: String) -> HashMap<String, FileInfo> {
let mut hash_list: HashMap<String, FileInfo> = HashMap::new();
for e in WalkDir::new(&dir) {
let e = e.unwrap();
let path = e.path();
if path.is_dir() {
continue;
}
let content = fs::read(path).unwrap();
let hash = get_hash(&content).await;
let path_str = path.display().to_string();
let file_info = FileInfo {
relative_path: path_str[dir.len() + 1..].to_string(),
path: path_str,
hash: hash.clone(),
};
hash_list.entry(hash).or_insert(file_info);
}
hash_list
}
async fn compare_hashes(old: HashMap<String, FileInfo>, new: HashMap<String, FileInfo>) -> Zsdiff {
let mut diff_files: HashMap<String, Vec<u8>> = HashMap::new();
let mut remove_files: Vec<String> = vec![];
let mut hashes: HashMap<String, String> = HashMap::new();
for (_, info) in &old {
remove_files.push(info.relative_path.clone());
}
for (new_hash, new_fileinfo) in &new {
let old_fileinfo = old.get(new_hash);
remove_files.retain(|filename| !filename.eq(&new_fileinfo.relative_path));
if old_fileinfo.is_none() {
let path = new_fileinfo.relative_path.clone();
diff_files.insert(path.clone(), fs::read(new_fileinfo.path.clone()).unwrap());
hashes.insert(
new_fileinfo.relative_path.clone(),
new_fileinfo.hash.clone(),
);
}
}
Zsdiff {
content: diff_files.clone(),
metadata: Metadata {
diff_files: diff_files.keys().cloned().collect(),
hashes,
remove_files,
},
}
}
pub async fn zsdiff(
filename: String,
old: String,
new: String,
level: i32,
) -> Result<(), io::Error> {
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).await;
let parts = compare_hashes.to_vec().await;
let file = fs::File::create(output_filename)?;
utils::compress_parts(parts, &file, level).await;
// let mut buf = Vec::new();
// file.read(&mut buf)?;
// let output_hash = get_hash(&buf).await;
// println!("{}", output_hash);
Ok(())
}
#[derive(Parser, Debug)]
struct Args {
#[arg(short, long)]
filename: String,
#[arg(short, long, default_value_t = 11)]
compress_level: i32,
#[arg(short, long)]
old: String,
#[arg(short, long)]
new: String,
}
#[tokio::main]
async fn main() -> io::Result<()> {
let args = Args::parse();
zsdiff(args.filename, args.old, args.new, args.compress_level).await?;
Ok(())
}