floating-ui 0.0.1

Rust bindings for Floating UI
Documentation
use std::sync::Arc;

use js_sys::{Function, Object, Promise};
use wasm_bindgen::prelude::*;

use crate::{
    utils_::object_utils::{self, get_option_js_value},
    Error, Result,
};

use super::{MiddlewareReturn, MiddlewareState, Promisable};

#[derive(Clone)]
pub struct Middleware {
    pub name: String,
    pub options: Option<JsValue>,
    pub r#fn: Arc<dyn Fn(MiddlewareState) -> Promisable<MiddlewareReturn>>,
}

impl TryFrom<JsValue> for Middleware {
    type Error = Error;

    fn try_from(value: JsValue) -> Result<Middleware> {
        let obj = Object::from(value.clone());

        let name = object_utils::get_string(&obj, "name")?;
        let options = get_option_js_value(&obj, "options")?;

        let fn_val = object_utils::get_js_value(&obj, "fn")?;
        let js_fn = Function::from(fn_val);

        let r#fn = move |state: MiddlewareState| {
            if let Ok(state) = state.try_into() {
                if let Ok(js_promise) = js_fn.call1(&value, &state) {
                    let promise: Promise = js_promise.into();
                    return Promisable::from_promise(promise);
                }
                panic!("Failed to call Middleware function");
            }

            panic!("Failed to convert MiddlewareState");
        };

        Ok(Self {
            name,
            options,
            r#fn: Arc::new(r#fn),
        })
    }
}

impl TryInto<JsValue> for Middleware {
    type Error = Error;

    fn try_into(self) -> Result<JsValue> {
        let obj = Object::new();

        object_utils::set_string(&obj, "name", &self.name)?;
        object_utils::set_option_js_value(&obj, "options", self.options)?;

        let r#fn = Closure::wrap(Box::new(move |state: JsValue| {
            if let Ok(state) = MiddlewareState::try_from(state) {
                let res: Promisable<MiddlewareReturn> = (self.r#fn)(state);

                if let Ok(res) = res.try_into_js_value() {
                    return res;
                }
                panic!("Failed to convert MiddlewareReturn");
            }

            panic!("Failed to convert MiddlewareState");
        }) as Box<dyn Fn(JsValue) -> JsValue>);

        object_utils::set_js_value(&obj, "fn", r#fn.as_ref().unchecked_ref())?;

        r#fn.forget();

        Ok(obj.into())
    }
}