1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/README.md"))]

#[cfg(test)]
pub mod tests;

pub mod sys;
pub mod shader;
pub mod component;
pub mod utils;
pub mod entity;
pub mod scene;
pub mod assets;
pub mod system;
#[cfg(feature = "yew-support")]
pub mod yew_ext;

pub use shader::*;
pub use component::*;
pub use utils::*;
pub use entity::*;
pub use scene::*;
pub use assets::*;
pub use system::*;
#[cfg(feature = "yew-support")]

/// Async function which initializes aframe by adding the aframe script tag
/// to the document header and waiting for the script onload event. 
/// Current Aframe version: 1.2.0
#[cfg(feature = "init")]
pub async fn init_aframe() -> Result<(), InitError>
{
    const LINK: &'static str = "https://aframe.io/releases/1.2.0/aframe.min.js";
    
    use wasm_bindgen::prelude::*;
    use std::sync::{Arc, Mutex};
    use async_lock::Barrier;
    use futures::executor::block_on;

    let result: Arc<Mutex<Result<(), InitError>>> = Arc::new(Mutex::new(Err(InitError)));
    let barrier = Arc::new(Barrier::new(2));

    let result_outer = result.clone();
    let barrier_inner = barrier.clone();

    // Append Aframe to document
    let document = web_sys::window()
        .and_then(|win| win.document())
        .ok_or(InitError)?;
    let head = document.head()
        .ok_or(InitError)?;
    let script_element = document.create_element("script")
        .map_err(|_| InitError)?;
    let script_element = script_element.dyn_into::<web_sys::HtmlElement>()
        .map_err(|_| InitError)?;
    head.append_child(&script_element)
        .map_err(|_| InitError)?;
    let closure = 
    {
        Closure::once(Box::new(move || 
        {
            *result.lock().unwrap() = Ok(());
            drop(result);
            block_on(barrier_inner.wait());
        }) as Box<dyn FnOnce()>)
    };
    script_element.set_onload(Some(closure.as_ref().unchecked_ref()));
    closure.forget();
    script_element.set_attribute("src", LINK)
        .map_err(|_| InitError)?;

    barrier.wait().await;
    Arc::try_unwrap(result_outer)
        .map_err(|_| InitError)
        .and_then(|mutex| mutex.into_inner().map_err(|_| InitError))
        .and_then(|result| result)
}

#[cfg(feature = "init")]
#[derive(Debug, Clone, Copy)]
pub struct InitError;

#[cfg(feature = "init")]
impl std::fmt::Display for InitError 
{
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result 
    {
        write!(f, "Failed to initialize")
    }
}

#[cfg(feature = "init")]
impl std::error::Error for InitError 
{
    fn description(&self) -> &str 
    {
        "Failed to initialize"
    }
}