1use sim_kernel::{Expr, Symbol};
4use sim_lib_scene::node;
5use sim_value::build::{int, map, sym, text, vector};
6
7pub const POLY_SECTION_VIEW_ID: &str = "view:component-poly-sections";
9
10pub fn poly_section_view(sections: &Expr) -> Expr {
12 let rows = sequence(field_named(sections, "sections").unwrap_or(sections))
13 .into_iter()
14 .enumerate()
15 .map(|(index, section)| section_row(§ion, index))
16 .collect();
17 node(
18 "table",
19 vec![
20 ("lens", sym(POLY_SECTION_VIEW_ID)),
21 ("role", sym("poly-section-view")),
22 ("rows", vector(rows)),
23 (
24 "actions",
25 vector(vec![
26 sym("enable-section"),
27 sym("disable-section"),
28 sym("inspect"),
29 ]),
30 ),
31 ],
32 )
33}
34
35fn section_row(section: &Expr, index: usize) -> Expr {
36 let id = field_named(section, "id")
37 .cloned()
38 .unwrap_or_else(|| Expr::Symbol(Symbol::new(format!("section-{index}"))));
39 let label = field_str_named(section, "label")
40 .map(text)
41 .unwrap_or_else(|| text(expr_label(&id)));
42 map(vec![
43 ("id", id),
44 ("label", label),
45 (
46 "enabled",
47 field_named(section, "enabled")
48 .cloned()
49 .unwrap_or(Expr::Bool(true)),
50 ),
51 (
52 "voices",
53 field_named(section, "voices")
54 .cloned()
55 .unwrap_or_else(|| int(1)),
56 ),
57 (
58 "clock",
59 field_named(section, "clock")
60 .cloned()
61 .unwrap_or_else(|| sym("sample-clock")),
62 ),
63 (
64 "rate",
65 field_named(section, "rate")
66 .cloned()
67 .unwrap_or_else(|| sym("audio-rate")),
68 ),
69 (
70 "actions",
71 vector(vec![sym("enable-section"), sym("disable-section")]),
72 ),
73 ])
74}
75
76fn sequence(value: &Expr) -> Vec<Expr> {
77 match value {
78 Expr::List(items) | Expr::Vector(items) => items.clone(),
79 Expr::Nil => Vec::new(),
80 other => vec![other.clone()],
81 }
82}
83
84fn field_named<'a>(expr: &'a Expr, name: &str) -> Option<&'a Expr> {
85 let Expr::Map(entries) = expr else {
86 return None;
87 };
88 entries
89 .iter()
90 .find_map(|(key, value)| (key_name(key) == Some(name)).then_some(value))
91}
92
93fn field_str_named<'a>(expr: &'a Expr, name: &str) -> Option<&'a str> {
94 match field_named(expr, name) {
95 Some(Expr::String(text)) => Some(text),
96 _ => None,
97 }
98}
99
100fn key_name(key: &Expr) -> Option<&str> {
101 match key {
102 Expr::Symbol(symbol) => Some(symbol.name.as_ref()),
103 Expr::String(text) => Some(text),
104 _ => None,
105 }
106}
107
108fn expr_label(expr: &Expr) -> String {
109 match expr {
110 Expr::Symbol(symbol) => symbol.as_qualified_str(),
111 Expr::String(text) => text.clone(),
112 other => format!("{other:?}"),
113 }
114}