dodo-bindgen-cli 2.0.0

A CLI tool for generating TypeScript bindings for libraries built with the `dodo_bindgen` crate
use std::fs;
use std::path::Path;

use dodo_bindgen_rust_ir::Item;
use object::{Object, ObjectSection};

const PREFIX: &[u8] = b"DODO_META_";

pub fn read(path: &Path) -> Vec<Item> {
    let data =
        fs::read(path).unwrap_or_else(|err| panic!("failed to read `{}`: {err}", path.display()));
    let file = object::File::parse(&*data)
        .unwrap_or_else(|err| panic!("failed to parse `{}`: {err}", path.display()));
    let exports = file
        .exports()
        .unwrap_or_else(|err| panic!("failed to read exports of `{}`: {err}", path.display()));

    let mut entries = Vec::new();
    for export in exports {
        let name = export.name();
        let name = name
            .strip_prefix(b"_")
            .filter(|name| name.starts_with(PREFIX))
            .unwrap_or(name);
        if !name.starts_with(PREFIX) {
            continue;
        }
        let name = String::from_utf8_lossy(name).into_owned();
        let bytes = bytes_at(&file, export.address())
            .unwrap_or_else(|| panic!("metadata symbol `{name}` points outside of any section"));
        let item = Item::decode(bytes)
            .unwrap_or_else(|err| panic!("failed to decode metadata symbol `{name}`: {err}"));
        entries.push((name, item));
    }

    entries.sort_by(|(a, _), (b, _)| a.cmp(b));
    entries.into_iter().map(|(_, item)| item).collect()
}

fn bytes_at<'data>(file: &object::File<'data>, address: u64) -> Option<&'data [u8]> {
    file.sections().find_map(|section| {
        let start = section.address();
        let offset = address.checked_sub(start)?;
        if offset >= section.size() {
            return None;
        }
        section.data().ok()?.get(offset as usize..)
    })
}