use wasm_bindgen::{JsCast, prelude::*};
use std::convert::TryFrom;
use js_sys::{Array, Object};
use std::sync::LazyLock;
static AFRAME: LazyLock<Option<Aframe>> = LazyLock::new(Aframe::get);
#[wasm_bindgen]
extern
{
#[wasm_bindgen(js_namespace = AFRAME)]
pub fn registerPrimitive(name: &str, definition: JsValue);
#[wasm_bindgen(js_namespace = AFRAME)]
pub fn registerComponent(name: &str, data: JsValue);
#[wasm_bindgen(js_namespace = AFRAME)]
pub fn registerSystem(name: &str, data: JsValue);
#[wasm_bindgen(js_namespace = AFRAME)]
pub fn registerShader(name: &str, data: JsValue);
#[wasm_bindgen(js_namespace = AFRAME)]
pub fn registerGeometry(name: &str, data: JsValue);
#[wasm_bindgen(js_namespace = AFRAME)]
pub fn registerElement(name: &str, data: JsValue);
}
pub(crate) fn access_field(obj: &Object, field_name: &'static str) -> Option<JsValue>
{
Object::entries(obj)
.iter()
.find(|e| e.dyn_ref::<Array>()
.filter(|entry| entry
.iter()
.next()
.and_then(|key| key.as_string())
.filter(|key_str| key_str == field_name)
.is_some())
.is_some())
}
pub fn three_js() -> Option<JsValue>
{
AFRAME.as_ref().and_then(|aframe| access_field(&aframe.0, "THREE"))
}
pub fn components() -> Option<JsValue>
{
AFRAME.as_ref().and_then(|aframe| access_field(&aframe.0, "components"))
}
pub fn geometries() -> Option<JsValue>
{
AFRAME.as_ref().and_then(|aframe| access_field(&aframe.0, "geometries"))
}
pub fn primitives() -> Option<JsValue>
{
AFRAME.as_ref()
.and_then(|aframe| access_field(&aframe.0, "primitives"))
.and_then(|primitives|
{
primitives.unchecked_into::<Array>()
.iter()
.skip(1)
.next()
.and_then(|primitives| access_field(primitives.unchecked_ref(), "primitives"))
})
}
pub fn shaders() -> Option<JsValue>
{
AFRAME.as_ref().and_then(|aframe| access_field(&aframe.0, "shaders"))
}
pub fn systems() -> Option<JsValue>
{
AFRAME.as_ref().and_then(|aframe| access_field(&aframe.0, "systems"))
}
pub fn version() -> Option<JsValue>
{
AFRAME.as_ref().and_then(|aframe| access_field(&aframe.0, "version"))
}
pub fn utils() -> Option<JsValue>
{
AFRAME.as_ref()
.and_then(|aframe| access_field(&aframe.0, "utils"))
.and_then(|utils| utils.unchecked_into::<Array>().iter().skip(1).next())
}
pub fn device() -> Option<JsValue>
{
utils()
.and_then(|utils|
{
access_field(utils.unchecked_ref(), "device")
.and_then(|utils| utils.unchecked_into::<Array>().iter().skip(1).next())
})
}
struct Aframe(Object);
unsafe impl Send for Aframe {}
unsafe impl Sync for Aframe {}
impl Aframe
{
fn get() -> Option<Self>
{
web_sys::window()
.ok_or("Failed to access window")
.and_then(Aframe::try_from)
.ok()
}
}
impl TryFrom<web_sys::Window> for Aframe
{
type Error = &'static str;
fn try_from(window: web_sys::Window) -> Result<Self, Self::Error>
{
window.get("AFRAME")
.map(Aframe)
.ok_or("Failed to access AFRAME global")
}
}