sim_shape/primitives/
object.rs1use std::sync::Arc;
5
6use crate::base::{MatchScore, Shape, ShapeDoc, ShapeMatch};
7use sim_kernel::{Cx, Expr, Result, Symbol, Value};
8
9#[derive(Clone, Debug, PartialEq, Eq)]
15pub struct ObjectExpr {
16 pub class: Symbol,
18 pub fields: Vec<(Symbol, Expr)>,
20}
21
22impl ObjectExpr {
23 pub fn to_expr(self) -> Expr {
25 Expr::Extension {
26 tag: Symbol::qualified("expr", "object"),
27 payload: Box::new(Expr::Map(vec![
28 (Expr::Symbol(Symbol::new("class")), Expr::Symbol(self.class)),
29 (
30 Expr::Symbol(Symbol::new("fields")),
31 Expr::Map(
32 self.fields
33 .into_iter()
34 .map(|(key, value)| (Expr::Symbol(key), value))
35 .collect(),
36 ),
37 ),
38 ])),
39 }
40 }
41
42 pub fn parse(expr: &Expr) -> Option<Self> {
44 let Expr::Extension { tag, payload } = expr else {
45 return None;
46 };
47 if *tag != Symbol::qualified("expr", "object") {
48 return None;
49 }
50 let Expr::Map(entries) = payload.as_ref() else {
51 return None;
52 };
53 let mut class = None;
54 let mut fields = None;
55 for (key, value) in entries {
56 let Expr::Symbol(key) = key else {
57 continue;
58 };
59 if *key == Symbol::new("class") {
60 if let Expr::Symbol(symbol) = value {
61 class = Some(symbol.clone());
62 }
63 } else if *key == Symbol::new("fields")
64 && let Expr::Map(entries) = value
65 {
66 let parsed = entries
67 .iter()
68 .map(|(field, value)| match field {
69 Expr::Symbol(symbol) => Some((symbol.clone(), value.clone())),
70 _ => None,
71 })
72 .collect::<Option<Vec<_>>>();
73 fields = parsed;
74 }
75 }
76 Some(Self {
77 class: class?,
78 fields: fields?,
79 })
80 }
81
82 pub fn field(&self, name: &Symbol) -> Option<&Expr> {
84 self.fields
85 .iter()
86 .find_map(|(field, value)| (field == name).then_some(value))
87 }
88}
89
90pub struct FieldSpec {
93 pub(crate) name: Symbol,
94 pub(crate) shape: Arc<dyn Shape>,
95 pub(crate) required: bool,
96}
97
98impl FieldSpec {
99 pub fn required(name: Symbol, shape: Arc<dyn Shape>) -> Self {
101 Self {
102 name,
103 shape,
104 required: true,
105 }
106 }
107
108 pub fn name(&self) -> &Symbol {
110 &self.name
111 }
112
113 pub fn shape(&self) -> &Arc<dyn Shape> {
115 &self.shape
116 }
117}
118
119pub struct FieldShape {
125 class: Option<Symbol>,
126 fields: Vec<FieldSpec>,
127}
128
129impl FieldShape {
130 pub fn new(class: Symbol, fields: Vec<FieldSpec>) -> Self {
132 Self {
133 class: Some(class),
134 fields,
135 }
136 }
137
138 pub fn anonymous(fields: Vec<FieldSpec>) -> Self {
140 Self {
141 class: None,
142 fields,
143 }
144 }
145
146 pub fn class_symbol(&self) -> Option<&Symbol> {
148 self.class.as_ref()
149 }
150
151 pub fn fields(&self) -> &[FieldSpec] {
153 &self.fields
154 }
155
156 fn match_entries(
157 &self,
158 cx: &mut Cx,
159 class: Option<&Symbol>,
160 entries: &[(Symbol, Expr)],
161 ) -> Result<ShapeMatch> {
162 if let Some(expected) = &self.class
163 && class != Some(expected)
164 {
165 return Ok(ShapeMatch::reject(format!("expected class {}", expected)));
166 }
167
168 let mut matched = ShapeMatch::accept(MatchScore::exact(20));
169 for spec in &self.fields {
170 let Some(value) = entries
171 .iter()
172 .find_map(|(name, value)| (name == &spec.name).then_some(value))
173 else {
174 if spec.required {
175 return Ok(ShapeMatch::reject(format!("missing field {}", spec.name)));
176 }
177 continue;
178 };
179 let field_match = spec.shape.check_expr(cx, value)?;
180 if !field_match.accepted {
181 return Ok(field_match);
182 }
183 matched.captures.extend(field_match.captures);
184 matched.score += field_match.score;
185 }
186 Ok(matched)
187 }
188}
189
190impl Shape for FieldShape {
191 fn check_value(&self, cx: &mut Cx, value: Value) -> Result<ShapeMatch> {
192 let expr = value.object().as_expr(cx)?;
193 self.check_expr(cx, &expr)
194 }
195
196 fn check_expr(&self, cx: &mut Cx, expr: &Expr) -> Result<ShapeMatch> {
197 if let Some(object) = ObjectExpr::parse(expr) {
198 return self.match_entries(cx, Some(&object.class), &object.fields);
199 }
200 if self.class.is_none()
201 && let Expr::Map(entries) = expr
202 {
203 let entries = entries
204 .iter()
205 .map(|(key, value)| match key {
206 Expr::Symbol(symbol) => Some((symbol.clone(), value.clone())),
207 _ => None,
208 })
209 .collect::<Option<Vec<_>>>();
210 if let Some(entries) = entries {
211 return self.match_entries(cx, None, &entries);
212 }
213 }
214 Ok(ShapeMatch::reject("expected object fields"))
215 }
216
217 fn describe(&self, cx: &mut Cx) -> Result<ShapeDoc> {
218 let mut doc = match &self.class {
219 Some(class) => ShapeDoc::new(format!("fields {}", class)),
220 None => ShapeDoc::new("fields"),
221 };
222 for spec in &self.fields {
223 let detail = spec.shape.describe(cx)?;
224 doc = doc.with_detail(format!("{}: {}", spec.name, detail.name));
225 }
226 Ok(doc)
227 }
228}