elio 1.5.1

Snappy, batteries-included terminal file manager with rich previews, inline images, bulk actions, and trash support.
Documentation
use super::{BinaryMetadata, ByteOrder, read_u32};

pub(super) fn parse(bytes: &[u8]) -> Option<BinaryMetadata> {
    match bytes.get(0..4)? {
        [0xfe, 0xed, 0xfa, 0xce] => parse_regular(bytes, ByteOrder::Big, "32-bit"),
        [0xce, 0xfa, 0xed, 0xfe] => parse_regular(bytes, ByteOrder::Little, "32-bit"),
        [0xfe, 0xed, 0xfa, 0xcf] => parse_regular(bytes, ByteOrder::Big, "64-bit"),
        [0xcf, 0xfa, 0xed, 0xfe] => parse_regular(bytes, ByteOrder::Little, "64-bit"),
        [0xca, 0xfe, 0xba, 0xbe] => parse_fat(bytes, ByteOrder::Big),
        [0xbe, 0xba, 0xfe, 0xca] => parse_fat(bytes, ByteOrder::Little),
        _ => None,
    }
}

fn parse_regular(bytes: &[u8], order: ByteOrder, bits: &'static str) -> Option<BinaryMetadata> {
    let cpu_type = read_u32(bytes, 4, order)?;
    let file_type = read_u32(bytes, 12, order)?;
    let command_count = usize::try_from(read_u32(bytes, 16, order)?).ok()?;

    Some(BinaryMetadata {
        detail: file_type_detail(file_type),
        format: "Mach-O",
        kind: file_type_name(file_type).map(str::to_string),
        architecture: cpu_name(cpu_type).map(str::to_string),
        bits: Some(bits),
        endianness: Some(order.label()),
        abi: None,
        subsystem: None,
        entry_point: None,
        section_count: None,
        command_count: Some(command_count),
    })
}

fn parse_fat(bytes: &[u8], order: ByteOrder) -> Option<BinaryMetadata> {
    let arch_count = usize::try_from(read_u32(bytes, 4, order)?).ok()?;
    let mut architectures = Vec::new();
    for index in 0..arch_count.min(4) {
        let offset = 8 + index * 20;
        let cpu_type = read_u32(bytes, offset, order)?;
        if let Some(name) = cpu_name(cpu_type) {
            architectures.push(name);
        }
    }

    Some(BinaryMetadata {
        detail: "Mach-O universal binary",
        format: "Mach-O (fat)",
        kind: Some("Universal binary".to_string()),
        architecture: (!architectures.is_empty()).then(|| architectures.join(", ")),
        bits: None,
        endianness: Some(order.label()),
        abi: None,
        subsystem: None,
        entry_point: None,
        section_count: Some(arch_count),
        command_count: None,
    })
}

fn cpu_name(cpu_type: u32) -> Option<&'static str> {
    match cpu_type {
        7 => Some("x86"),
        0x01000007 => Some("x86_64"),
        12 => Some("ARM"),
        0x0100000C => Some("ARM64"),
        18 => Some("PowerPC"),
        0x01000012 => Some("PowerPC64"),
        _ => None,
    }
}

fn file_type_name(file_type: u32) -> Option<&'static str> {
    match file_type {
        1 => Some("Relocatable object"),
        2 => Some("Executable"),
        6 => Some("Dynamic library"),
        8 => Some("Bundle"),
        11 => Some("Dynamic linker"),
        12 => Some("Kernel extension"),
        _ => None,
    }
}

fn file_type_detail(file_type: u32) -> &'static str {
    match file_type {
        1 => "Mach-O relocatable object",
        2 => "Mach-O executable",
        6 => "Mach-O dynamic library",
        8 => "Mach-O bundle",
        11 => "Mach-O dynamic linker",
        12 => "Mach-O kernel extension",
        _ => "Mach-O binary",
    }
}