wry-bindgen-runtime 0.1.0

Wry runtime transport for wry-bindgen semantic bindings
Documentation
use alloc::string::String;
use alloc::vec::Vec;

use super::TypeDef;

#[derive(Clone, Copy)]
pub struct JsFunctionSpec {
    code: JsFunctionCode,
}

impl JsFunctionSpec {
    pub const fn new(js_code: fn() -> String) -> Self {
        Self {
            code: JsFunctionCode::Global(js_code),
        }
    }

    pub const fn with_module(module: &'static JsModuleSpec, js_code: fn(&str) -> String) -> Self {
        Self {
            code: JsFunctionCode::Module { module, js_code },
        }
    }
}

#[derive(Clone, Copy)]
enum JsFunctionCode {
    Global(fn() -> String),
    Module {
        module: &'static JsModuleSpec,
        js_code: fn(&str) -> String,
    },
}

inventory::collect!(JsFunctionSpec);

#[derive(Clone, Copy)]
pub struct JsModuleSpec {
    content: &'static str,
}

impl JsModuleSpec {
    pub const fn new(content: &'static str) -> Self {
        Self { content }
    }

    pub const fn const_hash(&self) -> u64 {
        const FNV_OFFSET_BASIS: u64 = 0xcbf29ce484222325;
        const FNV_PRIME: u64 = 0x100000001b3;

        let mut hash = FNV_OFFSET_BASIS;
        let mut i = 0;
        let bytes = self.content.as_bytes();
        while i < bytes.len() {
            hash ^= bytes[i] as u64;
            hash = hash.wrapping_mul(FNV_PRIME);
            i += 1;
        }
        hash
    }
}

impl JsFunctionSpec {
    pub(crate) fn module(&self) -> Option<&'static JsModuleSpec> {
        match self.code {
            JsFunctionCode::Global(_) => None,
            JsFunctionCode::Module { module, .. } => Some(module),
        }
    }

    pub(crate) fn render_js_code(&self) -> String {
        match self.code {
            JsFunctionCode::Global(js_code) => js_code(),
            JsFunctionCode::Module { module, js_code } => {
                let module_binding = alloc::format!("module_{:x}", module.const_hash());
                js_code(&module_binding)
            }
        }
    }

    pub(crate) fn identity_eq(&self, other: &JsFunctionSpec) -> bool {
        match (self.code, other.code) {
            (JsFunctionCode::Global(a), JsFunctionCode::Global(b)) => a as usize == b as usize,
            (
                JsFunctionCode::Module {
                    module: module_a,
                    js_code: js_code_a,
                },
                JsFunctionCode::Module {
                    module: module_b,
                    js_code: js_code_b,
                },
            ) => core::ptr::eq(module_a, module_b) && js_code_a as usize == js_code_b as usize,
            _ => false,
        }
    }
}

impl JsModuleSpec {
    pub(crate) fn content(&self) -> &'static str {
        self.content
    }
}

#[derive(Clone, Copy)]
#[non_exhaustive]
pub enum JsClassMemberKind {
    Constructor,
    Method,
    StaticMethod,
    Getter,
    Setter,
}

#[derive(Clone, Copy)]
pub struct JsClassMemberSpec {
    class_name: &'static str,
    member_name: &'static str,
    export_name: &'static str,
    arg_count: usize,
    arg_types: fn() -> Vec<TypeDef>,
    return_type: fn() -> Option<TypeDef>,
    kind: JsClassMemberKind,
}

impl JsClassMemberSpec {
    pub const fn new(
        class_name: &'static str,
        member_name: &'static str,
        export_name: &'static str,
        arg_count: usize,
        arg_types: fn() -> Vec<TypeDef>,
        return_type: fn() -> Option<TypeDef>,
        kind: JsClassMemberKind,
    ) -> Self {
        Self {
            class_name,
            member_name,
            export_name,
            arg_count,
            arg_types,
            return_type,
            kind,
        }
    }
}

pub(super) type JsClassMemberParts = (
    &'static str,
    &'static str,
    &'static str,
    usize,
    Vec<TypeDef>,
    Option<TypeDef>,
    JsClassMemberKind,
);

impl JsClassMemberSpec {
    pub(crate) fn parts(&self) -> JsClassMemberParts {
        (
            self.class_name,
            self.member_name,
            self.export_name,
            self.arg_count,
            (self.arg_types)(),
            (self.return_type)(),
            self.kind,
        )
    }
}

inventory::collect!(JsClassMemberSpec);

#[derive(Clone, Copy)]
pub struct JsExportSpec {
    name: &'static str,
    handler: fn(&mut super::DecodedData) -> Result<super::EncodedData, String>,
}

impl JsExportSpec {
    pub const fn new(
        name: &'static str,
        handler: fn(&mut super::DecodedData) -> Result<super::EncodedData, String>,
    ) -> Self {
        Self { name, handler }
    }

    pub(crate) fn call_if_name(
        &self,
        name: &str,
        data: &mut super::DecodedData<'_>,
    ) -> Option<Result<super::EncodedData, String>> {
        (self.name == name).then(|| (self.handler)(data))
    }
}

inventory::collect!(JsExportSpec);