alef 0.22.15

Opinionated polyglot binding generator for Rust libraries
Documentation
#[allow(unused_imports)]
use napi::bindgen_prelude::{JsObjectValue, ToNapiValue, Unknown, Object};
#[allow(unused_imports)]
use napi::JsValue;

fn nodecontext_to_js_object<'e>(
    env: &'e napi::Env,
    ctx: &{{ core_crate }}::visitor::NodeContext,
) -> napi::Result<napi::bindgen_prelude::Object<'e>> {
    let mut obj = napi::bindgen_prelude::Object::new(env)?;
    obj.set_named_property("nodeType", env.create_string(&format!("{:?}", ctx.node_type))?)?;
    obj.set_named_property("tagName", env.create_string(&ctx.tag_name)?)?;
    obj.set_named_property("depth", env.create_uint32(ctx.depth as u32)?)?;
    obj.set_named_property("indexInParent", env.create_uint32(ctx.index_in_parent as u32)?)?;
    obj.set_named_property("isInline", ctx.is_inline)?;
    let parent_tag = match &ctx.parent_tag {
        Some(s) => env.create_string(s)?.to_unknown(),
        None => {
            // SAFETY: napi_get_null returns a valid napi_value for the given env.
            let raw = unsafe { napi::bindgen_prelude::ToNapiValue::to_napi_value(env.raw(), napi::bindgen_prelude::Null)? };
            unsafe { napi::bindgen_prelude::Unknown::from_raw_unchecked(env.raw(), raw) }
        }
    };
    obj.set_named_property("parentTag", parent_tag)?;
    let mut attrs = napi::bindgen_prelude::Object::new(env)?;
    for (k, v) in &ctx.attributes {
        attrs.set_named_property(k, env.create_string(v)?)?;
    }
    obj.set_named_property("attributes", attrs)?;
    Ok(obj)
}

pub struct {{ struct_name }} {
    env: napi::sys::napi_env,
    obj_ref: Option<napi::bindgen_prelude::ObjectRef<false>>,
}

impl std::fmt::Debug for {{ struct_name }} {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{{ struct_name }}")
    }
}

impl {{ struct_name }} {
    pub fn new(js_obj: napi::bindgen_prelude::Object<'_>) -> napi::Result<Self> {
        // SAFETY: Object<'_> is laid out as 3 pointer-sized words: [napi_env, napi_value, is_ref].
        // We extract the raw napi_env pointer so we can reconstruct an Env inside visitor
        // callbacks. The pointer is stable for the lifetime of the Node process.
        let env = unsafe {
            let raw: [*mut std::ffi::c_void; 3] = std::mem::transmute_copy(&js_obj);
            raw[0] as napi::sys::napi_env
        };
        // Create a persistent napi reference so the JS object survives across the
        // synchronous convert() call, regardless of HandleScope movement.
        let obj_ref = js_obj.create_ref::<false>()?;
        Ok(Self { env, obj_ref: Some(obj_ref) })
    }
}

impl Drop for {{ struct_name }} {
    fn drop(&mut self) {
        // Release the persistent reference so the JS object can be GC'd. unref takes
        // ownership; the Option lets us move out from &mut self in drop.
        if let Some(obj_ref) = self.obj_ref.take() {
            // self.env was captured from the napi_env that owns obj_ref.
            let env = napi::Env::from_raw(self.env);
            let _ = obj_ref.unref(&env);
        }
    }
}

// SAFETY: The visitor bridge stores an napi_ref (via ObjectRef), which is valid across
// threads as long as all napi:: operations using it happen on the original event loop
// thread where the reference was created. The Arc<Mutex<>> wrapper around the bridge
// ensures synchronous, single-threaded access during the convert() call.
unsafe impl Send for {{ struct_name }} {}
// SAFETY: see Send impl above.
unsafe impl Sync for {{ struct_name }} {}

impl {{ trait_path }} for {{ struct_name }} {
{{ method_impls }}
}