use std::{cell::RefCell, ptr::NonNull};
pub use wasm_bindgen::prelude::*;
pub mod uniffi {
pub use uniffi_core::{
RustBuffer, RustCallStatus, RustCallStatusCode, UniffiForeignPointerCell,
};
pub type VoidPointer = *const std::ffi::c_void;
}
pub trait IntoRust<HighLevel> {
fn into_rust(v: HighLevel) -> Self;
}
pub trait IntoJs<HighLevel> {
fn into_js(self) -> HighLevel;
}
macro_rules! identity_into_rust {
($high_level:ident, $rust_type:ty) => {
pub type $high_level = $rust_type;
impl IntoRust<$high_level> for $rust_type {
fn into_rust(v: $high_level) -> Self {
v
}
}
impl IntoJs<$high_level> for $rust_type {
fn into_js(self) -> $high_level {
self
}
}
};
}
identity_into_rust!(UInt8, u8);
identity_into_rust!(UInt16, u16);
identity_into_rust!(UInt32, u32);
identity_into_rust!(UInt64, u64);
identity_into_rust!(Int8, i8);
identity_into_rust!(Int16, i16);
identity_into_rust!(Int32, i32);
identity_into_rust!(Int64, i64);
identity_into_rust!(Float32, f32);
identity_into_rust!(Float64, f64);
pub type Handle = u64;
pub type VoidPointer = u64;
impl IntoRust<VoidPointer> for uniffi::VoidPointer {
fn into_rust(v: VoidPointer) -> Self {
v as Self
}
}
impl IntoJs<VoidPointer> for uniffi::VoidPointer {
fn into_js(self) -> VoidPointer {
self as VoidPointer
}
}
pub type ForeignBytes = Vec<u8>;
impl IntoRust<ForeignBytes> for uniffi::RustBuffer {
fn into_rust(v: ForeignBytes) -> Self {
Self::from_vec(v)
}
}
impl IntoJs<ForeignBytes> for uniffi::RustBuffer {
fn into_js(self) -> ForeignBytes {
self.destroy_into_vec()
}
}
macro_rules! uniffi_result {
($uniffi_result:ident, $high_level_type:ty) => {
#[wasm_bindgen]
extern "C" {
pub type $uniffi_result;
#[wasm_bindgen(method, getter)]
fn code(this: &$uniffi_result) -> i8;
#[wasm_bindgen(method, getter = errorBuf)]
fn error_buf(this: &$uniffi_result) -> Option<ForeignBytes>;
#[wasm_bindgen(method, getter)]
fn pointee(this: &$uniffi_result) -> Option<$high_level_type>;
}
impl $uniffi_result {
pub fn copy_into_status(&self, rust: &mut uniffi::RustCallStatus) {
let code = self.code();
rust.code = uniffi::RustCallStatusCode::try_from(code)
.expect("RustCallStatusCode is not valid");
let buf = uniffi::RustBuffer::from_vec(self.error_buf().unwrap_or_default());
*rust.error_buf = buf;
}
}
};
}
macro_rules! uniffi_result_with_return {
($uniffi_result:ident, $high_level_type:ty, $rust_type:ty) => {
uniffi_result!($uniffi_result, $high_level_type);
impl $uniffi_result {
pub fn copy_into_return(&self, rust: &mut $rust_type) {
*rust = <$rust_type>::into_rust(self.pointee().unwrap_or_default());
}
}
};
}
uniffi_result!(UniffiResultVoid, Int8);
uniffi_result_with_return!(UniffiResultUInt8, UInt8, u8);
uniffi_result_with_return!(UniffiResultUInt16, UInt16, u16);
uniffi_result_with_return!(UniffiResultUInt32, UInt32, u32);
uniffi_result_with_return!(UniffiResultUInt64, UInt64, u64);
uniffi_result_with_return!(UniffiResultInt8, Int8, i8);
uniffi_result_with_return!(UniffiResultInt16, Int16, i16);
uniffi_result_with_return!(UniffiResultInt32, Int32, i32);
uniffi_result_with_return!(UniffiResultInt64, Int64, i64);
uniffi_result_with_return!(UniffiResultFloat32, Float32, f32);
uniffi_result_with_return!(UniffiResultFloat64, Float64, f64);
uniffi_result_with_return!(UniffiResultForeignBytes, ForeignBytes, uniffi::RustBuffer);
#[wasm_bindgen]
#[derive(Default)]
pub struct RustCallStatus {
pub code: i8,
error_buf: Option<ForeignBytes>,
}
#[wasm_bindgen]
impl RustCallStatus {
#[wasm_bindgen(constructor)]
pub fn new() -> Self {
Default::default()
}
#[wasm_bindgen(getter = errorBuf)]
pub fn error_buf(self) -> Option<ForeignBytes> {
self.error_buf
}
#[wasm_bindgen(setter = errorBuf)]
pub fn set_error_buf(&mut self, bytes: Option<ForeignBytes>) {
self.error_buf = bytes;
}
}
impl RustCallStatus {
pub fn copy_from(&mut self, rust: uniffi::RustCallStatus) {
self.code = rust.code as i8;
let buf = std::mem::ManuallyDrop::into_inner(rust.error_buf).destroy_into_vec();
self.error_buf = Some(buf);
}
}
impl IntoRust<RustCallStatus> for uniffi::RustCallStatus {
fn into_rust(js: RustCallStatus) -> Self {
let code = uniffi::RustCallStatusCode::try_from(js.code)
.expect("Unexpected error code. This is likely a bug in UBRN");
let bytes = js.error_buf.unwrap_or_default();
let error_buf = std::mem::ManuallyDrop::new(uniffi::RustBuffer::from_vec(bytes));
uniffi::RustCallStatus { error_buf, code }
}
}
impl IntoJs<RustCallStatus> for uniffi::RustCallStatus {
fn into_js(self) -> RustCallStatus {
let mut status = RustCallStatus::new();
status.copy_from(self);
status
}
}
pub struct ForeignCell<T> {
inner: RefCell<Option<T>>,
}
impl<T> ForeignCell<T> {
pub fn new() -> Self {
Self {
inner: RefCell::new(None),
}
}
pub fn set(&self, value: T) {
self.inner.replace(Some(value));
}
pub fn with_value<F, R>(&self, f: F) -> R
where
F: FnOnce(&T) -> R,
{
let cell = &self.inner;
let value = cell.borrow();
let value = value
.as_ref()
.expect("ForeignCell accessed before initialization");
f(value)
}
}
impl<T> Default for ForeignCell<T> {
fn default() -> Self {
Self::new()
}
}
impl<JsType, RsType> IntoRust<JsType> for NonNull<RsType>
where
RsType: IntoRust<JsType>,
{
fn into_rust(js: JsType) -> Self {
let rs = RsType::into_rust(js);
let ptr = Box::into_raw(Box::new(rs));
unsafe { NonNull::new_unchecked(ptr) }
}
}