arora_behavior/built_in.rs
1//! Built-in keys: well-known store slots the runtime maintains for every behavior.
2//!
3//! The runtime writes these at the start of each step — before it ticks any
4//! behavior — so a behavior reads the current frame's timing straight from the
5//! [`store`](super::BehaviorContext::store) instead of taking it as a tick
6//! argument. Timing stays out of the
7//! [`BehaviorInterpreter`](super::BehaviorInterpreter) API: it is
8//! just data, like any other slot, and a behavior (an animation module, a graph
9//! time node) derives whatever it needs — `dt`, elapsed time — by reading these
10//! keys.
11//!
12//! The keys live under the reserved [`PREFIX`] namespace, which the runtime
13//! owns: behaviors must not publish their own keys under it. They travel
14//! outbound with every other change, so a remote reads the device's clock and
15//! derives what it needs from it — a step rate, jitter, a stalled loop. A
16//! remote that does not want them filters them on its side.
17
18/// Reserved namespace for runtime-maintained built-in keys. The runtime owns it;
19/// behaviors must not write their own keys under this prefix.
20pub const PREFIX: &str = "arora/";
21
22/// Monotonic **nanoseconds** since the runtime started, as a
23/// [`U64`](arora_types::value::Value::U64) value. The runtime advances it by the
24/// step's `dt` before each tick. Integer nanoseconds are exact (no float drift
25/// over a long run) and give ~584 years of range before `u64` overflows.
26pub const TIME: &str = "arora/time";
27
28/// **Nanoseconds** elapsed since the previous step, as a
29/// [`U64`](arora_types::value::Value::U64) value — the step's `dt`, surfaced for
30/// behaviors that pace themselves (an animation module, a graph time node).
31pub const DT: &str = "arora/dt";
32
33/// Whether `key` is a reserved built-in key — runtime-owned, so a behavior must
34/// not write it, and a consumer that wants to skip the clock can recognize it.
35pub fn is_built_in(key: &str) -> bool {
36 key.starts_with(PREFIX)
37}
38
39#[cfg(test)]
40mod tests {
41 use super::*;
42
43 #[test]
44 fn built_in_keys_share_the_reserved_prefix() {
45 assert!(is_built_in(TIME));
46 assert!(is_built_in(DT));
47 assert!(TIME.starts_with(PREFIX));
48 assert!(DT.starts_with(PREFIX));
49 }
50
51 #[test]
52 fn ordinary_keys_are_not_built_in() {
53 assert!(!is_built_in("sensor/x"));
54 assert!(!is_built_in("actuator/y"));
55 // A key that merely mentions "arora" later in the path is not reserved.
56 assert!(!is_built_in("device/arora/time"));
57 }
58}