diff --git a/cedar-drt/fuzz/src/schemas.rs b/cedar-drt/fuzz/src/schemas.rs index cbc68c1b1..a4e36006f 100644 --- a/cedar-drt/fuzz/src/schemas.rs +++ b/cedar-drt/fuzz/src/schemas.rs @@ -278,8 +278,11 @@ impl Equiv for cedar_policy_validator::types::AttributeType { impl Equiv for json_schema::Type { fn equiv(lhs: &Self, rhs: &Self) -> Result<(), String> { match (lhs, rhs) { - (Self::Type(lhs), Self::Type(rhs)) => Equiv::equiv(lhs, rhs), - (Self::CommonTypeRef { type_name: lhs }, Self::CommonTypeRef { type_name: rhs }) => { + (Self::Type { ty: lhs, .. }, Self::Type { ty: rhs, .. }) => Equiv::equiv(lhs, rhs), + ( + Self::CommonTypeRef { type_name: lhs, .. }, + Self::CommonTypeRef { type_name: rhs, .. }, + ) => { if lhs == rhs { Ok(()) } else { @@ -288,9 +291,9 @@ impl Equiv for json_schema::T )) } } - (Self::Type(tv), Self::CommonTypeRef { type_name }) - | (Self::CommonTypeRef { type_name }, Self::Type(tv)) => { - match tv { + (Self::Type { ty, .. }, Self::CommonTypeRef { type_name, .. }) + | (Self::CommonTypeRef { type_name, .. }, Self::Type { ty, .. }) => { + match ty { json_schema::TypeVariant::EntityOrCommon { type_name: tv_type_name, } if type_name == tv_type_name => { @@ -298,7 +301,7 @@ impl Equiv for json_schema::T Ok(()) } _ => Err(format!( - "Common-type `{type_name}` is not equivalent to non-common-type {tv:?}" + "Common-type `{type_name}` is not equivalent to non-common-type {ty:?}" )), } } diff --git a/cedar-policy-generators/src/expr.rs b/cedar-policy-generators/src/expr.rs index 0cdf72b6e..a458cfa60 100644 --- a/cedar-policy-generators/src/expr.rs +++ b/cedar-policy-generators/src/expr.rs @@ -572,7 +572,7 @@ impl<'a> ExprGenerator<'a> { u, )?, self.generate_expr_for_schematype( - &json_schema::Type::Type(json_schema::TypeVariant::String), + &json_schema::Type::Type { ty: json_schema::TypeVariant::String, loc: None }, max_depth - 1, u, )?, @@ -598,11 +598,12 @@ impl<'a> ExprGenerator<'a> { let attr_name = SmolStr::clone(u.choose(&attr_names)?); Ok(ast::Expr::has_attr( self.generate_expr_for_schematype( - &json_schema::Type::Type( - json_schema::TypeVariant::Entity { + &json_schema::Type::Type { + ty: json_schema::TypeVariant::Entity { name: ast::Name::from(entity_name.clone()).into(), - } - ), + }, + loc: None, + }, max_depth - 1, u, )?, @@ -768,7 +769,7 @@ impl<'a> ExprGenerator<'a> { u, )?, self.generate_expr_for_schematype( - &json_schema::Type::Type(json_schema::TypeVariant::String), + &json_schema::Type::Type { ty: json_schema::TypeVariant::String, loc: None }, max_depth - 1, u, )?, @@ -857,7 +858,7 @@ impl<'a> ExprGenerator<'a> { u, )?, self.generate_expr_for_schematype( - &json_schema::Type::Type(json_schema::TypeVariant::String), + &json_schema::Type::Type { ty: json_schema::TypeVariant::String, loc: None }, max_depth - 1, u, )?, @@ -961,7 +962,7 @@ impl<'a> ExprGenerator<'a> { u, )?, self.generate_expr_for_schematype( - &json_schema::Type::Type(json_schema::TypeVariant::String), + &json_schema::Type::Type { ty: json_schema::TypeVariant::String, loc: None }, max_depth - 1, u, )?, @@ -1070,7 +1071,7 @@ impl<'a> ExprGenerator<'a> { u, )?, self.generate_expr_for_schematype( - &json_schema::Type::Type(json_schema::TypeVariant::String), + &json_schema::Type::Type { ty: json_schema::TypeVariant::String, loc: None }, max_depth - 1, u, )?, @@ -1169,7 +1170,7 @@ impl<'a> ExprGenerator<'a> { u, )?, self.generate_expr_for_schematype( - &json_schema::Type::Type(json_schema::TypeVariant::String), + &json_schema::Type::Type { ty: json_schema::TypeVariant::String, loc: None }, max_depth - 1, u, )?, @@ -1275,7 +1276,7 @@ impl<'a> ExprGenerator<'a> { u, )?, self.generate_expr_for_schematype( - &json_schema::Type::Type(json_schema::TypeVariant::String), + &json_schema::Type::Type { ty: json_schema::TypeVariant::String, loc: None }, max_depth - 1, u, )?, @@ -1303,39 +1304,54 @@ impl<'a> ExprGenerator<'a> { u: &mut Unstructured<'_>, ) -> Result { match target_type { - json_schema::Type::CommonTypeRef { type_name } => self.generate_expr_for_schematype( - lookup_common_type(&self.schema.schema, type_name) - .unwrap_or_else(|| panic!("reference to undefined common type: {type_name}")), - max_depth, - u, - ), - json_schema::Type::Type(json_schema::TypeVariant::EntityOrCommon { type_name }) => { + json_schema::Type::CommonTypeRef { type_name, .. } => self + .generate_expr_for_schematype( + lookup_common_type(&self.schema.schema, type_name).unwrap_or_else(|| { + panic!("reference to undefined common type: {type_name}") + }), + max_depth, + u, + ), + json_schema::Type::Type { + ty: json_schema::TypeVariant::EntityOrCommon { type_name }, + .. + } => { match lookup_common_type(&self.schema.schema, type_name) { Some(ty) => self.generate_expr_for_schematype(ty, max_depth, u), None => { // must be an entity reference, so treat it as we treat entity references self.generate_expr_for_schematype( - &json_schema::Type::Type(json_schema::TypeVariant::Entity { - name: type_name.clone(), - }), + &json_schema::Type::Type { + ty: json_schema::TypeVariant::Entity { + name: type_name.clone(), + }, + loc: None, + }, max_depth, u, ) } } } - json_schema::Type::Type(json_schema::TypeVariant::Boolean) => { - self.generate_expr_for_type(&Type::bool(), max_depth, u) - } - json_schema::Type::Type(json_schema::TypeVariant::Long) => { - self.generate_expr_for_type(&Type::long(), max_depth, u) - } - json_schema::Type::Type(json_schema::TypeVariant::String) => { - self.generate_expr_for_type(&Type::string(), max_depth, u) - } - json_schema::Type::Type(json_schema::TypeVariant::Set { - element: element_ty, - }) => { + json_schema::Type::Type { + ty: json_schema::TypeVariant::Boolean, + .. + } => self.generate_expr_for_type(&Type::bool(), max_depth, u), + json_schema::Type::Type { + ty: json_schema::TypeVariant::Long, + .. + } => self.generate_expr_for_type(&Type::long(), max_depth, u), + json_schema::Type::Type { + ty: json_schema::TypeVariant::String, + .. + } => self.generate_expr_for_type(&Type::string(), max_depth, u), + json_schema::Type::Type { + ty: + json_schema::TypeVariant::Set { + element: element_ty, + }, + .. + } => { if max_depth == 0 || u.len() < 10 { // no recursion allowed, so, just do empty-set Ok(ast::Expr::set(vec![])) @@ -1417,7 +1433,7 @@ impl<'a> ExprGenerator<'a> { u, )?, self.generate_expr_for_schematype( - &json_schema::Type::Type(json_schema::TypeVariant::String), + &json_schema::Type::Type { ty: json_schema::TypeVariant::String, loc: None }, max_depth - 1, u, )?, @@ -1425,12 +1441,14 @@ impl<'a> ExprGenerator<'a> { }) } } - json_schema::Type::Type(json_schema::TypeVariant::Record( - json_schema::RecordType { - attributes, - additional_attributes, - }, - )) => { + json_schema::Type::Type { + ty: + json_schema::TypeVariant::Record(json_schema::RecordType { + attributes, + additional_attributes, + }), + .. + } => { if max_depth == 0 || u.len() < 10 { // no recursion allowed Err(Error::TooDeep) @@ -1553,7 +1571,7 @@ impl<'a> ExprGenerator<'a> { u, )?, self.generate_expr_for_schematype( - &json_schema::Type::Type(json_schema::TypeVariant::String), + &json_schema::Type::Type { ty: json_schema::TypeVariant::String, loc: None }, max_depth - 1, u, )?, @@ -1561,7 +1579,10 @@ impl<'a> ExprGenerator<'a> { }) } } - json_schema::Type::Type(json_schema::TypeVariant::Entity { name }) => { + json_schema::Type::Type { + ty: json_schema::TypeVariant::Entity { name }, + .. + } => { if max_depth == 0 || u.len() < 10 { // no recursion allowed, so, just do `principal`, `action`, or `resource` Ok(ast::Expr::var(*u.choose(&[ @@ -1652,7 +1673,7 @@ impl<'a> ExprGenerator<'a> { u, )?, self.generate_expr_for_schematype( - &json_schema::Type::Type(json_schema::TypeVariant::String), + &json_schema::Type::Type { ty: json_schema::TypeVariant::String, loc: None }, max_depth - 1, u, )?, @@ -1660,15 +1681,16 @@ impl<'a> ExprGenerator<'a> { }) } } - json_schema::Type::Type(json_schema::TypeVariant::Extension { name }) => { - match name.as_ref() { - "ipaddr" => self.generate_expr_for_type(&Type::ipaddr(), max_depth, u), - "decimal" => self.generate_expr_for_type(&Type::decimal(), max_depth, u), - "datetime" => self.generate_expr_for_type(&Type::datetime(), max_depth, u), - "duration" => self.generate_expr_for_type(&Type::duration(), max_depth, u), - _ => panic!("unrecognized extension type: {name:?}"), - } - } + json_schema::Type::Type { + ty: json_schema::TypeVariant::Extension { name }, + .. + } => match name.as_ref() { + "ipaddr" => self.generate_expr_for_type(&Type::ipaddr(), max_depth, u), + "decimal" => self.generate_expr_for_type(&Type::decimal(), max_depth, u), + "datetime" => self.generate_expr_for_type(&Type::datetime(), max_depth, u), + "duration" => self.generate_expr_for_type(&Type::duration(), max_depth, u), + _ => panic!("unrecognized extension type: {name:?}"), + }, } } @@ -1759,7 +1781,7 @@ impl<'a> ExprGenerator<'a> { u: &mut Unstructured<'_>, ) -> Result { match target_type { - json_schema::Type::CommonTypeRef { type_name } => self + json_schema::Type::CommonTypeRef { type_name, .. } => self .generate_ext_func_call_for_schematype( lookup_common_type(&self.schema.schema, type_name).unwrap_or_else(|| { panic!("reference to undefined common type: {type_name}") @@ -1767,16 +1789,19 @@ impl<'a> ExprGenerator<'a> { max_depth, u, ), - json_schema::Type::Type(ty) => match ty { + json_schema::Type::Type { ty, .. } => match ty { json_schema::TypeVariant::EntityOrCommon { type_name } => { match lookup_common_type(&self.schema.schema, type_name) { Some(ty) => self.generate_ext_func_call_for_schematype(ty, max_depth, u), None => { // must be an entity reference, so treat it how we treat entity references self.generate_ext_func_call_for_schematype( - &json_schema::Type::Type(json_schema::TypeVariant::Entity { - name: type_name.clone(), - }), + &json_schema::Type::Type { + ty: json_schema::TypeVariant::Entity { + name: type_name.clone(), + }, + loc: None, + }, max_depth, u, ) @@ -1983,7 +2008,7 @@ impl<'a> ExprGenerator<'a> { u: &mut Unstructured<'_>, ) -> Result { match target_type { - json_schema::Type::CommonTypeRef { type_name } => self + json_schema::Type::CommonTypeRef { type_name, .. } => self .generate_attr_value_for_schematype( lookup_common_type(&self.schema.schema, type_name).unwrap_or_else(|| { panic!("reference to undefined common type: {type_name}") @@ -1991,33 +2016,46 @@ impl<'a> ExprGenerator<'a> { max_depth, u, ), - json_schema::Type::Type(json_schema::TypeVariant::EntityOrCommon { type_name }) => { + json_schema::Type::Type { + ty: json_schema::TypeVariant::EntityOrCommon { type_name }, + .. + } => { match lookup_common_type(&self.schema.schema, type_name) { Some(ty) => self.generate_attr_value_for_schematype(ty, max_depth, u), None => { // must be an entity reference, so treat it how we treat entity references self.generate_attr_value_for_schematype( - &json_schema::Type::Type(json_schema::TypeVariant::Entity { - name: type_name.clone(), - }), + &json_schema::Type::Type { + ty: json_schema::TypeVariant::Entity { + name: type_name.clone(), + }, + loc: type_name.loc().cloned(), + }, max_depth, u, ) } } } - json_schema::Type::Type(json_schema::TypeVariant::Boolean) => { - self.generate_attr_value_for_type(&Type::bool(), max_depth, u) - } - json_schema::Type::Type(json_schema::TypeVariant::Long) => { - self.generate_attr_value_for_type(&Type::long(), max_depth, u) - } - json_schema::Type::Type(json_schema::TypeVariant::String) => { - self.generate_attr_value_for_type(&Type::string(), max_depth, u) - } - json_schema::Type::Type(json_schema::TypeVariant::Set { - element: element_ty, - }) => { + json_schema::Type::Type { + ty: json_schema::TypeVariant::Boolean, + .. + } => self.generate_attr_value_for_type(&Type::bool(), max_depth, u), + json_schema::Type::Type { + ty: json_schema::TypeVariant::Long, + .. + } => self.generate_attr_value_for_type(&Type::long(), max_depth, u), + json_schema::Type::Type { + ty: json_schema::TypeVariant::String, + .. + } => self.generate_attr_value_for_type(&Type::string(), max_depth, u), + json_schema::Type::Type { + ty: + json_schema::TypeVariant::Set { + element: element_ty, + }, + .. + } => { // the only valid Set-typed attribute value is a set literal if max_depth == 0 { // no recursion allowed: just do the empty set @@ -2035,12 +2073,14 @@ impl<'a> ExprGenerator<'a> { Ok(AttrValue::Set(l)) } } - json_schema::Type::Type(json_schema::TypeVariant::Record( - json_schema::RecordType { - attributes, - additional_attributes, - }, - )) => { + json_schema::Type::Type { + ty: + json_schema::TypeVariant::Record(json_schema::RecordType { + attributes, + additional_attributes, + }), + .. + } => { // the only valid Record-typed attribute value is a record literal if max_depth == 0 { // no recursion allowed: quit here @@ -2090,7 +2130,10 @@ impl<'a> ExprGenerator<'a> { Ok(AttrValue::Record(r)) } } - json_schema::Type::Type(json_schema::TypeVariant::Entity { name }) => { + json_schema::Type::Type { + ty: json_schema::TypeVariant::Entity { name }, + .. + } => { // the only valid entity-typed attribute value is a UID literal let entity_type_name = ast::Name::try_from(name.qualify_with_name(self.schema.namespace())) @@ -2100,24 +2143,22 @@ impl<'a> ExprGenerator<'a> { self.arbitrary_uid_with_type(&entity_type_name, u)?, )) } - json_schema::Type::Type(json_schema::TypeVariant::Extension { .. }) - if !self.settings.enable_extensions => - { + json_schema::Type::Type { + ty: json_schema::TypeVariant::Extension { .. }, + .. + } if !self.settings.enable_extensions => { panic!("shouldn't have TypeVariant::Extension with extensions disabled") } - json_schema::Type::Type(json_schema::TypeVariant::Extension { name }) => { - match name.as_ref() { - "ipaddr" => self.generate_attr_value_for_type(&Type::ipaddr(), max_depth, u), - "decimal" => self.generate_attr_value_for_type(&Type::decimal(), max_depth, u), - "datetime" => { - self.generate_attr_value_for_type(&Type::datetime(), max_depth, u) - } - "duration" => { - self.generate_attr_value_for_type(&Type::duration(), max_depth, u) - } - _ => unimplemented!("extension type {name:?}"), - } - } + json_schema::Type::Type { + ty: json_schema::TypeVariant::Extension { name }, + .. + } => match name.as_ref() { + "ipaddr" => self.generate_attr_value_for_type(&Type::ipaddr(), max_depth, u), + "decimal" => self.generate_attr_value_for_type(&Type::decimal(), max_depth, u), + "datetime" => self.generate_attr_value_for_type(&Type::datetime(), max_depth, u), + "duration" => self.generate_attr_value_for_type(&Type::duration(), max_depth, u), + _ => unimplemented!("extension type {name:?}"), + }, } } @@ -2222,39 +2263,54 @@ impl<'a> ExprGenerator<'a> { ) -> Result { use ast::Value; match target_type { - json_schema::Type::CommonTypeRef { type_name } => self.generate_value_for_schematype( - lookup_common_type(&self.schema.schema, type_name) - .unwrap_or_else(|| panic!("reference to undefined common type: {type_name}")), - max_depth, - u, - ), - json_schema::Type::Type(json_schema::TypeVariant::EntityOrCommon { type_name }) => { + json_schema::Type::CommonTypeRef { type_name, .. } => self + .generate_value_for_schematype( + lookup_common_type(&self.schema.schema, type_name).unwrap_or_else(|| { + panic!("reference to undefined common type: {type_name}") + }), + max_depth, + u, + ), + json_schema::Type::Type { + ty: json_schema::TypeVariant::EntityOrCommon { type_name }, + .. + } => { match lookup_common_type(&self.schema.schema, type_name) { Some(ty) => self.generate_value_for_schematype(ty, max_depth, u), None => { // must be an entity reference, so treat it how we treat entity references self.generate_value_for_schematype( - &json_schema::Type::Type(json_schema::TypeVariant::Entity { - name: type_name.clone(), - }), + &json_schema::Type::Type { + ty: json_schema::TypeVariant::Entity { + name: type_name.clone(), + }, + loc: type_name.loc().cloned(), + }, max_depth, u, ) } } } - json_schema::Type::Type(json_schema::TypeVariant::Boolean) => { - self.generate_value_for_type(&Type::bool(), max_depth, u) - } - json_schema::Type::Type(json_schema::TypeVariant::Long) => { - self.generate_value_for_type(&Type::long(), max_depth, u) - } - json_schema::Type::Type(json_schema::TypeVariant::String) => { - self.generate_value_for_type(&Type::string(), max_depth, u) - } - json_schema::Type::Type(json_schema::TypeVariant::Set { - element: element_ty, - }) => { + json_schema::Type::Type { + ty: json_schema::TypeVariant::Boolean, + .. + } => self.generate_value_for_type(&Type::bool(), max_depth, u), + json_schema::Type::Type { + ty: json_schema::TypeVariant::Long, + .. + } => self.generate_value_for_type(&Type::long(), max_depth, u), + json_schema::Type::Type { + ty: json_schema::TypeVariant::String, + .. + } => self.generate_value_for_type(&Type::string(), max_depth, u), + json_schema::Type::Type { + ty: + json_schema::TypeVariant::Set { + element: element_ty, + }, + .. + } => { // the only valid Set-typed attribute value is a set literal if max_depth == 0 { // no recursion allowed: just do the empty set @@ -2268,12 +2324,14 @@ impl<'a> ExprGenerator<'a> { Ok(Value::set(l, None)) } } - json_schema::Type::Type(json_schema::TypeVariant::Record( - json_schema::RecordType { - attributes, - additional_attributes, - }, - )) => { + json_schema::Type::Type { + ty: + json_schema::TypeVariant::Record(json_schema::RecordType { + attributes, + additional_attributes, + }), + .. + } => { // the only valid Record-typed attribute value is a record literal if max_depth == 0 { // no recursion allowed: quit here @@ -2317,7 +2375,10 @@ impl<'a> ExprGenerator<'a> { Ok(Value::record(r, None)) } } - json_schema::Type::Type(json_schema::TypeVariant::Entity { name }) => { + json_schema::Type::Type { + ty: json_schema::TypeVariant::Entity { name }, + .. + } => { // the only valid entity-typed attribute value is a UID literal // The namespace for the entity type is the namespace of the @@ -2500,17 +2561,20 @@ fn record_schematype_with_attr( attr_name: SmolStr, attr_type: impl Into>, ) -> json_schema::Type { - json_schema::Type::Type(json_schema::TypeVariant::Record(json_schema::RecordType { - attributes: [( - attr_name, - json_schema::TypeOfAttribute { - ty: attr_type.into(), - required: true, - annotations: Annotations::new(), - }, - )] - .into_iter() - .collect(), - additional_attributes: true, - })) + json_schema::Type::Type { + ty: json_schema::TypeVariant::Record(json_schema::RecordType { + attributes: [( + attr_name, + json_schema::TypeOfAttribute { + ty: attr_type.into(), + required: true, + annotations: Annotations::new(), + }, + )] + .into_iter() + .collect(), + additional_attributes: true, + }), + loc: None, + } } diff --git a/cedar-policy-generators/src/schema.rs b/cedar-policy-generators/src/schema.rs index f535da195..b3445cc58 100644 --- a/cedar-policy-generators/src/schema.rs +++ b/cedar-policy-generators/src/schema.rs @@ -84,8 +84,8 @@ fn arbitrary_attrspec>( let attr_names: Vec = u .arbitrary() .map_err(|e| while_doing("generating attribute names for an attrspec".into(), e))?; - Ok(json_schema::AttributesOrContext(json_schema::Type::Type( - json_schema::TypeVariant::Record(json_schema::RecordType { + Ok(json_schema::AttributesOrContext(json_schema::Type::Type { + ty: json_schema::TypeVariant::Record(json_schema::RecordType { attributes: attr_names .into_iter() .map(|attr| { @@ -115,7 +115,8 @@ fn arbitrary_attrspec>( false }, }), - ))) + loc: None, + })) } /// size hint for arbitrary_attrspec fn arbitrary_attrspec_size_hint( @@ -181,82 +182,88 @@ pub fn arbitrary_schematype_with_bounded_depth>( max_depth: usize, u: &mut Unstructured<'_>, ) -> Result> { - Ok(json_schema::Type::Type(uniform!( - u, - json_schema::TypeVariant::String, - json_schema::TypeVariant::Long, - json_schema::TypeVariant::Boolean, - { - if max_depth == 0 { - // can't recurse; we arbitrarily choose Set in this case - json_schema::TypeVariant::Set { - element: Box::new(json_schema::Type::Type(json_schema::TypeVariant::Long)), + Ok(json_schema::Type::Type { + ty: uniform!( + u, + json_schema::TypeVariant::String, + json_schema::TypeVariant::Long, + json_schema::TypeVariant::Boolean, + { + if max_depth == 0 { + // can't recurse; we arbitrarily choose Set in this case + json_schema::TypeVariant::Set { + element: Box::new(json_schema::Type::Type { + ty: json_schema::TypeVariant::Long, + loc: None, + }), + } + } else { + json_schema::TypeVariant::Set { + element: Box::new(arbitrary_schematype_with_bounded_depth( + settings, + entity_types, + max_depth - 1, + u, + )?), + } } - } else { - json_schema::TypeVariant::Set { - element: Box::new(arbitrary_schematype_with_bounded_depth( - settings, - entity_types, - max_depth - 1, - u, - )?), + }, + { + if max_depth == 0 { + // can't recurse; use empty-record + json_schema::TypeVariant::Record(json_schema::RecordType { + attributes: BTreeMap::new(), + additional_attributes: if settings.enable_additional_attributes { + u.arbitrary()? + } else { + false + }, + }) + } else { + json_schema::TypeVariant::Record(json_schema::RecordType { + attributes: { + let attr_names: HashSet = u + .arbitrary() + .map_err(|e| while_doing("generating attribute names".into(), e))?; + attr_names + .into_iter() + .map(|attr_name| { + Ok(( + attr_name.into(), + arbitrary_typeofattribute_with_bounded_depth( + settings, + entity_types, + max_depth - 1, + u, + )?, + )) + }) + .collect::>>()? + }, + additional_attributes: if settings.enable_additional_attributes { + u.arbitrary()? + } else { + false + }, + }) } + }, + entity_type_name_to_schema_type_variant::(u.choose(entity_types)?), + json_schema::TypeVariant::Extension { + name: "ipaddr".parse().unwrap(), + }, + json_schema::TypeVariant::Extension { + name: "decimal".parse().unwrap(), + }, + json_schema::TypeVariant::Extension { + name: "datetime".parse().unwrap(), + }, + json_schema::TypeVariant::Extension { + name: "duration".parse().unwrap(), } - }, - { - if max_depth == 0 { - // can't recurse; use empty-record - json_schema::TypeVariant::Record(json_schema::RecordType { - attributes: BTreeMap::new(), - additional_attributes: if settings.enable_additional_attributes { - u.arbitrary()? - } else { - false - }, - }) - } else { - json_schema::TypeVariant::Record(json_schema::RecordType { - attributes: { - let attr_names: HashSet = u - .arbitrary() - .map_err(|e| while_doing("generating attribute names".into(), e))?; - attr_names - .into_iter() - .map(|attr_name| { - Ok(( - attr_name.into(), - arbitrary_typeofattribute_with_bounded_depth( - settings, - entity_types, - max_depth - 1, - u, - )?, - )) - }) - .collect::>>()? - }, - additional_attributes: if settings.enable_additional_attributes { - u.arbitrary()? - } else { - false - }, - }) - } - }, - entity_type_name_to_schema_type_variant::(u.choose(entity_types)?), - json_schema::TypeVariant::Extension { - name: "ipaddr".parse().unwrap(), - }, - json_schema::TypeVariant::Extension { - name: "decimal".parse().unwrap(), - }, - json_schema::TypeVariant::Extension { - name: "datetime".parse().unwrap(), - }, - json_schema::TypeVariant::Extension { - name: "duration".parse().unwrap(), - } - ))) + ), + loc: None, + }) } /// Convert an [`ast::EntityType`] into the corresponding @@ -274,7 +281,10 @@ pub fn entity_type_name_to_schema_type_variant>( pub fn entity_type_name_to_schema_type>( name: &ast::EntityType, ) -> json_schema::Type { - json_schema::Type::Type(entity_type_name_to_schema_type_variant(name)) + json_schema::Type::Type { + ty: entity_type_name_to_schema_type_variant(name), + loc: name.loc().cloned(), + } } /// size hint for arbitrary_schematype_with_bounded_depth @@ -315,12 +325,12 @@ fn schematype_to_type( schematy: &json_schema::Type, ) -> Type { match schematy { - json_schema::Type::CommonTypeRef { type_name } => schematype_to_type( + json_schema::Type::CommonTypeRef { type_name, .. } => schematype_to_type( schema, lookup_common_type(schema, type_name) .unwrap_or_else(|| panic!("reference to undefined common type: {type_name}")), ), - json_schema::Type::Type(ty) => match ty { + json_schema::Type::Type { ty, .. } => match ty { json_schema::TypeVariant::Boolean => Type::bool(), json_schema::TypeVariant::Long => Type::long(), json_schema::TypeVariant::String => Type::string(), @@ -367,13 +377,13 @@ pub(crate) fn attrs_from_attrs_or_context<'a>( attrsorctx: &'a json_schema::AttributesOrContext, ) -> Attributes<'a> { match &attrsorctx.0 { - json_schema::Type::CommonTypeRef { type_name } => match lookup_common_type(schema, type_name).unwrap_or_else(|| panic!("reference to undefined common type: {type_name}")) { + json_schema::Type::CommonTypeRef { type_name, .. } => match lookup_common_type(schema, type_name).unwrap_or_else(|| panic!("reference to undefined common type: {type_name}")) { json_schema::Type::CommonTypeRef { .. } => panic!("common type `{type_name}` refers to another common type, which is not allowed as of this writing?"), - json_schema::Type::Type(json_schema::TypeVariant::Record(json_schema::RecordType { attributes, additional_attributes })) => Attributes { attrs: attributes, additional_attrs: *additional_attributes }, - json_schema::Type::Type(ty) => panic!("expected attributes or context to be a record, got {ty:?}"), + json_schema::Type::Type { ty: json_schema::TypeVariant::Record(json_schema::RecordType { attributes, additional_attributes }), .. } => Attributes { attrs: attributes, additional_attrs: *additional_attributes }, + ty => panic!("expected attributes or context to be a record, got {ty:?}"), } - json_schema::Type::Type(json_schema::TypeVariant::Record(json_schema::RecordType { attributes, additional_attributes })) => Attributes { attrs: attributes, additional_attrs: *additional_attributes }, - json_schema::Type::Type(ty) => panic!("expected attributes or context to be a record, got {ty:?}"), + json_schema::Type::Type { ty: json_schema::TypeVariant::Record(json_schema::RecordType { attributes, additional_attributes }), .. } => Attributes { attrs: attributes, additional_attrs: *additional_attributes }, + ty => panic!("expected attributes or context to be a record, got {ty:?}"), } } @@ -384,7 +394,7 @@ fn attrs_in_schematype( schematype: &json_schema::Type, ) -> Box)>> { match schematype { - json_schema::Type::Type(variant) => match variant { + json_schema::Type::Type { ty, .. } => match ty { json_schema::TypeVariant::Boolean => Box::new(std::iter::empty()), json_schema::TypeVariant::Long => Box::new(std::iter::empty()), json_schema::TypeVariant::String => Box::new(std::iter::empty()), @@ -396,9 +406,12 @@ fn attrs_in_schematype( // it's an entity type, so treat it like we treat entity types attrs_in_schematype( schema, - &json_schema::Type::Type(json_schema::TypeVariant::Entity { - name: type_name.clone(), - }), + &json_schema::Type::Type { + ty: json_schema::TypeVariant::Entity { + name: type_name.clone(), + }, + loc: type_name.loc().cloned(), + }, ) } } @@ -417,7 +430,7 @@ fn attrs_in_schematype( Box::new(toplevel.into_iter().chain(recursed)) } }, - json_schema::Type::CommonTypeRef { type_name } => attrs_in_schematype( + json_schema::Type::CommonTypeRef { type_name, .. } => attrs_in_schematype( schema, lookup_common_type(schema, type_name) .unwrap_or_else(|| panic!("reference to undefined common type: {type_name}")), @@ -462,12 +475,12 @@ fn build_attributes_by_type<'a>( // Common type bindings #[derive(Debug)] struct Bindings { - // Bindings from `json_schema::Type` to a list of `UnreservedId`. - // The `ids` field ensures that `UnreservedId`s are unique. + // Bindings from `json_schema::Type` to a list of `CommonTypeId`. + // The `ids` field ensures that `CommonTypeId`s are unique. // Note that the `json_schema::Type`s in the `bindings` map should not // contain any common type references. bindings: BTreeMap, Vec>, - // The set of `UnreservedId`s used in the bindings + // The set of `CommonTypeId`s used in the bindings ids: HashSet, } impl Bindings { @@ -521,25 +534,32 @@ impl Bindings { json_schema::Type::CommonTypeRef { .. } => { unreachable!("common type references shouldn't be here") } - json_schema::Type::Type(json_schema::TypeVariant::Set { element }) => { - Ok(json_schema::Type::Type(json_schema::TypeVariant::Set { + json_schema::Type::Type { + ty: json_schema::TypeVariant::Set { element }, + loc, + } => Ok(json_schema::Type::Type { + ty: json_schema::TypeVariant::Set { element: Box::new(if let Some(ids) = self.bindings.get(element) { json_schema::Type::CommonTypeRef { type_name: ast::Name::unqualified_name(u.choose(ids)?.clone().into()) .into(), + loc: element.loc().cloned(), } } else { self.rewrite_type(u, element)? }), - })) - } - json_schema::Type::Type(json_schema::TypeVariant::Record( - json_schema::RecordType { - attributes, - additional_attributes, }, - )) => Ok(json_schema::Type::Type(json_schema::TypeVariant::Record( - json_schema::RecordType { + loc: loc.clone(), + }), + json_schema::Type::Type { + ty: + json_schema::TypeVariant::Record(json_schema::RecordType { + attributes, + additional_attributes, + }), + loc, + } => Ok(json_schema::Type::Type { + ty: json_schema::TypeVariant::Record(json_schema::RecordType { attributes: BTreeMap::from_iter( attributes .iter() @@ -556,8 +576,9 @@ impl Bindings { .collect::>>()?, ), additional_attributes: additional_attributes.to_owned(), - }, - ))), + }), + loc: loc.clone(), + }), _ => Ok(ty.clone()), } } @@ -587,6 +608,7 @@ impl Bindings { let new_ty = if let Some(ids) = self.bindings.get(ty) { json_schema::Type::CommonTypeRef { type_name: ast::Name::unqualified_name(u.choose(ids)?.clone().into()).into(), + loc: None, } } else { self.rewrite_type(u, ty)? @@ -619,6 +641,7 @@ impl Bindings { json_schema::Type::CommonTypeRef { type_name: ast::Name::unqualified_name(ids[i + 1].clone().into()) .into(), + loc: None, }, ); common_types.insert(ids[ids.len() - 1].clone(), self.rewrite_type(u, ty)?); @@ -640,13 +663,20 @@ fn bind_type( bindings.add_binding((ty.clone(), u.arbitrary()?)); } match ty { - json_schema::Type::Type(json_schema::TypeVariant::Set { element }) => { + json_schema::Type::Type { + ty: json_schema::TypeVariant::Set { element }, + .. + } => { bind_type(element, u, bindings)?; } - json_schema::Type::Type(json_schema::TypeVariant::Record(json_schema::RecordType { - attributes, - additional_attributes: _, - })) => { + json_schema::Type::Type { + ty: + json_schema::TypeVariant::Record(json_schema::RecordType { + attributes, + additional_attributes: _, + }), + .. + } => { attributes .iter() .map(|(_, attr_ty)| bind_type(&attr_ty.ty, u, bindings)) @@ -1209,7 +1239,7 @@ impl Schema { name: "duration".parse().unwrap(), }), } - .map(json_schema::Type::Type)) + .map(|ty| json_schema::Type::Type { ty, loc: None })) } /// get an attribute name and its `json_schema::Type`, from the schema @@ -1672,12 +1702,14 @@ fn downgrade_schematype_to_raw( schematype: json_schema::Type, ) -> json_schema::Type { match schematype { - json_schema::Type::CommonTypeRef { type_name } => json_schema::Type::CommonTypeRef { + json_schema::Type::CommonTypeRef { type_name, loc } => json_schema::Type::CommonTypeRef { type_name: RawName::from_name(type_name), + loc, + }, + json_schema::Type::Type { ty, loc } => json_schema::Type::Type { + ty: downgrade_schematypevariant_to_raw(ty), + loc, }, - json_schema::Type::Type(stv) => { - json_schema::Type::Type(downgrade_schematypevariant_to_raw(stv)) - } } }