master
John Lauer 1.2.0: chip thumbnail baked into every IC symbol by default (real outline or generic chip), chipOutline/chipSize prefs, AI check-and-revise hints, STEP ladder in hints 9a15e91 1mo ago
//! adom-symbol in the browser: the SAME `sym_gen` the CLI uses, compiled to
//! wasm32 with a plain C ABI (no wasm-bindgen CLI needed). One export:
//! `generate(json) -> kicad_sym` with the CLI's `create` JSON body.
#[path = "../../src/sym_gen.rs"]
#[allow(dead_code)]
mod sym_gen;

use std::alloc::{alloc, dealloc, Layout};

#[derive(serde::Deserialize)]
struct JsonPin { name: String, number: String, #[serde(rename = "type")] ptype: String, #[serde(default)] group: Option<String>, #[serde(default)] side: Option<String> }
#[derive(serde::Deserialize, Default)]
struct Preferences { #[serde(default, rename = "pinLayout")] pin_layout: Option<String>, #[serde(default, rename = "chipNameCentered")] chip_name_centered: bool, #[serde(default, rename = "refPos")] ref_pos: Option<String>, #[serde(default, rename = "valuePos")] value_pos: Option<String>, #[serde(default, rename = "chipOutline")] chip_outline: Option<bool>, #[serde(default, rename = "chipSize")] chip_size: Option<String> }
#[derive(serde::Deserialize)]
struct Input { #[serde(rename = "symbolName")] symbol_name: String, #[serde(default)] manufacturer: String, #[serde(default)] package: String, #[serde(default)] description: String, #[serde(default, rename = "datasheetUrl")] datasheet_url: String, #[serde(default, rename = "referencePrefix")] reference_prefix: Option<String>, #[serde(default)] pins: Vec<JsonPin>, #[serde(default)] preferences: Preferences }

fn run(json: &str) -> String {
    let i: Input = match serde_json::from_str(json) { Ok(v) => v, Err(e) => return format!("ERROR: {e}") };
    let input = sym_gen::IcInput {
        symbol_name: i.symbol_name, manufacturer: i.manufacturer, package: i.package, description: i.description,
        datasheet_url: i.datasheet_url, reference_prefix: i.reference_prefix.unwrap_or_else(|| "U".into()),
        pins: i.pins.into_iter().map(|p| sym_gen::Pin { name: p.name, number: p.number, ptype: p.ptype, group: p.group, side: p.side }).collect(),
        pin_layout: i.preferences.pin_layout.unwrap_or_else(|| "left-right".into()),
        chip_name_centered: i.preferences.chip_name_centered,
        ref_pos: i.preferences.ref_pos.unwrap_or_default(), value_pos: i.preferences.value_pos.unwrap_or_default(),
        distributor_pn: vec![],
        // Browser build: no assets to discover, so the generic chip (default ON,
        // like the CLI) or nothing.
        chip_outline: if i.preferences.chip_outline.unwrap_or(true) { Some(sym_gen::generic_outline(i.preferences.chip_size.as_deref().unwrap_or("large"))) } else { None },
    };
    sym_gen::generate_ic_sym(&input)
}

/// Allocate `len` bytes the host can write into.
#[no_mangle]
pub extern "C" fn ab_alloc(len: usize) -> *mut u8 { unsafe { alloc(Layout::from_size_align(len.max(1), 1).unwrap()) } }
#[no_mangle]
pub extern "C" fn ab_free(ptr: *mut u8, len: usize) { unsafe { dealloc(ptr, Layout::from_size_align(len.max(1), 1).unwrap()) } }

/// Returns a pointer to [u32 len][bytes...]; free with ab_free(ptr, len+4).
#[no_mangle]
pub extern "C" fn ab_generate(ptr: *const u8, len: usize) -> *mut u8 {
    let json = unsafe { std::str::from_utf8_unchecked(std::slice::from_raw_parts(ptr, len)) };
    let out = run(json);
    let bytes = out.as_bytes();
    let total = bytes.len() + 4;
    let p = ab_alloc(total);
    unsafe {
        std::ptr::copy_nonoverlapping((bytes.len() as u32).to_le_bytes().as_ptr(), p, 4);
        std::ptr::copy_nonoverlapping(bytes.as_ptr(), p.add(4), bytes.len());
    }
    p
}