@@ -44,6 +44,8 @@ pub enum SchemaType {
44
44
Record {
45
45
/// Attributes and their types
46
46
attrs : HashMap < SmolStr , AttributeType > ,
47
+ /// Can a record with this type have attributes other than those specified in `attrs`
48
+ open_attrs : bool ,
47
49
} ,
48
50
/// Entity
49
51
Entity {
@@ -119,7 +121,16 @@ impl SchemaType {
119
121
( Set { element_ty : elty1 } , Set { element_ty : elty2 } ) => {
120
122
elty1. is_consistent_with ( elty2)
121
123
}
122
- ( Record { attrs : attrs1 } , Record { attrs : attrs2 } ) => {
124
+ (
125
+ Record {
126
+ attrs : attrs1,
127
+ open_attrs : open1,
128
+ } ,
129
+ Record {
130
+ attrs : attrs2,
131
+ open_attrs : open2,
132
+ } ,
133
+ ) => {
123
134
attrs1. iter ( ) . all ( |( k, v) | {
124
135
match attrs2. get ( k) {
125
136
Some ( ty) => {
@@ -129,9 +140,9 @@ impl SchemaType {
129
140
}
130
141
None => {
131
142
// attrs1 has the attribute, attrs2 does not.
132
- // if required in attrs1, incompatible.
133
- // otherwise fine
134
- !v. required
143
+ // if required in attrs1 and attrs2 is
144
+ // closed, incompatible. otherwise fine
145
+ !v. required || * open2
135
146
}
136
147
}
137
148
} ) && attrs2. iter ( ) . all ( |( k, v) | {
@@ -143,9 +154,9 @@ impl SchemaType {
143
154
}
144
155
None => {
145
156
// attrs2 has the attribute, attrs1 does not.
146
- // if required in attrs2, incompatible.
147
- // otherwise fine
148
- !v. required
157
+ // if required in attrs2 and attrs1 is closed,
158
+ // incompatible. otherwise fine
159
+ !v. required || * open1
149
160
}
150
161
}
151
162
} )
@@ -192,11 +203,17 @@ impl std::fmt::Display for SchemaType {
192
203
Self :: String => write ! ( f, "string" ) ,
193
204
Self :: Set { element_ty } => write ! ( f, "(set of {})" , & element_ty) ,
194
205
Self :: EmptySet => write ! ( f, "empty-set" ) ,
195
- Self :: Record { attrs } => {
196
- if attrs. is_empty ( ) {
206
+ Self :: Record { attrs, open_attrs } => {
207
+ if attrs. is_empty ( ) && * open_attrs {
208
+ write ! ( f, "any record" )
209
+ } else if attrs. is_empty ( ) {
197
210
write ! ( f, "empty record" )
198
211
} else {
199
- write ! ( f, "record with attributes: {{" ) ?;
212
+ if * open_attrs {
213
+ write ! ( f, "record with at least attributes: {{" ) ?;
214
+ } else {
215
+ write ! ( f, "record with attributes: {{" ) ?;
216
+ }
200
217
// sorting attributes ensures that there is a single, deterministic
201
218
// Display output for each `SchemaType`, which is important for
202
219
// tests that check equality of error messages
@@ -324,7 +341,8 @@ pub fn schematype_of_restricted_expr(
324
341
// but marking it optional is more flexible -- allows the
325
342
// attribute type to `is_consistent_with()` more types
326
343
Ok ( ( k. clone ( ) , AttributeType :: optional ( attr_type) ) )
327
- } ) . collect :: < Result < HashMap < _ , _ > , GetSchemaTypeError > > ( ) ?
344
+ } ) . collect :: < Result < HashMap < _ , _ > , GetSchemaTypeError > > ( ) ?,
345
+ open_attrs : false ,
328
346
} )
329
347
}
330
348
ExprKind :: ExtensionFunctionApp { fn_name, .. } => {
@@ -370,6 +388,7 @@ pub fn schematype_of_value(value: &Value) -> Result<SchemaType, HeterogeneousSet
370
388
. iter ( )
371
389
. map ( |( k, v) | Ok ( ( k. clone ( ) , AttributeType :: required ( schematype_of_value ( v) ?) ) ) )
372
390
. collect :: < Result < _ , HeterogeneousSetError > > ( ) ?,
391
+ open_attrs : false ,
373
392
} ) ,
374
393
Value :: ExtensionValue ( ev) => Ok ( SchemaType :: Extension {
375
394
name : ev. typename ( ) ,
0 commit comments