binread 2.2.0

A Rust crate for helping read structs from binary data using ✨macro magic✨
Documentation
#![allow(unused_must_use)]
use super::Endian;
use std::io::prelude::*;
use std::path::Path;
use std::sync::{
    atomic::{AtomicUsize, Ordering},
    Mutex,
};

use lazy_static::lazy_static;

lazy_static! {
    static ref FILE: Mutex<Option<Box<dyn Write + Send>>> = Mutex::new({
        std::env::var("DEBUG_TEMPLATE")
            .ok()
            .map(|path| {
                let mut file = std::fs::File::create(path).ok()?;
                write!(file, "{}", SETUP).ok()?;
                Some(file)
            })
            .flatten()
            .map(|file| Box::new(file) as _)
    });
}

static CURRENT_STRUCT_NUM: AtomicUsize = AtomicUsize::new(0);
static CURRENT_VAR_NUM: AtomicUsize = AtomicUsize::new(0);

const SETUP: &str =
    "// Generated by BinRead macro by jam1garner (https://github.com/jam1garner/binread)
typedef   char  i8;
typedef  uchar  u8;
typedef  int16 i16;
typedef uint16 u16;
typedef  int32 i32;
typedef uint32 u32;
typedef  int64 i64;
typedef uint64 u64;
typedef  float f32;
typedef double f64;

";

const COLORS: &[&str] = &[
    "0xE85EBE", "0xFF6E41", "0x00FFC6", "0x788231", "0x00B917", "0x85A900", "0x0076FF", "0x006401",
    "0x009BFF", "0x00FF78", "0xDEFF74", "0xE56FFE", "0xBDD393", "0x7E2DD2", "0x90FB92", "0xFFDB66",
    "0xFFB167", "0xB500FF", "0x43002C", "0x004754", "0x263400", "0x7A4782", "0x774D00", "0xFFA6FE",
    "0xA5FFD2", "0x7544B1", "0xBB8800", "0x01D0FF", "0xBDC6FF", "0xFE8900", "0xFFEEE8", "0x01FFFE",
    "0xA75740", "0x98FF52", "0x968AE8", "0xFF74A3", "0x683D3B", "0xFF029D", "0xFF00F6", "0xFF0000",
    "0x5FAD4E", "0x008F9C", "0xBE9970", "0xC28C9F", "0x00AE7E", "0x6A826C", "0x007DB5", "0x0000FF",
    "0x6B6882", "0x620E00", "0x91D0CB", "0x001544", "0xA42400", "0xFF937E", "0x95003A", "0x00FF00",
    "0x005F39", "0xFFE502", "0x0E4CA1", "0x9E008E", "0xFF0056", "0xD5FF00", "0x010067", "0x000000",
];

pub fn set_output_file<P: AsRef<Path>>(path: P) -> std::io::Result<()> {
    set_output(std::fs::File::create(path.as_ref())?);
    Ok(())
}

pub fn set_output<W: Write + Send + 'static>(mut writer: W) {
    writeln!(writer, "{}", SETUP);
    *FILE.lock().unwrap() = Some(Box::new(writer));
}

pub fn unset_output() {
    *FILE.lock().unwrap() = None;
}

pub fn write_named(endian: Endian, pos: u64, type_name: &str, var_name: &str) {
    let mut lock = FILE.lock().unwrap();
    let file = match lock.as_mut() {
        Some(x) => x,
        None => return,
    };

    match endian {
        Endian::Big => writeln!(*file, "BigEndian();"),
        Endian::Little => writeln!(*file, "LittleEndian();"),
        _ => writeln!(*file),
    };

    let color_index = CURRENT_VAR_NUM.fetch_add(1, Ordering::SeqCst) & 0x3f;

    writeln!(*file, "FSeek(0x{:X});", pos);
    writeln!(
        *file,
        "{} {}<bgcolor={}>;\n",
        type_name, var_name, COLORS[color_index]
    );
}

pub fn get_next_var_name() -> String {
    let var_num = CURRENT_VAR_NUM.fetch_add(1, Ordering::SeqCst);
    format!("var{}", var_num)
}

pub fn get_next_color() -> &'static str {
    let color_index = CURRENT_VAR_NUM.fetch_add(1, Ordering::SeqCst) & 0x3f;
    COLORS[color_index]
}

pub fn write_start_struct(name: &str) {
    let mut lock = FILE.lock().unwrap();
    let file = match lock.as_mut() {
        Some(x) => x,
        None => return,
    };

    writeln!(
        *file,
        "struct {}_{} {{",
        name,
        CURRENT_STRUCT_NUM.fetch_add(1, Ordering::SeqCst)
    );
}

pub fn write_comment(comment: &str) {
    let mut lock = FILE.lock().unwrap();
    let file = match lock.as_mut() {
        Some(x) => x,
        None => return,
    };

    writeln!(*file, "// {}", comment);
}

pub fn write_end_struct(name: Option<&str>) {
    let mut lock = FILE.lock().unwrap();
    let file = match lock.as_mut() {
        Some(x) => x,
        None => return,
    };

    let var_name = name.unwrap_or("root");

    writeln!(*file, "}} {};", var_name);
}

pub fn write(endian: Endian, pos: u64, type_name: &str) {
    let var_name = get_next_var_name();

    write_named(endian, pos, type_name, &var_name);
}

pub fn write_vec_named(endian: Endian, pos: u64, type_name: &str, count: usize, name: &str) {
    let mut lock = FILE.lock().unwrap();
    let file = match lock.as_mut() {
        Some(x) => x,
        None => return,
    };

    match endian {
        Endian::Big => writeln!(*file, "BigEndian();"),
        Endian::Little => writeln!(*file, "LittleEndian();"),
        _ => writeln!(*file),
    };

    let color_index = CURRENT_VAR_NUM.fetch_add(1, Ordering::SeqCst) & 0x3f;

    writeln!(*file, "FSeek(0x{:X});", pos);
    writeln!(
        *file,
        "{} {}[{}]<bgcolor={}>;\n",
        type_name, name, count, COLORS[color_index]
    );
}

pub fn write_vec(endian: Endian, pos: u64, type_name: &str, count: usize) {
    let var_num = CURRENT_VAR_NUM.fetch_add(1, Ordering::SeqCst);
    write_vec_named(endian, pos, type_name, count, &format!("var{}", var_num))
}