diff --git a/crates/bevy_remote/src/builtin_methods.rs b/crates/bevy_remote/src/builtin_methods.rs index 1c72714802..d9270499fc 100644 --- a/crates/bevy_remote/src/builtin_methods.rs +++ b/crates/bevy_remote/src/builtin_methods.rs @@ -25,7 +25,7 @@ use serde_json::{Map, Value}; use crate::{ error_codes, schemas::{ - json_schema::{export_type, JsonSchemaBevyType}, + json_schema::{JsonSchemaBevyType, TypeRegistrySchemaReader}, open_rpc::OpenRpcDocument, }, BrpError, BrpResult, @@ -1245,7 +1245,8 @@ pub fn export_registry_types(In(params): In>, world: &World) -> Br return None; } } - let (id, schema) = export_type(type_reg, extra_info); + let id = type_reg.type_id(); + let schema = types.export_type_json_schema_for_id(id, extra_info)?; if !filter.type_limit.with.is_empty() && !filter @@ -1265,7 +1266,7 @@ pub fn export_registry_types(In(params): In>, world: &World) -> Br { return None; } - Some((id.to_string(), schema)) + Some((type_reg.type_info().type_path().into(), schema)) }) .collect::>(); diff --git a/crates/bevy_remote/src/schemas/json_schema.rs b/crates/bevy_remote/src/schemas/json_schema.rs index ffb5d1a10a..f0d7bf0261 100644 --- a/crates/bevy_remote/src/schemas/json_schema.rs +++ b/crates/bevy_remote/src/schemas/json_schema.rs @@ -1,8 +1,10 @@ //! Module with JSON Schema type for Bevy Registry Types. //! It tries to follow this standard: -use alloc::borrow::Cow; use bevy_platform::collections::HashMap; -use bevy_reflect::{GetTypeRegistration, Reflect, TypeRegistration, TypeRegistry}; +use bevy_reflect::{ + prelude::ReflectDefault, serde::ReflectSerializer, GetTypeRegistration, Reflect, + TypeRegistration, TypeRegistry, +}; use core::any::TypeId; use serde::{Deserialize, Serialize}; use serde_json::Value; @@ -19,33 +21,48 @@ pub trait TypeRegistrySchemaReader { &self, extra_info: &SchemaTypesMetadata, ) -> Option { - self.export_type_json_schema_for_id(extra_info, TypeId::of::()) + self.export_type_json_schema_for_id(TypeId::of::(), extra_info) } /// Export type JSON Schema. fn export_type_json_schema_for_id( &self, - extra_info: &SchemaTypesMetadata, type_id: TypeId, + extra_info: &SchemaTypesMetadata, ) -> Option; + + /// Try to get default value for type id. + fn try_get_default_value_for_type_id(&self, type_id: TypeId) -> Option; } impl TypeRegistrySchemaReader for TypeRegistry { fn export_type_json_schema_for_id( &self, - extra_info: &SchemaTypesMetadata, type_id: TypeId, + extra_info: &SchemaTypesMetadata, ) -> Option { let type_reg = self.get(type_id)?; - Some((type_reg, extra_info).into()) - } -} + let mut schema: JsonSchemaBevyType = (type_reg, extra_info).into(); + schema.default_value = self.try_get_default_value_for_type_id(type_id); -/// Exports schema info for a given type -pub fn export_type( - reg: &TypeRegistration, - metadata: &SchemaTypesMetadata, -) -> (Cow<'static, str>, JsonSchemaBevyType) { - (reg.type_info().type_path().into(), (reg, metadata).into()) + Some(schema) + } + + fn try_get_default_value_for_type_id(&self, type_id: TypeId) -> Option { + let type_reg = self.get(type_id)?; + let default_data = type_reg.data::()?; + let default = default_data.default(); + let serializer = ReflectSerializer::new(&*default, self); + let value_object = serde_json::to_value(serializer) + .ok() + .and_then(|v| v.as_object().cloned())?; + if value_object.len() == 1 { + if let Some((_, value)) = value_object.into_iter().next() { + return Some(value); + } + } + + None + } } impl From<(&TypeRegistration, &SchemaTypesMetadata)> for JsonSchemaBevyType { @@ -58,7 +75,6 @@ impl From<(&TypeRegistration, &SchemaTypesMetadata)> for JsonSchemaBevyType { return JsonSchemaBevyType::default(); }; typed_schema.reflect_types = metadata.get_registered_reflect_types(reg); - *typed_schema } } @@ -175,6 +191,9 @@ pub struct JsonSchemaBevyType { /// Type description #[serde(skip_serializing_if = "Option::is_none", default)] pub description: Option, + /// Default value for the schema. + #[serde(skip_serializing_if = "Option::is_none", default, rename = "default")] + pub default_value: Option, } /// Represents different types of JSON Schema values that can be used in schema definitions. @@ -361,6 +380,21 @@ mod tests { use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize}; use serde_json::json; + fn export_type() -> JsonSchemaBevyType { + let atr = AppTypeRegistry::default(); + { + let mut register = atr.write(); + register.register::(); + } + let type_registry = atr.read(); + let Some(schema) = + type_registry.export_type_json_schema::(&SchemaTypesMetadata::default()) + else { + panic!("Failed to export JSON schema for Foo"); + }; + schema + } + #[test] fn reflect_export_struct() { #[derive(Reflect, Resource, Default, Deserialize, Serialize)] @@ -370,26 +404,7 @@ mod tests { #[reflect(@10..=15i16)] b: Option, } - - let atr = AppTypeRegistry::default(); - { - let mut register = atr.write(); - register.register::(); - } - let type_registry = atr.read(); - let foo_registration = type_registry - .get(TypeId::of::()) - .expect("SHOULD BE REGISTERED") - .clone(); - // for field in foo_registration - // .type_info() - // .as_struct() - // .expect("msg") - // .iter() - // { - // eprintln!("{}: {:#?}", field.name(), field.build_schema_type_info()); - // } - let (_, schema) = export_type(&foo_registration, &SchemaTypesMetadata::default()); + let schema = export_type::(); eprintln!("{}", serde_json::to_string_pretty(&schema).expect("msg")); assert!( @@ -421,18 +436,7 @@ mod tests { #[default] NoValue, } - - let atr = AppTypeRegistry::default(); - { - let mut register = atr.write(); - register.register::(); - } - let type_registry = atr.read(); - let foo_registration = type_registry - .get(TypeId::of::()) - .expect("SHOULD BE REGISTERED") - .clone(); - let (_, schema) = export_type(&foo_registration, &SchemaTypesMetadata::default()); + let schema = export_type::(); eprintln!("{}", serde_json::to_string_pretty(&schema).expect("msg")); assert!( schema.reflect_types.contains(&"Component".to_owned()), @@ -457,18 +461,7 @@ mod tests { #[default] NoValue, } - - let atr = AppTypeRegistry::default(); - { - let mut register = atr.write(); - register.register::(); - } - let type_registry = atr.read(); - let foo_registration = type_registry - .get(TypeId::of::()) - .expect("SHOULD BE REGISTERED") - .clone(); - let (_, schema) = export_type(&foo_registration, &SchemaTypesMetadata::default()); + let schema = export_type::(); assert!( !schema.reflect_types.contains(&"Component".to_owned()), "Should not be a component" @@ -512,11 +505,9 @@ mod tests { let mut metadata = SchemaTypesMetadata::default(); metadata.map_type_data::("CustomData"); let type_registry = atr.read(); - let foo_registration = type_registry - .get(TypeId::of::()) - .expect("SHOULD BE REGISTERED") - .clone(); - let (_, schema) = export_type(&foo_registration, &metadata); + let schema = type_registry + .export_type_json_schema::(&metadata) + .expect("Failed to export"); assert!( !metadata.has_type_data::(&schema.reflect_types), "Should not be a component" @@ -543,17 +534,7 @@ mod tests { #[reflect(Component, Default, Serialize, Deserialize)] struct TupleStructType(usize, i32); - let atr = AppTypeRegistry::default(); - { - let mut register = atr.write(); - register.register::(); - } - let type_registry = atr.read(); - let foo_registration = type_registry - .get(TypeId::of::()) - .expect("SHOULD BE REGISTERED") - .clone(); - let (_, schema) = export_type(&foo_registration, &SchemaTypesMetadata::default()); + let schema = export_type::(); assert!( schema.reflect_types.contains(&"Component".to_owned()), "Should be a component" @@ -575,17 +556,7 @@ mod tests { a: u16, } - let atr = AppTypeRegistry::default(); - { - let mut register = atr.write(); - register.register::(); - } - let type_registry = atr.read(); - let foo_registration = type_registry - .get(TypeId::of::()) - .expect("SHOULD BE REGISTERED") - .clone(); - let (_, schema) = export_type(&foo_registration, &SchemaTypesMetadata::default()); + let schema = export_type::(); let schema_as_value = serde_json::to_value(&schema).expect("Should serialize"); eprintln!("{:#?}", &schema_as_value); let value = json!({ @@ -597,6 +568,9 @@ mod tests { "Resource", "Default", ], + "default": { + "a": 0 + }, "kind": "Struct", "type": "object", "additionalProperties": false,