aframe/
system.rs

1use std::collections::HashMap;
2
3use crate::sys::{registerSystem};
4use crate::utils::*;
5use serde::Serialize;
6use wasm_bindgen::{JsCast, prelude::*};
7
8
9/// Top-level macro to define systems. Usage resembles struct creation syntax.
10/// The `js!` macro is available for writing inline javascript, and returns a
11/// js_sys::Function object. This macro calls `into` on expressions passed into the 
12/// fields expecting function, allowing the `js!` macro to be used as a catch-all.
13/// Takes the optional fields described in the table below.
14///
15/// | field | syntax explanation | description |
16/// |-------|--------------------|-------------|
17/// | schema | A hashmap containing string keys and ComponentProperty values. Recommend the maplit crate | Describes component properties |
18/// | init | JsValue created from a js_sys::Function() | Called on initialization |
19/// | pause | JsValue created from a js_sys::Function() | Called when the entity or scene pauses |
20/// | play | JsValue created from a js_sys::Function() | Called when the entity or scene resumes |
21/// | tick | JsValue created from a js_sys::Function(time, timeDelta) | Called on each tick or frame of the scene’s render loop |
22/// | properties | name: JsValue, ... | Additional comma-separated functions or data with any valid name may be specified |
23///
24/// All parameteres are optional, although the order must be exactly as shown. 
25/// `schema` should be a HashMap with string keys and `AframeProperty` 
26/// values. The rest are strings containing  javascript code. A `js!` macro 
27/// is provided to allow inline javascript code to be included in the Rust code
28/// (See the docs for the `js!` macro for caveats and limitations). Here's an 
29/// example:
30/// ```ignore
31/// // Example: 
32/// let some_system = system_def!
33/// (
34///     schema: hashmap!
35///     {
36///         "some_float" => AframeProperty::float("number", None),
37///         "some_text" => AframeProperty::string("string", Some(Cow::Borrowed("init")))
38///     },
39///     init: js!
40///     (
41///         this.data.some_float = 1.0; 
42///         this.data.some_text = "I'm a bit of text";
43///     ),
44///     tick: js!
45///     (time, delta =>>
46///         this.data.some_float = this.data.some_float + 1.0;
47///     ),
48///     pause: js!(this.data.some_text = "paused!";),
49///     play: js!(this.data.some_text = "playing!";),
50///     properties:
51///         reset_me: js!(this.data.some_float = 0.0;)
52/// );
53/// unsafe
54/// {
55///     some_system.register("system_name");
56/// }
57/// ```
58#[macro_export]
59macro_rules! system_def
60{
61    (
62        $(schema: $schema:expr,)?
63        $(init: $init:expr,)?
64        $(pause: $pause:expr,)?
65        $(play: $play:expr,)?
66        $(tick: $tick:expr,)?
67        $(properties: $($fn_name:ident: $func:expr),*)?
68    ) => 
69    {
70        $crate::system::SystemReg
71        {
72            $(schema: $schema,)?
73            $(init: $init.into(),)?
74            $(pause: $pause.into(),)?
75            $(play: $play.into(),)?
76            $(tick: $tick.into(),)?
77            $(properties: 
78            {
79                let mut props = std::collections::HashMap::new();
80                $(
81                    props.insert(stringify!($fn_name), $func.into());
82                )*
83                props
84            },)?
85            ..$crate::system::SystemReg::default()
86        }
87    }
88}
89
90/// System registration definition. All JsValues should be derived from [`js_sys::Function`]
91#[derive(Serialize, Clone)]
92pub struct SystemReg
93{
94    pub schema: HashMap<&'static str, AframeProperty>,
95    #[serde(skip)] pub init: JsValue,
96    #[serde(skip)] pub pause: JsValue,
97    #[serde(skip)] pub play: JsValue,
98    #[serde(skip)] pub tick: JsValue,
99    #[serde(skip)] pub properties: HashMap<&'static str, JsValue>
100}
101impl Default for SystemReg
102{
103    fn default() -> Self 
104    {
105        let empty_fn: JsValue = js_sys::Function::default().into();
106        Self
107        {
108            schema: HashMap::new(),
109            init: empty_fn.clone(),
110            pause: empty_fn.clone(),
111            play: empty_fn.clone(),
112            tick: empty_fn,
113            properties: HashMap::new()
114        }
115    }
116}
117impl From<&SystemReg> for JsValue
118{
119    fn from(sysr: &SystemReg) -> Self 
120    {
121        let js_value = serde_wasm_bindgen::to_value(sysr).expect("Failed to convert SystemReg into JsObject");
122        define_property(js_value.unchecked_ref(), "init", (sysr.init).unchecked_ref());
123        define_property(js_value.unchecked_ref(), "pause", (sysr.pause).unchecked_ref());
124        define_property(js_value.unchecked_ref(), "play", (sysr.play).unchecked_ref());
125        define_property(js_value.unchecked_ref(), "tick", (sysr.tick).unchecked_ref());
126        for (k, v) in sysr.properties.iter()
127        {
128            define_property(js_value.unchecked_ref(), k, v.unchecked_ref());
129        }
130        js_value
131    }
132}
133impl SystemReg
134{
135    /// Register a system in aframe. Warning: Aframe must be initialized before this is called.
136    pub unsafe fn register(self, name: &str)
137    {
138        registerSystem(name, (&self).into());
139    }
140}