@@ -172,6 +172,81 @@ impl<N: Display> Fragment<N> {
172
172
}
173
173
}
174
174
175
+ /// An [`UnreservedId`] that cannot be reserved JSON schema keywords
176
+ /// like `Set`, `Long`, and etc.
177
+ #[ derive( Debug , Clone , PartialEq , Eq , Hash , Serialize ) ]
178
+ #[ cfg_attr( feature = "wasm" , derive( tsify:: Tsify ) ) ]
179
+ #[ cfg_attr( feature = "wasm" , tsify( into_wasm_abi, from_wasm_abi) ) ]
180
+ pub struct CommonTypeId ( #[ cfg_attr( feature = "wasm" , tsify( type = "string" ) ) ] UnreservedId ) ;
181
+
182
+ impl From < CommonTypeId > for UnreservedId {
183
+ fn from ( value : CommonTypeId ) -> Self {
184
+ value. 0
185
+ }
186
+ }
187
+
188
+ impl CommonTypeId {
189
+ /// Create a [`CommonTypeId`] based on an [`UnreservedId`] but do not check
190
+ /// if the latter is valid or not
191
+ pub fn unchecked ( id : UnreservedId ) -> Self {
192
+ Self ( id)
193
+ }
194
+ }
195
+
196
+ impl Display for CommonTypeId {
197
+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
198
+ self . 0 . fmt ( f)
199
+ }
200
+ }
201
+
202
+ #[ cfg( feature = "arbitrary" ) ]
203
+ impl < ' a > arbitrary:: Arbitrary < ' a > for CommonTypeId {
204
+ fn arbitrary ( u : & mut arbitrary:: Unstructured < ' a > ) -> arbitrary:: Result < Self > {
205
+ let id: UnreservedId = u. arbitrary ( ) ?;
206
+ if is_reserved_schema_keyword ( & id) {
207
+ // PANIC SAFETY: `_Bool`, `_Record`, and etc are valid common type names as well as valid unreserved names.
208
+ #[ allow( clippy:: unwrap_used) ]
209
+ let new_id = format ! ( "_{id}" ) . parse ( ) . unwrap ( ) ;
210
+ Ok ( CommonTypeId :: unchecked ( new_id) )
211
+ } else {
212
+ Ok ( CommonTypeId :: unchecked ( id) )
213
+ }
214
+ }
215
+
216
+ fn size_hint ( depth : usize ) -> ( usize , Option < usize > ) {
217
+ <UnreservedId as arbitrary:: Arbitrary >:: size_hint ( depth)
218
+ }
219
+ }
220
+
221
+ // Test if this id is a reserved JSON schema keyword.
222
+ // Issues:
223
+ // https://github.com/cedar-policy/cedar/issues/1070
224
+ // https://github.com/cedar-policy/cedar/issues/1139
225
+ pub ( crate ) fn is_reserved_schema_keyword ( id : & UnreservedId ) -> bool {
226
+ matches ! (
227
+ id. as_ref( ) ,
228
+ "Bool" | "Boolean" | "Entity" | "Extension" | "Long" | "Record" | "Set" | "String"
229
+ )
230
+ }
231
+
232
+ /// Deserialize a [`CommonTypeId`]
233
+ impl < ' de > Deserialize < ' de > for CommonTypeId {
234
+ fn deserialize < D > ( deserializer : D ) -> std:: result:: Result < Self , D :: Error >
235
+ where
236
+ D : Deserializer < ' de > ,
237
+ {
238
+ UnreservedId :: deserialize ( deserializer) . and_then ( |id| {
239
+ if is_reserved_schema_keyword ( & id) {
240
+ Err ( serde:: de:: Error :: custom ( format ! (
241
+ "Used reserved schema keyword: {id} "
242
+ ) ) )
243
+ } else {
244
+ Ok ( Self ( id) )
245
+ }
246
+ } )
247
+ }
248
+ }
249
+
175
250
/// A single namespace definition from a Fragment.
176
251
/// This is composed of common types, entity types, and action definitions.
177
252
///
@@ -194,7 +269,7 @@ pub struct NamespaceDefinition<N> {
194
269
#[ serde( default ) ]
195
270
#[ serde( skip_serializing_if = "HashMap::is_empty" ) ]
196
271
#[ serde( with = "::serde_with::rust::maps_duplicate_key_is_error" ) ]
197
- pub common_types : HashMap < UnreservedId , Type < N > > ,
272
+ pub common_types : HashMap < CommonTypeId , Type < N > > ,
198
273
#[ serde( with = "::serde_with::rust::maps_duplicate_key_is_error" ) ]
199
274
pub entity_types : HashMap < UnreservedId , EntityType < N > > ,
200
275
#[ serde( with = "::serde_with::rust::maps_duplicate_key_is_error" ) ]
0 commit comments