use std::process::Command;
pub fn require_7zz() {
if which_7zz().is_none() {
eprintln!("skipping: 7zz not found on PATH");
}
}
fn which_7zz() -> Option<String> {
for candidate in &[
"/usr/bin/7zz",
"/usr/local/bin/7zz",
"/opt/homebrew/bin/7zz",
] {
if std::path::Path::new(candidate).exists() {
return Some((*candidate).to_owned());
}
}
let out = Command::new("which").arg("7zz").output().ok()?;
if out.status.success() {
let path = String::from_utf8(out.stdout).ok()?.trim().to_owned();
if !path.is_empty() {
return Some(path);
}
}
None
}
pub fn seven_zip_compress(input: &[u8]) -> Vec<u8> {
let dir = tempdir();
let input_path = dir.join("input.bin");
let archive_path = dir.join("output.7z");
std::fs::write(&input_path, input).expect("write oracle input");
let status = Command::new(which_7zz().expect("7zz not found"))
.args([
"a",
"-t7z",
"-m0=lzma",
archive_path.to_str().unwrap(),
input_path.to_str().unwrap(),
])
.status()
.expect("run 7zz a");
assert!(status.success(), "7zz compression failed: {status}");
std::fs::read(&archive_path).expect("read oracle archive")
}
#[allow(dead_code)] pub fn seven_zip_decompress(archive: &[u8]) -> Vec<u8> {
let dir = tempdir();
let archive_path = dir.join("input.7z");
let out_dir = dir.join("out");
std::fs::create_dir_all(&out_dir).expect("create out dir");
std::fs::write(&archive_path, archive).expect("write oracle archive");
let status = Command::new(which_7zz().expect("7zz not found"))
.args([
"e",
"-y",
archive_path.to_str().unwrap(),
&format!("-o{}", out_dir.display()),
])
.status()
.expect("run 7zz e");
assert!(status.success(), "7zz extraction failed: {status}");
let entry = std::fs::read_dir(&out_dir)
.expect("read out dir")
.filter_map(|e| e.ok())
.find(|e| e.path().is_file())
.unwrap_or_else(|| panic!("no output files after 7zz e"));
std::fs::read(entry.path()).expect("read extracted file")
}
fn tempdir() -> std::path::PathBuf {
let dir = std::env::temp_dir().join(format!("lazippy-oracle-{}", std::process::id()));
std::fs::create_dir_all(&dir).expect("create tempdir");
dir
}
#[test]
#[ignore = "LZMA not yet implemented"]
fn oracle_round_trip() {
use crate::tests::fixtures;
require_7zz();
let input = fixtures::random_seeded(64 * 1024);
let props = crate::decode::header::Properties {
lc: 3,
lp: 0,
pb: 2,
dict_size: 1 << 23,
uncompressed_size: Some(input.len() as u64),
};
let _compressed = crate::encode::encode(&input, props).expect("lazippy encode");
let archive = seven_zip_compress(&input);
let _decompressed = crate::decode::decode(&archive).expect("lazippy decode");
}