jay-compositor 1.15.0

The Jay compositor
use crate::utils::fx_hash::FHashMap;
use flate2::Compression;
use flate2::GzBuilder;
use jay_algorithms::jar::JarError;
use jay_algorithms::jar::JarEvent;
use jay_algorithms::jar::JarReader;
use jay_algorithms::tar::TarWriter;
use std::io;
use std::io::BufWriter;
use thiserror::Error;
use uapi::OwnedFd;

#[derive(Debug, Error)]
pub enum JarToTarError {
    #[error("Could not create event reader")]
    OpenJar(#[source] JarError),
    #[error("Could not read event")]
    ReadJar(#[source] JarError),
    #[error("Could not write tar")]
    WriteTar(#[source] io::Error),
    #[error("Jar refers to an unknown link")]
    MissingLink,
}

pub fn jar_to_tar(root: &str, src: &OwnedFd, dst: &OwnedFd) -> Result<(), JarToTarError> {
    let mut reader = JarReader::new(src).map_err(JarToTarError::OpenJar)?;
    let filename = format!("{root}.tar");
    let writer = BufWriter::with_capacity(1 << 17, dst.borrow());
    let writer = GzBuilder::new()
        .filename(filename)
        .write(writer, Compression::default());
    let mut buf_writer = BufWriter::new(writer);
    let mut tar_writer = TarWriter::new(&mut buf_writer);
    let mut lens = vec![];
    let mut path = vec![];
    let mut paths = FHashMap::default();
    loop {
        let ev = reader.next().map_err(JarToTarError::ReadJar)?;
        let Some(ev) = ev else {
            break;
        };
        let len = path.len();
        let res = match ev {
            JarEvent::Dir(p) => {
                lens.push(len);
                path.extend_from_slice(p);
                path.push(b'/');
                tar_writer.add_dir(&path)
            }
            JarEvent::DirUp => {
                let len = lens.pop().unwrap();
                path.truncate(len);
                Ok(())
            }
            JarEvent::Reg(p, unique, c) => {
                path.extend_from_slice(p);
                if let Some(unique) = unique {
                    paths.insert(unique, path.clone());
                }
                let res = tar_writer.add_reg(&path, c);
                path.truncate(len);
                res
            }
            JarEvent::Lnk(p, l) => {
                path.extend_from_slice(p);
                let res = tar_writer.add_lnk(&path, l);
                path.truncate(len);
                res
            }
            JarEvent::Hrd(p, unique) => {
                let l = paths.get(&unique).ok_or(JarToTarError::MissingLink)?;
                path.extend_from_slice(p);
                let res = tar_writer.add_hrd(&path, l);
                path.truncate(len);
                res
            }
        };
        res.map_err(JarToTarError::WriteTar)?;
    }
    tar_writer.finish().map_err(JarToTarError::WriteTar)?;
    buf_writer
        .into_inner()
        .map_err(|e| e.into_error())
        .map_err(JarToTarError::WriteTar)?
        .finish()
        .map_err(JarToTarError::WriteTar)?
        .into_inner()
        .map_err(|e| e.into_error())
        .map_err(JarToTarError::WriteTar)?;
    Ok(())
}