From 0ff3201c3cceaa01c17b2a84f4d7dc95e7b65207 Mon Sep 17 00:00:00 2001 From: Piotr Siuszko Date: Sat, 5 Jul 2025 13:15:06 +0200 Subject: [PATCH] Missing definitions handling --- crates/bevy_remote/src/schemas/json_schema.rs | 56 +++++++----------- .../bevy_remote/src/schemas/reflect_info.rs | 58 +++++++++++++++++-- 2 files changed, 75 insertions(+), 39 deletions(-) diff --git a/crates/bevy_remote/src/schemas/json_schema.rs b/crates/bevy_remote/src/schemas/json_schema.rs index 1a2fe34cc7..282d6dbe1a 100644 --- a/crates/bevy_remote/src/schemas/json_schema.rs +++ b/crates/bevy_remote/src/schemas/json_schema.rs @@ -3,8 +3,7 @@ use alloc::borrow::Cow; use bevy_platform::collections::HashMap; use bevy_reflect::{ - prelude::ReflectDefault, serde::ReflectSerializer, GetTypeRegistration, Reflect, - TypeRegistration, TypeRegistry, + prelude::ReflectDefault, serde::ReflectSerializer, GetTypeRegistration, Reflect, TypeRegistry, }; use core::any::TypeId; use serde::{Deserialize, Serialize}; @@ -44,7 +43,26 @@ impl TypeRegistrySchemaReader for TypeRegistry { extra_info: &SchemaTypesMetadata, ) -> Option { let type_reg = self.get(type_id)?; - let mut schema: JsonSchemaBevyType = (type_reg, extra_info).try_into().ok()?; + + let mut definition = TypeInformation::from(type_reg) + .to_schema_type_info_with_metadata(extra_info) + .to_definition(); + for missing in &definition.missing_definitions { + let reg_option = self.get(*missing); + if let Some(reg) = reg_option { + let missing_schema = + TypeInformation::from(reg).to_schema_type_info_with_metadata(extra_info); + let mis_def = missing_schema.to_definition(); + definition.definitions.extend(mis_def.definitions); + if let Some(missing_id) = mis_def.id { + if !definition.definitions.contains_key(&missing_id) { + definition.definitions.insert(missing_id, missing_schema); + } + } + } + } + let mut schema: JsonSchemaBevyType = definition.into(); + schema.reflect_type_data = extra_info.get_registered_reflect_types(type_reg); schema.schema = Some(SchemaMarker.into()); schema.default_value = self.try_get_default_value_for_type_id(type_id); @@ -69,38 +87,6 @@ impl TypeRegistrySchemaReader for TypeRegistry { } } -/// Error type for invalid JSON Schema conversions. -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum InvalidJsonSchema { - /// The type cannot be converted to a valid JSON Schema. - InvalidType, -} - -impl TryFrom<(&TypeRegistration, &SchemaTypesMetadata)> for JsonSchemaBevyType { - type Error = InvalidJsonSchema; - - fn try_from(value: (&TypeRegistration, &SchemaTypesMetadata)) -> Result { - let (reg, metadata) = value; - // if let Some(s) = reg.data::() { - // return Ok(s.0.clone()); - // } - let mut schema: JsonSchemaBevyType = TypeInformation::from(reg) - .to_schema_type_info_with_metadata(metadata) - .to_definition() - .into(); - schema.reflect_type_data = metadata.get_registered_reflect_types(reg); - Ok(schema) - } -} - -impl TryFrom<&TypeRegistration> for JsonSchemaBevyType { - type Error = InvalidJsonSchema; - - fn try_from(value: &TypeRegistration) -> Result { - (value, &SchemaTypesMetadata::default()).try_into() - } -} - /// Identifies the JSON Schema version used in the schema. #[derive(Deserialize, Serialize, Debug, Reflect, PartialEq, Clone)] pub struct SchemaMarker; diff --git a/crates/bevy_remote/src/schemas/reflect_info.rs b/crates/bevy_remote/src/schemas/reflect_info.rs index e053347479..c287320133 100644 --- a/crates/bevy_remote/src/schemas/reflect_info.rs +++ b/crates/bevy_remote/src/schemas/reflect_info.rs @@ -1071,6 +1071,9 @@ pub struct SchemaDefinition { pub schema: JsonSchemaBevyType, /// The properties of the schema. pub definitions: HashMap, + /// Missing definitions of the schema. + /// Could be the case for the types that are stored as generic arguments. + pub missing_definitions: Vec, } impl From for JsonSchemaBevyType { @@ -1237,11 +1240,13 @@ impl SchemaTypeInfo { pub fn to_definition(&self) -> SchemaDefinition { let mut id: Option = self.ty_info.try_get_type_reference_id(); let mut definitions: HashMap = HashMap::new(); + let mut missing_definitions: Vec = Vec::new(); if let Some(custom_schema) = &self.ty_info.try_get_custom_schema() { return SchemaDefinition { id, schema: custom_schema.0.clone(), definitions, + missing_definitions, }; } let range = self.get_range(); @@ -1298,7 +1303,9 @@ impl SchemaTypeInfo { id, schema: _, definitions: field_definitions, + missing_definitions: key_missing_definitions, } = key.to_definition(); + missing_definitions.extend(key_missing_definitions); if let Some(id) = id { definitions.insert(id, key.clone()); definitions.extend(field_definitions); @@ -1310,7 +1317,10 @@ impl SchemaTypeInfo { id, schema: _, definitions: field_definitions, + missing_definitions: value_missing_definitions, } = value.to_definition(); + + missing_definitions.extend(value_missing_definitions); if let Some(id) = id { definitions.insert(id, value.clone()); definitions.extend(field_definitions); @@ -1405,6 +1415,7 @@ impl SchemaTypeInfo { ..Default::default() }, definitions: HashMap::new(), + missing_definitions, }; } } @@ -1430,7 +1441,9 @@ impl SchemaTypeInfo { id, schema: _, definitions: field_definitions, + missing_definitions: field_missing_definitions, } = field_schema.to_definition(); + missing_definitions.extend(field_missing_definitions); definitions.extend(field_definitions); let Some(id) = id else { continue }; if !definitions.contains_key(&id) { @@ -1450,7 +1463,9 @@ impl SchemaTypeInfo { id, schema: new_schema_type, definitions: field_definitions, + missing_definitions: field_missing_definitions, } = field_schema.to_definition(); + missing_definitions.extend(field_missing_definitions); definitions.extend(field_definitions); if let Some(id) = id { definitions.insert(id.clone(), field_schema); @@ -1519,8 +1534,10 @@ impl SchemaTypeInfo { id, schema: _, definitions: field_definitions, + missing_definitions: field_missing_definitions, } = field_schema.to_definition(); definitions.extend(field_definitions); + missing_definitions.extend(field_missing_definitions); let Some(id) = id else { continue }; if !definitions.contains_key(&id) { definitions.insert(id, field_schema); @@ -1546,7 +1563,9 @@ impl SchemaTypeInfo { id, schema: _, definitions: field_definitions, + missing_definitions: field_missing_definitions, } = items_schema.to_definition(); + missing_definitions.extend(field_missing_definitions); definitions.extend(field_definitions); if let Some(id) = id { definitions.insert(id, items_schema); @@ -1561,7 +1580,8 @@ impl SchemaTypeInfo { ty_info: TypeInformation::Type(Box::new(*generic.ty())), ..(**schema_type_info).clone() }; - let definition = schema_optional.to_definition(); + missing_definitions.push(generic.type_id()); + let definition = schema_optional.clone().to_definition(); definitions.extend(definition.definitions); schema.ref_type = None; schema.schema_type = None; @@ -1570,7 +1590,7 @@ impl SchemaTypeInfo { schema_type: Some(SchemaTypeVariant::Single(SchemaType::Null)), ..Default::default() }), - Box::new(definition.schema), + Box::new(schema_optional.to_ref_schema()), ]; } } @@ -1578,6 +1598,7 @@ impl SchemaTypeInfo { id, schema, definitions, + missing_definitions, } } } @@ -1813,10 +1834,12 @@ where #[cfg(test)] pub(super) mod tests { - use bevy_ecs::{component::Component, name::Name}; + use bevy_ecs::{component::Component, name::Name, reflect::AppTypeRegistry}; use bevy_platform::collections::HashMap; use bevy_reflect::GetTypeRegistration; + use crate::schemas::json_schema::TypeRegistrySchemaReader; + use super::*; /// Validate a JSON schema against a set of valid and invalid instances. @@ -1962,7 +1985,7 @@ pub(super) mod tests { serde_json::json!({"a": 5555,"b": 5555}), ], ); - let atr = bevy_ecs::reflect::AppTypeRegistry::default(); + let atr = AppTypeRegistry::default(); { let mut register = atr.write(); register.register::(); @@ -2078,6 +2101,33 @@ pub(super) mod tests { ); } + #[test] + fn optional_tests() { + #[derive(Reflect, Default, Deserialize, Serialize)] + pub struct ArrayComponent { + pub array: [u8; 3], + } + let atr = AppTypeRegistry::default(); + { + let mut register = atr.write(); + register.register::(); + register.register::>(); + } + let type_registry = atr.read(); + let schema = type_registry + .export_type_json_schema::>(&Default::default()) + .expect("Failed to export type JSON schema"); + validate::>( + schema, + &[None, Some(ArrayComponent { array: [5, 1, 9] })], + &[ + serde_json::json!({"array": [1, 2, 3]}), + serde_json::Value::Null, + ], + &[serde_json::json!({"array": [1999, 2, 3]})], + ); + } + #[test] fn reflect_struct_with_array() { #[derive(Reflect, Default, Deserialize, Serialize)]