use std::io::{Read, Write};
pub fn pack_tar_entry<W: Write, R: Read>(
builder: &mut tar::Builder<W>,
name: &str,
mode: u32,
size: u64,
reader: R,
) -> std::io::Result<()> {
let mut header = tar::Header::new_gnu();
header.set_size(size);
header.set_mode(mode);
builder.append_data(&mut header, name, reader)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pack_tar_entry_roundtrips_through_archive() {
use crate::assert::Verdict;
const NAME: &str = "ktstr";
const MODE: u32 = 0o755;
let payload: &[u8] = b"hello tar world\n";
let mut buf: Vec<u8> = Vec::new();
{
let mut builder = tar::Builder::new(&mut buf);
pack_tar_entry(&mut builder, NAME, MODE, payload.len() as u64, payload)
.expect("pack_tar_entry must succeed for valid inputs");
builder.finish().expect("tar finish");
}
let mut archive = tar::Archive::new(&buf[..]);
let mut entries = archive.entries().expect("read entries");
let mut entry = entries
.next()
.expect("at least one entry")
.expect("entry header readable");
let header = entry.header().clone();
let path_str = header
.path()
.expect("path bytes must decode")
.to_string_lossy()
.to_string();
let mode = header.mode().expect("mode must decode");
let size = header.size().expect("size must decode");
let mut got = Vec::new();
entry.read_to_end(&mut got).expect("payload readable");
let mut v = Verdict::new();
crate::claim!(v, path_str).eq(NAME.to_string());
crate::claim!(v, mode).eq(MODE);
crate::claim!(v, size).eq(payload.len() as u64);
let got_hex = hex::encode(&got);
let want_hex = hex::encode(payload);
crate::claim!(v, got_hex).eq(want_hex);
let r = v.into_result();
assert!(
r.passed,
"tar roundtrip claims must all pass: {:?}",
r.details,
);
assert!(
entries.next().is_none(),
"pack_tar_entry must produce exactly one entry per call",
);
}
#[test]
fn pack_tar_entry_preserves_distinct_modes_across_calls() {
use crate::assert::Verdict;
let mut buf: Vec<u8> = Vec::new();
{
let mut builder = tar::Builder::new(&mut buf);
pack_tar_entry(&mut builder, "a", 0o755, 1, &b"x"[..]).unwrap();
pack_tar_entry(&mut builder, "b", 0o644, 1, &b"y"[..]).unwrap();
builder.finish().unwrap();
}
let mut archive = tar::Archive::new(&buf[..]);
let mut iter = archive.entries().unwrap();
let a = iter.next().unwrap().unwrap();
let b = iter.next().unwrap().unwrap();
let a_mode = a.header().mode().unwrap();
let b_mode = b.header().mode().unwrap();
let mut v = Verdict::new();
crate::claim!(v, a_mode).eq(0o755u32);
crate::claim!(v, b_mode).eq(0o644u32);
crate::claim!(v, a_mode).ne(b_mode);
let r = v.into_result();
assert!(
r.passed,
"per-call mode must be preserved distinctly: {:?}",
r.details,
);
}
}