1use std::{collections::BTreeSet, error::Error, fmt};
4
5use sim_kernel::{ClassId, ClassRef, ReadConstructorRef, Ref, ShapeRef, Symbol, Value};
6
7#[derive(Clone, Debug, PartialEq, Eq)]
9pub struct ClassIdentity {
10 id: ClassId,
11 symbol: Symbol,
12}
13
14impl ClassIdentity {
15 pub fn checked(id: ClassId, symbol: Symbol) -> Result<Self, ClassDescriptorError> {
17 validate_symbol(&symbol).map_err(|reason| ClassDescriptorError::InvalidIdentity {
18 name: symbol.clone(),
19 reason,
20 })?;
21 Ok(Self { id, symbol })
22 }
23
24 pub fn id(&self) -> ClassId {
25 self.id
26 }
27 pub fn symbol(&self) -> &Symbol {
28 &self.symbol
29 }
30}
31
32#[derive(Clone, Debug)]
34pub enum DeclaredParent {
35 Resolved {
37 identity: ClassIdentity,
38 class: ClassRef,
39 },
40 Unresolved {
42 identity: ClassIdentity,
43 reference: Ref,
44 },
45}
46
47impl DeclaredParent {
48 pub fn resolved(identity: ClassIdentity, class: ClassRef) -> Self {
49 Self::Resolved { identity, class }
50 }
51 pub fn unresolved(identity: ClassIdentity, reference: Ref) -> Self {
52 Self::Unresolved {
53 identity,
54 reference,
55 }
56 }
57 pub fn identity(&self) -> &ClassIdentity {
58 match self {
59 Self::Resolved { identity, .. } | Self::Unresolved { identity, .. } => identity,
60 }
61 }
62 pub fn unresolved_reference(&self) -> Option<&Ref> {
63 match self {
64 Self::Unresolved { reference, .. } => Some(reference),
65 Self::Resolved { .. } => None,
66 }
67 }
68 pub fn resolved_class(&self) -> Option<&ClassRef> {
69 match self {
70 Self::Resolved { class, .. } => Some(class),
71 Self::Unresolved { .. } => None,
72 }
73 }
74}
75
76#[derive(Clone, Debug)]
78pub struct MemberShape {
79 pub name: Symbol,
80 pub shape: ShapeRef,
81}
82
83#[derive(Clone, Debug)]
85pub struct OpenMetadataEntry {
86 pub name: Symbol,
87 pub value: Value,
88}
89
90#[derive(Clone, Debug)]
92pub struct ReadConstruction {
93 pub constructor: ReadConstructorRef,
94 pub args_shape: ShapeRef,
95}
96
97#[derive(Clone, Debug)]
99pub struct ClassDescriptorInput {
100 pub identity: ClassIdentity,
101 pub parents: Vec<DeclaredParent>,
102 pub constructor_shape: ShapeRef,
103 pub instance_shape: ShapeRef,
104 pub members: Vec<MemberShape>,
105 pub read_construction: Option<ReadConstruction>,
106 pub metadata: Vec<OpenMetadataEntry>,
107}
108
109#[derive(Clone, Debug)]
111pub struct ClassDescriptor {
112 input: ClassDescriptorInput,
113}
114
115impl ClassDescriptor {
116 pub fn new(input: ClassDescriptorInput) -> Result<Self, ClassDescriptorError> {
117 validate_shape(Symbol::new("constructor"), &input.constructor_shape)?;
118 validate_shape(Symbol::new("instance"), &input.instance_shape)?;
119
120 let mut parents = BTreeSet::new();
121 for parent in &input.parents {
122 let identity = parent.identity();
123 if identity.id == input.identity.id {
124 return Err(ClassDescriptorError::SelfParent {
125 name: identity.symbol.clone(),
126 });
127 }
128 if !parents.insert(identity.id) {
129 return Err(ClassDescriptorError::DuplicateParent {
130 name: identity.symbol.clone(),
131 });
132 }
133 if let DeclaredParent::Resolved { class, .. } = parent {
134 let Some(actual) = class.object().as_class() else {
135 return Err(ClassDescriptorError::InvalidParent {
136 name: identity.symbol.clone(),
137 reason: "resolved value is not a class",
138 });
139 };
140 if actual.id() != identity.id || actual.symbol() != identity.symbol {
141 return Err(ClassDescriptorError::InvalidParent {
142 name: identity.symbol.clone(),
143 reason: "resolved class identity does not match its declaration",
144 });
145 }
146 }
147 }
148
149 let mut members = BTreeSet::new();
150 for member in &input.members {
151 validate_symbol(&member.name).map_err(|reason| {
152 ClassDescriptorError::InvalidMember {
153 name: member.name.clone(),
154 reason,
155 }
156 })?;
157 if !members.insert(member.name.clone()) {
158 return Err(ClassDescriptorError::DuplicateMember {
159 name: member.name.clone(),
160 });
161 }
162 validate_shape(member.name.clone(), &member.shape)?;
163 }
164
165 if let Some(read) = &input.read_construction {
166 if read.constructor.object().as_read_constructor().is_none() {
167 return Err(ClassDescriptorError::InvalidReadConstructor);
168 }
169 validate_shape(Symbol::new("read-constructor"), &read.args_shape)?;
170 }
171
172 let mut metadata = BTreeSet::new();
173 for entry in &input.metadata {
174 validate_symbol(&entry.name).map_err(|reason| {
175 ClassDescriptorError::InvalidMetadata {
176 name: entry.name.clone(),
177 reason,
178 }
179 })?;
180 if !metadata.insert(entry.name.clone()) {
181 return Err(ClassDescriptorError::DuplicateMetadata {
182 name: entry.name.clone(),
183 });
184 }
185 }
186 Ok(Self { input })
187 }
188
189 pub fn identity(&self) -> &ClassIdentity {
190 &self.input.identity
191 }
192 pub fn parents(&self) -> &[DeclaredParent] {
193 &self.input.parents
194 }
195 pub fn constructor_shape(&self) -> &ShapeRef {
196 &self.input.constructor_shape
197 }
198 pub fn instance_shape(&self) -> &ShapeRef {
199 &self.input.instance_shape
200 }
201 pub fn members(&self) -> &[MemberShape] {
202 &self.input.members
203 }
204 pub fn read_construction(&self) -> Option<&ReadConstruction> {
205 self.input.read_construction.as_ref()
206 }
207 pub fn metadata(&self) -> &[OpenMetadataEntry] {
208 &self.input.metadata
209 }
210}
211
212fn validate_shape(name: Symbol, shape: &ShapeRef) -> Result<(), ClassDescriptorError> {
213 if shape.object().as_shape().is_none() {
214 return Err(ClassDescriptorError::MalformedShape { name });
215 }
216 Ok(())
217}
218
219fn validate_symbol(symbol: &Symbol) -> Result<(), &'static str> {
220 if symbol.name.is_empty() {
221 return Err("name is empty");
222 }
223 Symbol::checked(symbol.name.clone()).map_err(|_| "name is malformed")?;
224 if let Some(namespace) = &symbol.namespace {
225 if namespace.is_empty() {
226 return Err("namespace is empty");
227 }
228 Symbol::checked(namespace.clone()).map_err(|_| "namespace is malformed")?;
229 }
230 Ok(())
231}
232
233#[derive(Clone, Debug, PartialEq, Eq)]
235pub enum ClassDescriptorError {
236 InvalidIdentity { name: Symbol, reason: &'static str },
237 InvalidParent { name: Symbol, reason: &'static str },
238 DuplicateParent { name: Symbol },
239 SelfParent { name: Symbol },
240 InvalidMember { name: Symbol, reason: &'static str },
241 DuplicateMember { name: Symbol },
242 MalformedShape { name: Symbol },
243 InvalidReadConstructor,
244 InvalidMetadata { name: Symbol, reason: &'static str },
245 DuplicateMetadata { name: Symbol },
246}
247
248impl fmt::Display for ClassDescriptorError {
249 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
250 write!(f, "{self:?}")
251 }
252}
253impl Error for ClassDescriptorError {}
254
255#[cfg(test)]
256mod tests {
257 use std::sync::Arc;
258
259 use sim_kernel::{Cx, Expr, MatchScore, Shape, ShapeDoc, ShapeMatch};
260
261 use super::*;
262
263 struct AnyShape;
264 impl Shape for AnyShape {
265 fn check_value(&self, _cx: &mut Cx, _value: Value) -> sim_kernel::Result<ShapeMatch> {
266 Ok(ShapeMatch::accept(MatchScore::exact(1)))
267 }
268 fn check_expr(&self, _cx: &mut Cx, _expr: &Expr) -> sim_kernel::Result<ShapeMatch> {
269 Ok(ShapeMatch::accept(MatchScore::exact(1)))
270 }
271 fn describe(&self, _cx: &mut Cx) -> sim_kernel::Result<ShapeDoc> {
272 Ok(ShapeDoc::new("any"))
273 }
274 }
275
276 fn shape(cx: &Cx) -> ShapeRef {
277 cx.factory().opaque(Arc::new(AnyShape)).unwrap()
278 }
279
280 fn identity(id: u32, name: &str) -> ClassIdentity {
281 ClassIdentity::checked(ClassId(id), Symbol::qualified("test", name)).unwrap()
282 }
283
284 fn input(cx: &Cx) -> ClassDescriptorInput {
285 ClassDescriptorInput {
286 identity: identity(40, "Child"),
287 parents: Vec::new(),
288 constructor_shape: shape(cx),
289 instance_shape: shape(cx),
290 members: Vec::new(),
291 read_construction: None,
292 metadata: Vec::new(),
293 }
294 }
295
296 #[test]
297 fn unresolved_parent_is_preserved_as_unresolved_typed_input() {
298 let cx = sim_kernel::testing::bare_cx();
299 let unresolved = Ref::Symbol(Symbol::qualified("loader", "Parent"));
300 let mut raw = input(&cx);
301 raw.parents.push(DeclaredParent::unresolved(
302 identity(41, "Parent"),
303 unresolved.clone(),
304 ));
305
306 let descriptor = ClassDescriptor::new(raw).unwrap();
307 assert_eq!(descriptor.parents().len(), 1);
308 assert_eq!(
309 descriptor.parents()[0].unresolved_reference(),
310 Some(&unresolved)
311 );
312 assert!(descriptor.parents()[0].resolved_class().is_none());
313 }
314
315 #[test]
316 fn duplicate_member_reports_the_offending_name() {
317 let cx = sim_kernel::testing::bare_cx();
318 let mut raw = input(&cx);
319 let name = Symbol::new("answer");
320 raw.members.push(MemberShape {
321 name: name.clone(),
322 shape: shape(&cx),
323 });
324 raw.members.push(MemberShape {
325 name: name.clone(),
326 shape: shape(&cx),
327 });
328 assert_eq!(
329 ClassDescriptor::new(raw).unwrap_err(),
330 ClassDescriptorError::DuplicateMember { name }
331 );
332 }
333
334 #[test]
335 fn malformed_member_shape_reports_the_offending_name() {
336 let cx = sim_kernel::testing::bare_cx();
337 let mut raw = input(&cx);
338 let name = Symbol::new("broken");
339 raw.members.push(MemberShape {
340 name: name.clone(),
341 shape: cx.factory().nil().unwrap(),
342 });
343 assert_eq!(
344 ClassDescriptor::new(raw).unwrap_err(),
345 ClassDescriptorError::MalformedShape { name }
346 );
347 }
348
349 #[test]
350 fn self_parent_reports_the_declared_parent_name() {
351 let cx = sim_kernel::testing::bare_cx();
352 let mut raw = input(&cx);
353 let own_identity = raw.identity.clone();
354 let name = own_identity.symbol().clone();
355 raw.parents.push(DeclaredParent::unresolved(
356 own_identity,
357 Ref::Symbol(name.clone()),
358 ));
359 assert_eq!(
360 ClassDescriptor::new(raw).unwrap_err(),
361 ClassDescriptorError::SelfParent { name }
362 );
363 }
364}