assay-lua 0.16.0

General-purpose enhanced Lua runtime. Batteries-included scripting, automation, and web services.
Documentation
use flate2::read::GzDecoder;
use flate2::write::GzEncoder;
use flate2::Compression;
use mlua::{Lua, Table};
use std::io::Write;

pub fn register_tar(lua: &Lua) -> mlua::Result<()> {
    let tar_table = lua.create_table()?;

    let create_fn = lua.create_function(move |_lua, (output, files, opts): (String, Table, Option<Table>)| {
        let gzip = opts.as_ref()
            .and_then(|t| t.get::<bool>("gzip").ok())
            .unwrap_or(true);

        let output_data: Vec<u8> = if gzip {
            let enc = GzEncoder::new(Vec::new(), Compression::default());
            let mut tar_builder = tar::Builder::new(enc);
            add_files(&mut tar_builder, &files)?;
            let enc = tar_builder.into_inner().map_err(|e| mlua::Error::external(e.to_string()))?;
            enc.finish().map_err(|e| mlua::Error::external(e.to_string()))?
        } else {
            let mut tar_builder = tar::Builder::new(Vec::new());
            add_files(&mut tar_builder, &files)?;
            tar_builder.into_inner().map_err(|e| mlua::Error::external(e.to_string()))?
        };

        std::fs::write(&output, &output_data).map_err(|e| mlua::Error::external(e.to_string()))?;
        Ok(true)
    })?;
    tar_table.set("create", create_fn)?;

    let extract_fn = lua.create_function(move |_lua, (archive, dest): (String, String)| {
        let file = std::fs::read(&archive).map_err(|e| mlua::Error::external(e.to_string()))?;

        if archive.ends_with(".gz") || archive.ends_with(".tgz") {
            let decoder = GzDecoder::new(&file[..]);
            let mut archive = tar::Archive::new(decoder);
            archive.unpack(&dest).map_err(|e| mlua::Error::external(e.to_string()))?;
        } else {
            let mut archive = tar::Archive::new(&file[..]);
            archive.unpack(&dest).map_err(|e| mlua::Error::external(e.to_string()))?;
        }

        Ok(true)
    })?;
    tar_table.set("extract", extract_fn)?;

    let list_fn = lua.create_function(move |_lua, archive: String| {
        let file = std::fs::read(&archive).map_err(|e| mlua::Error::external(e.to_string()))?;
        let mut entries = Vec::new();

        if archive.ends_with(".gz") || archive.ends_with(".tgz") {
            let decoder = GzDecoder::new(&file[..]);
            let mut archive = tar::Archive::new(decoder);
            for entry in archive.entries().map_err(|e| mlua::Error::external(e.to_string()))? {
                let entry = entry.map_err(|e| mlua::Error::external(e.to_string()))?;
                let path = entry.path().map_err(|e| mlua::Error::external(e.to_string()))?;
                entries.push(path.to_string_lossy().to_string());
            }
        } else {
            let mut archive = tar::Archive::new(&file[..]);
            for entry in archive.entries().map_err(|e| mlua::Error::external(e.to_string()))? {
                let entry = entry.map_err(|e| mlua::Error::external(e.to_string()))?;
                let path = entry.path().map_err(|e| mlua::Error::external(e.to_string()))?;
                entries.push(path.to_string_lossy().to_string());
            }
        }

        Ok(entries)
    })?;
    tar_table.set("list", list_fn)?;

    lua.globals().set("tar", tar_table)?;
    Ok(())
}

fn add_files<W: Write>(builder: &mut tar::Builder<W>, files: &Table) -> mlua::Result<()> {
    for pair in files.pairs::<String, mlua::Value>() {
        let (path, value) = pair?;
        let content = match value {
            mlua::Value::String(s) => s.to_str()?.to_string(),
            _ => value.to_string()?,
        };
        let mut header = tar::Header::new_gnu();
        header.set_path(&path).map_err(|e| mlua::Error::external(e.to_string()))?;
        header.set_size(content.len() as u64);
        header.set_mode(0o644);
        header.set_cksum();
        builder
            .append_data(&mut header, &path, content.as_bytes())
            .map_err(|e| mlua::Error::external(e.to_string()))?;
    }
    Ok(())
}