1use sim_kernel::{Cx, Expr, MatchScore, Result, Shape, ShapeDoc, ShapeMatch, Symbol, Value};
11
12use crate::kinds::{SCENE_KINDS, SCENE_NAMESPACE, is_known_kind, scene_kind};
13use crate::model::node_kind;
14
15pub struct SceneNodeShape {
17 kind: Symbol,
18 symbol: Symbol,
19}
20
21impl SceneNodeShape {
22 pub fn new(name: &str) -> Self {
24 Self {
25 kind: scene_kind(name),
26 symbol: Symbol::qualified(SCENE_NAMESPACE, capitalize(name)),
27 }
28 }
29}
30
31impl Shape for SceneNodeShape {
32 fn symbol(&self) -> Option<Symbol> {
33 Some(self.symbol.clone())
34 }
35
36 fn check_value(&self, cx: &mut Cx, value: Value) -> Result<ShapeMatch> {
37 let expr = value.object().as_expr(cx)?;
38 self.check_expr(cx, &expr)
39 }
40
41 fn check_expr(&self, _cx: &mut Cx, expr: &Expr) -> Result<ShapeMatch> {
42 match node_kind(expr) {
43 Some(kind) if kind == self.kind => Ok(ShapeMatch::accept(MatchScore::exact(20))),
44 Some(kind) => Ok(ShapeMatch::reject(format!(
45 "scene node kind '{kind}' does not match '{}'",
46 self.kind
47 ))),
48 None => Ok(ShapeMatch::reject("value is not a scene node")),
49 }
50 }
51
52 fn describe(&self, _cx: &mut Cx) -> Result<ShapeDoc> {
53 Ok(ShapeDoc::new(self.symbol.name.to_string())
54 .with_detail(format!("matches scene nodes tagged '{}'", self.kind)))
55 }
56}
57
58pub struct SceneShape;
60
61impl Shape for SceneShape {
62 fn symbol(&self) -> Option<Symbol> {
63 Some(Symbol::qualified(SCENE_NAMESPACE, "Scene"))
64 }
65
66 fn check_value(&self, cx: &mut Cx, value: Value) -> Result<ShapeMatch> {
67 let expr = value.object().as_expr(cx)?;
68 self.check_expr(cx, &expr)
69 }
70
71 fn check_expr(&self, _cx: &mut Cx, expr: &Expr) -> Result<ShapeMatch> {
72 match node_kind(expr) {
73 Some(kind) if is_known_kind(&kind) => Ok(ShapeMatch::accept(MatchScore::exact(5))),
74 Some(kind) => Ok(ShapeMatch::reject(format!(
75 "unrecognized scene kind '{kind}'"
76 ))),
77 None => Ok(ShapeMatch::reject("value is not a scene node")),
78 }
79 }
80
81 fn describe(&self, _cx: &mut Cx) -> Result<ShapeDoc> {
82 Ok(ShapeDoc::new("Scene").with_detail("any recognized scene node (a kind-tagged map)"))
83 }
84}
85
86pub fn scene_shape_symbol() -> Symbol {
88 Symbol::qualified(SCENE_NAMESPACE, "Scene")
89}
90
91pub fn scene_shape_specs() -> Vec<(Symbol, std::sync::Arc<dyn Shape>)> {
94 let mut specs: Vec<(Symbol, std::sync::Arc<dyn Shape>)> =
95 vec![(scene_shape_symbol(), std::sync::Arc::new(SceneShape))];
96 for name in SCENE_KINDS {
97 let shape = SceneNodeShape::new(name);
98 specs.push((shape.symbol.clone(), std::sync::Arc::new(shape)));
99 }
100 specs
101}
102
103fn capitalize(name: &str) -> String {
104 let mut chars = name.chars();
105 match chars.next() {
106 Some(first) => first.to_ascii_uppercase().to_string() + chars.as_str(),
107 None => String::new(),
108 }
109}