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}