Make one_of field in top schema optional, disabled by default.

This commit is contained in:
Piotr Siuszko 2025-07-09 16:59:48 +02:00
parent ebe6e04407
commit 5ad2351c2a
3 changed files with 61 additions and 48 deletions

View File

@ -359,9 +359,16 @@ pub struct BrpJsonSchemaQueryFilter {
#[serde(skip_serializing_if = "Vec::is_empty", default)] #[serde(skip_serializing_if = "Vec::is_empty", default)]
pub with_crates: Vec<String>, pub with_crates: Vec<String>,
/// Constrain resource by type /// Constrain resource by data type.
/// Mapping of data types to their corresponding JSON schema types
/// is provided by the [`crate::schemas::SchemaTypesMetadata`] resource.
#[serde(default)] #[serde(default)]
pub type_limit: JsonSchemaTypeLimit, pub type_limit: JsonSchemaTypeLimit,
/// Add `one_of` field to the root of the schema.
/// It will allow to use the schema for validation values if they match the format used by the Bevy reflect serialization.
#[serde(default)]
pub meta_schemas_export: bool,
} }
impl BrpJsonSchemaQueryFilter { impl BrpJsonSchemaQueryFilter {
@ -402,12 +409,12 @@ impl JsonSchemaTypeLimit {
} }
/// Check if the type limit should skip a type. /// Check if the type limit should skip a type.
pub fn should_skip_type(&self, registered_types: &[Cow<'_, str>]) -> bool { pub fn should_skip_type(&self, registered_types: &[&Cow<'_, str>]) -> bool {
if !self.with.is_empty() if !self.with.is_empty()
&& !self && !self
.with .with
.iter() .iter()
.any(|c| registered_types.iter().any(|cc| c.eq(cc))) .any(|c| registered_types.iter().any(|cc| c.eq(*cc)))
{ {
return true; return true;
} }
@ -415,7 +422,7 @@ impl JsonSchemaTypeLimit {
&& self && self
.without .without
.iter() .iter()
.any(|c| registered_types.iter().any(|cc| c.eq(cc))) .any(|c| registered_types.iter().any(|cc| c.eq(*cc)))
{ {
return true; return true;
} }
@ -1411,15 +1418,15 @@ fn export_registry_types_typed(
if filter.should_skip_for_crate(type_reg) { if filter.should_skip_for_crate(type_reg) {
return None; return None;
} }
if !filter.type_limit.is_empty() { let registered_types = metadata.get_registered_reflect_types(type_reg);
let registered_types = metadata.get_registered_reflect_types(type_reg);
if filter if filter
.type_limit .type_limit
.should_skip_type(registered_types.as_slice()) .should_skip_type(registered_types.as_slice())
{ {
return None; return None;
}
} }
let type_id = type_reg.type_id(); let type_id = type_reg.type_id();
let mut dep_ids = types.get_type_dependencies(type_id); let mut dep_ids = types.get_type_dependencies(type_id);
dep_ids.insert(type_id); dep_ids.insert(type_id);
@ -1437,35 +1444,37 @@ fn export_registry_types_typed(
Some((schema_id, Box::new(schema))) Some((schema_id, Box::new(schema)))
}) })
.collect(); .collect();
schema.one_of = schema if filter.meta_schemas_export {
.definitions schema.one_of = schema
.iter() .definitions
.flat_map(|(id, schema)| { .iter()
if !schema.reflect_type_data.contains(&"Serialize".into()) .flat_map(|(id, schema)| {
|| !schema.reflect_type_data.contains(&"Deserialize".into()) if !schema.reflect_type_data.contains(&"Serialize".into())
{ || !schema.reflect_type_data.contains(&"Deserialize".into())
return None; {
} return None;
let schema = JsonSchemaBevyType { }
properties: [( let schema = JsonSchemaBevyType {
schema.type_path.clone(), properties: [(
JsonSchemaBevyType { schema.type_path.clone(),
ref_type: Some(TypeReferencePath::definition(id.clone())), JsonSchemaBevyType {
description: schema.description.clone(), ref_type: Some(TypeReferencePath::definition((*id).clone())),
..Default::default() description: schema.description.clone(),
} ..Default::default()
}
.into(),
)]
.into(), .into(),
)] schema_type: Some(crate::schemas::json_schema::SchemaType::Object.into()),
.into(), additional_properties: Some(JsonSchemaVariant::BoolValue(false)),
schema_type: Some(crate::schemas::json_schema::SchemaType::Object.into()), description: schema.description.clone(),
additional_properties: Some(JsonSchemaVariant::BoolValue(false)), reflect_type_data: schema.reflect_type_data.clone(),
description: schema.description.clone(), ..Default::default()
reflect_type_data: schema.reflect_type_data.clone(), };
..Default::default() Some(schema.into())
}; })
Some(schema.into()) .collect();
}) }
.collect();
Ok(schema) Ok(schema)
} }
@ -1828,7 +1837,7 @@ mod tests {
#[cfg(feature = "bevy_math")] #[cfg(feature = "bevy_math")]
fn export_schema_test() { fn export_schema_test() {
#[derive(Reflect, Default, Deserialize, Serialize, Component)] #[derive(Reflect, Default, Deserialize, Serialize, Component)]
#[reflect(Component)] #[reflect(Component, Serialize, Deserialize)]
pub struct OtherStruct { pub struct OtherStruct {
/// FIELD DOC /// FIELD DOC
pub field: String, pub field: String,
@ -1836,18 +1845,18 @@ mod tests {
} }
/// STRUCT DOC /// STRUCT DOC
#[derive(Reflect, Default, Deserialize, Serialize, Component)] #[derive(Reflect, Default, Deserialize, Serialize, Component)]
#[reflect(Component)] #[reflect(Component, Serialize, Deserialize)]
pub struct SecondStruct; pub struct SecondStruct;
/// STRUCT DOC /// STRUCT DOC
#[derive(Reflect, Default, Deserialize, Serialize, Component)] #[derive(Reflect, Default, Deserialize, Serialize, Component)]
#[reflect(Component)] #[reflect(Component, Serialize, Deserialize)]
pub struct ThirdStruct { pub struct ThirdStruct {
pub array_strings: Vec<String>, pub array_strings: Vec<String>,
pub array_structs: [OtherStruct; 5], pub array_structs: [OtherStruct; 5],
// pub map_strings: HashMap<String, i32>, // pub map_strings: HashMap<String, i32>,
} }
#[derive(Reflect, Default, Deserialize, Serialize, Component)] #[derive(Reflect, Default, Deserialize, Serialize, Component)]
#[reflect(Component)] #[reflect(Component, Serialize, Deserialize)]
pub struct NestedStruct { pub struct NestedStruct {
pub other: OtherStruct, pub other: OtherStruct,
pub second: SecondStruct, pub second: SecondStruct,

View File

@ -173,10 +173,10 @@ impl SchemaTypesMetadata {
} }
/// Build reflect types list for a given type registration /// Build reflect types list for a given type registration
pub fn get_registered_reflect_types(&self, reg: &TypeRegistration) -> Vec<Cow<'static, str>> { pub fn get_registered_reflect_types(&self, reg: &TypeRegistration) -> Vec<&Cow<'static, str>> {
self.type_data_map self.type_data_map
.iter() .iter()
.filter_map(|(id, name)| reg.data_by_id(*id).and(Some(name.clone()))) .filter_map(|(id, name)| reg.data_by_id(*id).and(Some(name)))
.collect() .collect()
} }

View File

@ -1367,7 +1367,11 @@ impl TypeDefinitionBuilder for TypeRegistry {
.map(|docs| docs.trim().replace("\n", "").into()); .map(|docs| docs.trim().replace("\n", "").into());
#[cfg(not(feature = "documentation"))] #[cfg(not(feature = "documentation"))]
let description = None; let description = None;
let reflect_type_data = metadata
.get_registered_reflect_types(type_reg)
.iter()
.map(|c| Cow::Owned(c.to_string()))
.collect();
let mut schema = JsonSchemaBevyType { let mut schema = JsonSchemaBevyType {
description, description,
type_path, type_path,
@ -1380,7 +1384,7 @@ impl TypeDefinitionBuilder for TypeRegistry {
exclusive_minimum: range.min.get_exclusive(), exclusive_minimum: range.min.get_exclusive(),
exclusive_maximum: range.max.get_exclusive(), exclusive_maximum: range.max.get_exclusive(),
schema_type: (&internal).into(), schema_type: (&internal).into(),
reflect_type_data: metadata.get_registered_reflect_types(type_reg), reflect_type_data,
..default() ..default()
}; };
schema.schema_type = (&internal).into(); schema.schema_type = (&internal).into();