marshal-parser 0.1.1

Parser for Python's "marshal" serialization format
Documentation
use std::fs::File;
use std::io::{Read, Write};
use std::process::Command;

use marshal_parser::*;

pub fn parse_and_rewrite(old_path: &str, new_path: &str) -> anyhow::Result<()> {
    let mut file = File::options()
        .read(true)
        .write(false)
        .create_new(false)
        .open(old_path)?;

    let mut data = Vec::new();
    file.read_to_end(&mut data)?;

    let marshal = MarshalObject::parse_pyc(&data)?;
    let result = marshal.clear_unused_ref_flags(&data)?;

    let mut new = File::create_new(new_path)?;
    new.write_all(&result)?;

    Ok(())
}

pub fn pyc_header_length(version: (u16, u16)) -> usize {
    if version >= (3, 7) {
        16
    } else if version >= (3, 3) {
        12
    } else {
        8
    }
}

pub fn compare_with_python(old: &str, new: &str, (py_major, py_minor): (u16, u16)) -> anyhow::Result<()> {
    let result = Command::new(format!("python{py_major}.{py_minor}"))
        .arg("tests/compare.py")
        .arg(old)
        .arg(new)
        .arg(pyc_header_length((py_major, py_minor)).to_string())
        .output()?;

    if result.status.code() == Some(0) {
        Ok(())
    } else {
        anyhow::bail!("Deserialized marshal dumps differ")
    }
}