#![allow(clippy::doc_markdown)]
use std::collections::BTreeMap;
use std::path::PathBuf;
use std::process::ExitCode;
use zipatch_rs::chunk::SqpkCommand;
use zipatch_rs::{Chunk, ZiPatchReader};
fn main() -> ExitCode {
let Some(path) = std::env::args_os().nth(1).map(PathBuf::from) else {
eprintln!("usage: parse_summary <path/to/file.patch>");
return ExitCode::from(2);
};
let reader = match ZiPatchReader::from_path(&path) {
Ok(r) => r,
Err(e) => {
eprintln!("failed to open {}: {e}", path.display());
return ExitCode::FAILURE;
}
};
let mut counts: BTreeMap<&'static str, u64> = BTreeMap::new();
let mut total: u64 = 0;
for result in reader {
let chunk = match result {
Ok(c) => c,
Err(e) => {
eprintln!("parse error after {total} chunks: {e}");
return ExitCode::FAILURE;
}
};
total += 1;
*counts.entry(chunk_label(&chunk)).or_insert(0) += 1;
}
println!("file: {}", path.display());
println!("total chunks: {total}");
println!();
println!("{:<32} {:>10}", "variant", "count");
println!("{:-<32} {:->10}", "", "");
for (label, count) in &counts {
println!("{label:<32} {count:>10}");
}
ExitCode::SUCCESS
}
fn chunk_label(chunk: &Chunk) -> &'static str {
match chunk {
Chunk::FileHeader(_) => "FileHeader",
Chunk::ApplyOption(_) => "ApplyOption",
Chunk::ApplyFreeSpace(_) => "ApplyFreeSpace",
Chunk::AddDirectory(_) => "AddDirectory",
Chunk::DeleteDirectory(_) => "DeleteDirectory",
Chunk::Sqpk(cmd) => match cmd {
SqpkCommand::AddData(_) => "Sqpk::AddData",
SqpkCommand::DeleteData(_) => "Sqpk::DeleteData",
SqpkCommand::ExpandData(_) => "Sqpk::ExpandData",
SqpkCommand::Header(_) => "Sqpk::Header",
SqpkCommand::TargetInfo(_) => "Sqpk::TargetInfo",
SqpkCommand::File(_) => "Sqpk::File",
SqpkCommand::Index(_) => "Sqpk::Index",
SqpkCommand::PatchInfo(_) => "Sqpk::PatchInfo",
_ => "Sqpk::<unknown>",
},
Chunk::EndOfFile => "EndOfFile",
_ => "<unknown>",
}
}