emscripten_rs_sys 0.3.0

Autogenerated Rust bindings to the Emscripten API
use std::ffi::c_char;

pub use crate::{emscripten_asm_const_double, emscripten_asm_const_int, emscripten_asm_const_ptr};
pub use emscripten_rs_macros::js_asm;

pub trait AsmSignature: Default {
    const SIGNATURE: char;
}

macro_rules! int_like {
    ($($t:ty),*) => {
        $(
            impl AsmSignature for $t {
                const SIGNATURE: char = 'i';
            }
        )*
    };
}

int_like!(u8, i8, u16, i16, u32, i32, ());

impl AsmSignature for i64 {
    const SIGNATURE: char = 'j';
}

impl AsmSignature for u64 {
    const SIGNATURE: char = 'j';
}

impl<T> AsmSignature for *const T {
    const SIGNATURE: char = 'p';
}

impl<T> AsmSignature for *mut T {
    const SIGNATURE: char = 'p';
}

impl AsmSignature for f32 {
    const SIGNATURE: char = 'f';
}

impl AsmSignature for f64 {
    const SIGNATURE: char = 'd';
}

pub struct SignatureBuilder<const N: usize> {
    sig: [c_char; N],
}

impl SignatureBuilder<1> {
    pub const fn new<Ret: AsmSignature>() -> Self {
        Self {
            sig: [Ret::SIGNATURE as c_char],
        }
    }

    pub const fn new_for<Ret: AsmSignature>(_: &Ret) -> Self {
        Self {
            sig: [Ret::SIGNATURE as c_char],
        }
    }
}

const NEXT_LEN<const N: usize>: usize = N + 1;

const fn push<T: Copy + [const] Default, const N: usize>(
    arr: [T; N],
    value: T,
) -> [T; core::direct_const_arg!(NEXT_LEN::<N>)] {
    let mut out = [T::default(); core::direct_const_arg!(NEXT_LEN::<N>)];
    let _ = &out[..N].copy_from_slice(&arr);
    out[N] = value;
    out
}

impl<const N: usize> SignatureBuilder<N> {
    pub const fn add_param<Param: AsmSignature>(
        self,
        _: &Param,
    ) -> SignatureBuilder<core::direct_const_arg!(NEXT_LEN::<N>)> {
        SignatureBuilder {
            sig: push(self.sig, Param::SIGNATURE as c_char),
        }
    }

    pub const fn finish(self) -> [c_char; core::direct_const_arg!(NEXT_LEN::<N>)] {
        push(self.sig, '\0' as _)
    }
}

#[cfg(test)]
mod tests {

    use super::*;

    #[test]
    fn proof_of_concept() {
        use crate::binding;

        let result = unsafe {
            #[unsafe(link_section = "em_asm")]
            static CODE: [u8; 16] = *b"return $0 + $1;\0";

            let x = 10;
            let y = 20;

            binding::emscripten_asm_const_int(
                CODE.as_ptr() as _,
                SignatureBuilder::new::<i32>()
                    .add_param(&x)
                    .add_param(&y)
                    .finish()
                    .as_ptr(),
                x,
                y,
            )
        };

        assert_eq!(result, 30);
    }

    #[test]
    fn basic() {
        let number = 2;
        js_asm! {
            |number| {
                console.log("Got: ", number);
            }
        }

        assert_eq!(
            js_asm! {
                || -> i32 {
                    return 1;
                }
            },
            1
        );
    }

    #[test]
    fn mul_int() {
        let x = 3;
        let y = 2;

        let result = js_asm! {
            |x,y| -> i32 {
                return x*y;
            }
        };
        assert_eq!(result, 6);
    }

    #[test]
    fn add_doubles() {
        let a = 12.5;
        let b = 52.2;

        let result = js_asm! {
            |a,b| -> f64 {
                return a*b;
            }
        };
        assert_eq!(result, 12.5 * 52.2);
    }

    #[test]
    fn ptr_return() {
        let string_ptr = c"Hello".as_ptr();
        let returned_ptr = js_asm! { |string_ptr| -> *mut c_char { return string_ptr; } };
        assert_eq!(string_ptr, returned_ptr as *const c_char);
    }
}