1use sim_kernel::Expr;
10
11pub use sim_value::build::{float, int, list, map, sym, text, vector};
12
13use crate::model::node;
14
15pub const RESERVED_DATA_KEYS: &[&str] = &["kind"];
17
18pub fn stack(dir: &str, children: Vec<Expr>) -> Expr {
20 node(
21 "stack",
22 vec![("dir", sym(dir)), ("children", list(children))],
23 )
24}
25
26pub fn box_(role: &str, children: Vec<Expr>) -> Expr {
28 node(
29 "box",
30 vec![("role", sym(role)), ("children", list(children))],
31 )
32}
33
34pub fn badge(status: &str, label: &str) -> Expr {
36 node(
37 "badge",
38 vec![("status", sym(status)), ("label", text(label))],
39 )
40}
41
42pub fn badge_cluster(badges: Vec<Expr>) -> Expr {
44 node("badge-cluster", vec![("badges", list(badges))])
45}
46
47pub fn text_node(content: impl Into<String>) -> Expr {
49 node("text", vec![("text", text(content.into()))])
50}
51
52pub fn data_map(entries: Vec<(&str, Expr)>) -> Expr {
55 debug_assert!(
56 entries
57 .iter()
58 .all(|(key, _)| !RESERVED_DATA_KEYS.contains(key)),
59 "data_map: a plain data map must not use a reserved scene-node key (e.g. 'kind'); \
60 rename the field"
61 );
62 map(entries)
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68
69 #[test]
70 fn scene_shape_helpers_validate() {
71 let scene = stack(
72 "column",
73 vec![box_(
74 "summary",
75 vec![text_node("hi"), badge_cluster(vec![badge("ok", "done")])],
76 )],
77 );
78 crate::model::validate_scene(&scene).expect("helper scenes validate");
79 }
80
81 #[test]
82 fn data_map_allows_non_reserved_keys() {
83 let value = data_map(vec![("style", sym("line")), ("at", int(3))]);
84 assert!(matches!(value, Expr::Map(_)));
85 }
86
87 #[test]
88 #[should_panic(expected = "reserved scene-node key")]
89 fn data_map_rejects_a_reserved_key_in_debug() {
90 let _ = data_map(vec![("kind", sym("line"))]);
91 }
92}