use md5; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::{fs, io}; use zstd::{Decoder, Encoder}; const METADATA_VERSION: u16 = 1; const SUPPORTED_VERSION: [u16; 1] = [1]; pub struct Zsdiff { pub content: HashMap>, pub metadata: Metadata, } impl Zsdiff { pub async fn from_vec(_data: Vec) -> Result { let meta_version = u16::from_be_bytes(_data[..2].try_into().unwrap()); println!(">>> Metadata version: {}", meta_version); if !SUPPORTED_VERSION.contains(&meta_version) { return Err(io::Error::new( io::ErrorKind::Other, "Metadata version mismatch", )); } let meta_size = u32::from_be_bytes(_data[2..6].try_into().unwrap()) as usize; let mut index = 6; let meta = _data[index..index + meta_size].to_vec(); let metadata: Metadata = serde_json::from_slice(&meta)?; println!(">>> Metadata parsed successfully"); index += meta_size; println!(">>> File count: {}", metadata.file_count); let data = _data; let mut content = HashMap::new(); while index < data.len() { let filename_size = u32::from_be_bytes(data[index..index + 4].try_into().unwrap()) as usize; index += 4; let filename = String::from_utf8(data[index..filename_size + index].to_vec()).unwrap(); index += filename_size; let content_size = u32::from_be_bytes(data[index..index + 4].try_into().unwrap()) as usize; index += 4; let cont = data[index..index + content_size].to_vec(); index += cont.len(); content.insert(filename, cont); } Ok(Zsdiff { content, metadata }) } pub async fn to_vec(&self) -> Vec { let mut meta_bytes: Vec = Vec::new(); meta_bytes.extend(METADATA_VERSION.to_be_bytes()); let meta = serde_json::to_vec(&self.metadata).unwrap(); meta_bytes.extend((meta.len() as u32).to_be_bytes()); meta_bytes.extend(meta); let mut parts: Vec = Vec::new(); for (filename, content) in &self.content { let filename_size: [u8; 4] = (filename.len() as u32).to_be_bytes(); let filename_encoded = vec![filename_size.as_slice(), filename.as_bytes()].concat(); let content_size: [u8; 4] = (content.len() as u32).to_be_bytes(); let content_encoded = vec![content_size.as_slice(), content.as_slice()].concat(); let part = vec![filename_encoded, content_encoded].concat(); parts.extend(part) } let out = vec![meta_bytes, parts].concat(); out } } #[derive(Serialize, Deserialize, Debug)] pub struct Metadata { pub(crate) diff_files: Vec, pub hashes: HashMap, pub remove_files: Vec, pub remove_folders: Vec, pub file_count: u32, pub compress_level: i32, } pub async fn get_hash(data: Vec) -> String { let hash = md5::compute(&data[..]); 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(); } 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) }