1use std::sync::Arc;
11
12use sim_kernel::{Cx, Expr, MatchScore, Result, Shape, ShapeDoc, ShapeMatch, Symbol, Value};
13use sim_shape::{
14 ExactExprShape, ExprKind, ExprKindShape, NumberValueShape, OrShape, RepeatShape,
15 TableExtraPolicy, TableFieldSpec, TableShape,
16};
17
18use crate::kinds::{KIND_KEY, SCENE_KINDS, SCENE_NAMESPACE, scene_kind};
19
20struct RankedShape {
21 symbol: Symbol,
22 name: String,
23 detail: String,
24 score: MatchScore,
25 inner: Arc<dyn Shape>,
26}
27
28impl RankedShape {
29 fn ranked(&self, mut matched: ShapeMatch) -> ShapeMatch {
30 if matched.accepted {
31 matched.score = self.score;
32 }
33 matched
34 }
35}
36
37impl Shape for RankedShape {
38 fn symbol(&self) -> Option<Symbol> {
39 Some(self.symbol.clone())
40 }
41
42 fn is_effectful(&self) -> bool {
43 self.inner.is_effectful()
44 }
45
46 fn is_total(&self) -> bool {
47 self.inner.is_total()
48 }
49
50 fn check_value(&self, cx: &mut Cx, value: Value) -> Result<ShapeMatch> {
51 self.inner
52 .check_value(cx, value)
53 .map(|matched| self.ranked(matched))
54 }
55
56 fn check_expr(&self, cx: &mut Cx, expr: &Expr) -> Result<ShapeMatch> {
57 self.inner
58 .check_expr(cx, expr)
59 .map(|matched| self.ranked(matched))
60 }
61
62 fn describe(&self, _cx: &mut Cx) -> Result<ShapeDoc> {
63 Ok(ShapeDoc::new(self.name.clone()).with_detail(self.detail.clone()))
64 }
65}
66
67fn kind_field_shape(kind: Symbol) -> Arc<dyn Shape> {
68 Arc::new(TableShape::new(
69 vec![TableFieldSpec {
70 key: Symbol::new(KIND_KEY),
71 shape: Arc::new(ExactExprShape::new(Expr::Symbol(kind))),
72 required: true,
73 }],
74 TableExtraPolicy::Allow,
75 ))
76}
77
78fn field(key: &str, shape: Arc<dyn Shape>, required: bool) -> TableFieldSpec {
79 TableFieldSpec {
80 key: Symbol::new(key),
81 shape,
82 required,
83 }
84}
85
86fn expr_kind(kind: ExprKind) -> Arc<dyn Shape> {
87 Arc::new(ExprKindShape::new(kind))
88}
89
90fn heatmap_shape() -> Arc<dyn Shape> {
91 let number = || Arc::new(NumberValueShape) as Arc<dyn Shape>;
92 let footprint = Arc::new(TableShape::new(
93 vec![
94 field("cells", number(), true),
95 field("bytes", number(), true),
96 ],
97 TableExtraPolicy::Allow,
98 ));
99 Arc::new(TableShape::new(
100 vec![
101 field(
102 KIND_KEY,
103 Arc::new(ExactExprShape::new(Expr::Symbol(scene_kind("heatmap")))),
104 true,
105 ),
106 field("rows", number(), true),
107 field("cols", number(), true),
108 field("values", Arc::new(RepeatShape::new(number())), true),
109 field(
110 "valid",
111 Arc::new(RepeatShape::new(expr_kind(ExprKind::Bool))),
112 true,
113 ),
114 field("min", number(), true),
115 field("max", number(), true),
116 field("palette", expr_kind(ExprKind::Symbol), true),
117 field("label", expr_kind(ExprKind::String), true),
118 field("detector", expr_kind(ExprKind::String), true),
119 field("footprint", footprint, true),
120 field("advisory", expr_kind(ExprKind::String), false),
121 ],
122 TableExtraPolicy::Allow,
123 ))
124}
125
126fn kind_shape(name: &str) -> Arc<dyn Shape> {
127 if name == "heatmap" {
128 heatmap_shape()
129 } else {
130 kind_field_shape(scene_kind(name))
131 }
132}
133
134fn ranked_shape(
135 symbol: Symbol,
136 name: impl Into<String>,
137 detail: impl Into<String>,
138 score: i32,
139 inner: Arc<dyn Shape>,
140) -> Arc<dyn Shape> {
141 Arc::new(RankedShape {
142 symbol,
143 name: name.into(),
144 detail: detail.into(),
145 score: MatchScore::exact(score),
146 inner,
147 })
148}
149
150pub fn scene_shape_symbol() -> Symbol {
152 Symbol::qualified(SCENE_NAMESPACE, "Scene")
153}
154
155pub(crate) fn scene_shape() -> Arc<dyn Shape> {
156 let choices = SCENE_KINDS.iter().map(|name| kind_shape(name)).collect();
157 ranked_shape(
158 scene_shape_symbol(),
159 "Scene",
160 "any recognized scene node (a kind-tagged map)",
161 5,
162 Arc::new(OrShape::new(choices)),
163 )
164}
165
166fn scene_node_shape(name: &str) -> (Symbol, Arc<dyn Shape>) {
167 let symbol = Symbol::qualified(SCENE_NAMESPACE, capitalize(name));
168 let kind = scene_kind(name);
169 let shape = ranked_shape(
170 symbol.clone(),
171 symbol.name.to_string(),
172 format!("matches scene nodes tagged '{kind}'"),
173 20,
174 kind_shape(name),
175 );
176 (symbol, shape)
177}
178
179pub fn scene_shape_specs() -> Vec<(Symbol, Arc<dyn Shape>)> {
182 let mut specs: Vec<(Symbol, Arc<dyn Shape>)> = vec![(scene_shape_symbol(), scene_shape())];
183 for name in SCENE_KINDS {
184 specs.push(scene_node_shape(name));
185 }
186 specs
187}
188
189fn capitalize(name: &str) -> String {
190 let mut chars = name.chars();
191 match chars.next() {
192 Some(first) => first.to_ascii_uppercase().to_string() + chars.as_str(),
193 None => String::new(),
194 }
195}