#![allow(dead_code)]
use std::fs;
use std::io::Write;
use std::os::unix::fs::{MetadataExt, PermissionsExt};
use std::path::{Path, PathBuf};
pub enum E<'a> {
Dir(&'a str),
File(&'a str, &'a [u8]),
Mode(&'a str, &'a [u8], u32),
Link(&'a str, &'a str),
Hard(&'a str, &'a str),
}
pub fn tar_bytes(entries: &[E]) -> Vec<u8> {
let mut b = tar::Builder::new(Vec::new());
for e in entries {
let mut h = tar::Header::new_gnu();
h.set_uid(0);
h.set_gid(0);
h.set_mtime(1_000_000);
match e {
E::Dir(p) => {
h.set_entry_type(tar::EntryType::Directory);
h.set_size(0);
h.set_mode(0o755);
b.append_data(&mut h, p, &[][..]).unwrap();
}
E::File(p, c) | E::Mode(p, c, _) => {
let mode = if let E::Mode(_, _, m) = e { *m } else { 0o644 };
h.set_entry_type(tar::EntryType::Regular);
h.set_size(c.len() as u64);
h.set_mode(mode);
b.append_data(&mut h, p, *c).unwrap();
}
E::Link(p, target) => {
h.set_entry_type(tar::EntryType::Symlink);
h.set_size(0);
h.set_mode(0o777);
b.append_link(&mut h, p, target).unwrap();
}
E::Hard(p, target) => {
h.set_entry_type(tar::EntryType::Link);
h.set_size(0);
h.set_mode(0o644);
b.append_link(&mut h, p, target).unwrap();
}
}
}
b.into_inner().unwrap()
}
pub fn gzip(bytes: &[u8]) -> Vec<u8> {
let mut e = flate2::write::GzEncoder::new(Vec::new(), flate2::Compression::fast());
e.write_all(bytes).unwrap();
e.finish().unwrap()
}
pub fn gzip_full_flush(bytes: &[u8], every: usize) -> Vec<u8> {
use flate2::{Compress, Compression, FlushCompress};
let mut out = vec![0x1f, 0x8b, 8, 0, 0, 0, 0, 0, 0, 0xff];
let mut c = Compress::new(Compression::fast(), false);
let mut buf = vec![0u8; every * 2 + 1024];
for (i, chunk) in bytes.chunks(every).enumerate() {
let last = (i + 1) * every >= bytes.len();
let flush = if last {
FlushCompress::Finish
} else {
FlushCompress::Full
};
let mut input = chunk;
loop {
let (bi, bo) = (c.total_in(), c.total_out());
c.compress(input, &mut buf, flush).unwrap();
let used = (c.total_in() - bi) as usize;
let made = (c.total_out() - bo) as usize;
out.extend_from_slice(&buf[..made]);
input = &input[used..];
if input.is_empty() && made < buf.len() {
break;
}
}
}
let mut crc = flate2::Crc::new();
crc.update(bytes);
out.extend_from_slice(&crc.sum().to_le_bytes());
out.extend_from_slice(&(bytes.len() as u32).to_le_bytes());
out
}
pub fn zstd(bytes: &[u8]) -> Vec<u8> {
zstd::encode_all(bytes, 3).unwrap()
}
pub fn blob(dir: &Path, name: &str, bytes: &[u8]) -> PathBuf {
let p = dir.join(name);
fs::write(&p, bytes).unwrap();
p
}
pub fn noise(len: usize, seed: u64) -> Vec<u8> {
let mut x = seed | 1;
(0..len)
.map(|_| {
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
x as u8
})
.collect()
}
pub fn text(len: usize) -> Vec<u8> {
let words = [
"fn ",
"let ",
"self",
".unwrap()",
"\n",
" ",
"match ",
"Ok(",
"path",
"; ",
];
noise(len, 11)
.iter()
.flat_map(|b| words[(*b % 10) as usize].bytes())
.take(len)
.collect()
}
pub fn tree(root: &Path) -> Vec<(PathBuf, String)> {
fn walk(root: &Path, dir: &Path, out: &mut Vec<(PathBuf, String)>) {
for e in fs::read_dir(dir).unwrap().flatten() {
let p = e.path();
let rel = p.strip_prefix(root).unwrap().to_path_buf();
let m = fs::symlink_metadata(&p).unwrap();
let mode = m.permissions().mode() & 0o7777;
if m.file_type().is_symlink() {
let t = fs::read_link(&p).unwrap();
out.push((rel, format!("link {}", t.display())));
} else if m.is_dir() {
out.push((rel, format!("dir {mode:o}")));
walk(root, &p, out);
} else {
let c = fs::read(&p).unwrap();
let shown = if c.len() > 64 {
format!("{} bytes", c.len())
} else {
String::from_utf8_lossy(&c).into_owned()
};
out.push((rel, format!("file {mode:o} n{} {shown}", m.nlink())));
}
}
}
let mut out = Vec::new();
walk(root, root, &mut out);
out.sort();
out
}