diff --git a/crates/bevy_remote/src/builtin_methods.rs b/crates/bevy_remote/src/builtin_methods.rs index ed95c87a9a..65ee39ffc5 100644 --- a/crates/bevy_remote/src/builtin_methods.rs +++ b/crates/bevy_remote/src/builtin_methods.rs @@ -1822,9 +1822,6 @@ mod tests { input: BrpJsonSchemaQueryFilter, world: &World, ) -> JsonSchemaBevyType { - let response = - export_registry_types_typed(input, world).expect("Failed to export registry types"); - - response + export_registry_types_typed(input, world).expect("Failed to export registry types") } } diff --git a/crates/bevy_remote/src/schemas/json_schema.rs b/crates/bevy_remote/src/schemas/json_schema.rs index 6a2acb161f..7ac7382d59 100644 --- a/crates/bevy_remote/src/schemas/json_schema.rs +++ b/crates/bevy_remote/src/schemas/json_schema.rs @@ -11,7 +11,8 @@ use serde_json::Value; use crate::schemas::{ reflect_info::{ - is_non_zero_number_type, SchemaNumber, TypeInformation, TypeReferenceId, TypeReferencePath, + is_non_zero_number_type, SchemaNumber, TypeDefinitionBuilder, TypeReferenceId, + TypeReferencePath, }, SchemaTypesMetadata, }; @@ -42,25 +43,7 @@ impl TypeRegistrySchemaReader for TypeRegistry { type_id: TypeId, extra_info: &SchemaTypesMetadata, ) -> Option { - let type_reg = self.get(type_id)?; - - let mut definition = TypeInformation::from(type_reg) - .to_schema_type_info_with_metadata(extra_info) - .to_definition(); - for dependency in &definition.dependencies { - let reg_option = self.get(*dependency); - 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 { - 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); + let mut schema = self.build_schema_for_type_id_with_definitions(type_id, extra_info)?; schema.schema = Some(SchemaMarker.into()); schema.default_value = self.try_get_default_value_for_type_id(type_id); diff --git a/crates/bevy_remote/src/schemas/reflect_info.rs b/crates/bevy_remote/src/schemas/reflect_info.rs index ac1fa1e66e..9456b4f3bc 100644 --- a/crates/bevy_remote/src/schemas/reflect_info.rs +++ b/crates/bevy_remote/src/schemas/reflect_info.rs @@ -2,7 +2,7 @@ use alloc::borrow::Cow; use alloc::sync::Arc; use bevy_derive::{Deref, DerefMut}; -use bevy_platform::collections::{HashMap, HashSet}; +use bevy_platform::collections::HashSet; use bevy_reflect::attributes::CustomAttributes; use bevy_reflect::{ EnumInfo, GenericInfo, NamedField, Reflect, Type, TypeInfo, TypePathTable, TypeRegistration, @@ -12,6 +12,10 @@ use bevy_utils::{default, TypeIdMap}; use core::any::TypeId; use core::fmt; use core::fmt::{Display, Formatter}; +use core::num::{ + NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128, + NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize, +}; use core::slice::Iter; use core::{ any::Any, @@ -20,10 +24,6 @@ use core::{ }; use serde::de::{self, Visitor}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use std::num::{ - NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128, - NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize, -}; use crate::schemas::json_schema::{ JsonSchemaBevyType, JsonSchemaVariant, SchemaKind, SchemaType, SchemaTypeVariant, @@ -56,24 +56,24 @@ impl Display for TypeReferenceId { impl TypeReferenceId { /// Returns the type path of the reference. pub fn type_path(&self) -> String { - self.replace("-", "::") + self.replace("-", "::").replace("+", ", ") + } +} +impl From<&str> for TypeReferenceId { + fn from(t: &str) -> Self { + TypeReferenceId(t.replace("::", "-").replace(", ", "+").into()) } } impl From<&Type> for TypeReferenceId { fn from(t: &Type) -> Self { - TypeReferenceId(t.path().replace("::", "-").into()) + t.path().into() } } impl From<&TypePathTable> for TypeReferenceId { fn from(t: &TypePathTable) -> Self { - TypeReferenceId(t.path().replace("::", "-").into()) - } -} -impl From<&str> for TypeReferenceId { - fn from(t: &str) -> Self { - TypeReferenceId(t.replace("::", "-").into()) + t.path().into() } } @@ -266,10 +266,10 @@ impl TypeInformation { SchemaType::try_get_primitive_type_from_type_id(self.type_id()) } /// Converts the type information into a schema type information. - pub fn to_schema_type_info(self) -> SchemaTypeInfo { - let internal_schema_type = (&self).into(); + pub fn to_schema_type_info(&self) -> SchemaTypeInfo { + let internal_schema_type = self.into(); SchemaTypeInfo { - ty_info: self, + ty_info: self.clone(), field_data: None, internal_schema_type, reflect_type_data: None, @@ -277,17 +277,17 @@ impl TypeInformation { } /// Converts the type information into a schema type information. pub fn to_schema_type_info_with_metadata( - self, + &self, metadata: &SchemaTypesMetadata, ) -> SchemaTypeInfo { - let reflect_type_data = if let Self::TypeRegistration(reg) = &self { + let reflect_type_data = if let Self::TypeRegistration(reg) = self { Some(metadata.get_registered_reflect_types(reg)) } else { None }; - let internal_schema_type = (&self).into(); + let internal_schema_type = self.into(); SchemaTypeInfo { - ty_info: self, + ty_info: self.clone(), field_data: None, internal_schema_type, reflect_type_data, @@ -750,12 +750,12 @@ pub struct MinMaxValues { impl From<&JsonSchemaBevyType> for MinMaxValues { fn from(value: &JsonSchemaBevyType) -> Self { let min = match (&value.exclusive_minimum, &value.minimum) { - (Some(ex), None) => Some(BoundValue::Exclusive(ex.clone())), + (Some(ex), None) => Some(BoundValue::Exclusive(*ex)), (_, Some(inclusive)) => Some(BoundValue::Inclusive(*inclusive)), _ => None, }; let max = match (&value.exclusive_maximum, &value.maximum) { - (Some(ex), None) => Some(BoundValue::Exclusive(ex.clone())), + (Some(ex), None) => Some(BoundValue::Exclusive(*ex)), (_, Some(inclusive)) => Some(BoundValue::Inclusive(*inclusive)), _ => None, }; @@ -1166,35 +1166,6 @@ impl From<&FieldInformation> for SchemaTypeInfo { } } -/// Contains comprehensive information about a type's schema representation. -/// This struct aggregates all the necessary information to generate a JSON schema -/// from Rust type information obtained through reflection. -#[derive(Clone, Debug, Default)] -pub(crate) struct SchemaDefinition { - /// The type reference ID of the schema. - pub id: Option, - /// The JSON schema type of the schema. - 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 dependencies: Vec, -} - -impl From for JsonSchemaBevyType { - fn from(value: SchemaDefinition) -> Self { - Self { - definitions: value - .definitions - .iter() - .map(|(id, schema)| (id.clone(), schema.to_definition().schema.into())) - .collect(), - ..value.schema - } - } -} - /// Contains comprehensive information about a type's schema representation. /// This struct aggregates all the necessary information to generate a JSON schema /// from Rust type information obtained through reflection. @@ -1342,372 +1313,372 @@ impl SchemaTypeInfo { schema } - /// Converts the schema type information into a JSON schema definition. - pub fn to_definition(&self) -> SchemaDefinition { - let mut id: Option = self.ty_info.try_get_type_reference_id(); - let mut definitions: HashMap = HashMap::new(); - if let Some(custom_schema) = &self.ty_info.try_get_custom_schema() { - return SchemaDefinition { - id, - schema: custom_schema.0.clone(), - definitions, - dependencies: Default::default(), - }; - } - let mut dependencies: HashSet = HashSet::new(); - let range = self.get_range(); + // /// Converts the schema type information into a JSON schema definition. + // pub fn to_definition_NONE(&self) -> SchemaDefinition { + // let mut id: Option = self.ty_info.try_get_type_reference_id(); + // let mut definitions: HashMap = HashMap::new(); + // if let Some(custom_schema) = &self.ty_info.try_get_custom_schema() { + // return SchemaDefinition { + // id, + // schema: custom_schema.0.clone(), + // definitions, + // dependencies: Default::default(), + // }; + // } + // let mut dependencies: HashSet = HashSet::new(); + // let range = self.get_range(); - let (type_path, short_path, crate_name, module_path) = - if let Some(type_path_table) = self.ty_info.try_get_type_path_table() { - ( - type_path_table.path().into(), - type_path_table.short_path().into(), - type_path_table.crate_name().map(Into::into), - type_path_table.module_path().map(Into::into), - ) - } else { - (Cow::default(), Cow::default(), None, None) - }; + // let (type_path, short_path, crate_name, module_path) = + // if let Some(type_path_table) = self.ty_info.try_get_type_path_table() { + // ( + // type_path_table.path().into(), + // type_path_table.short_path().into(), + // type_path_table.crate_name().map(Into::into), + // type_path_table.module_path().map(Into::into), + // ) + // } else { + // (Cow::default(), Cow::default(), None, None) + // }; - let not = if is_non_zero_number_type(self.ty_info.type_id()) { - Some(Box::new(JsonSchemaBevyType { - const_value: Some(0.into()), - ..default() - })) - } else { - None - }; + // let not = if is_non_zero_number_type(self.ty_info.type_id()) { + // Some(Box::new(JsonSchemaBevyType { + // const_value: Some(0.into()), + // ..default() + // })) + // } else { + // None + // }; - let mut schema = JsonSchemaBevyType { - description: self.ty_info.get_docs(), - not, - type_path, - short_path, - crate_name, - module_path, - kind: Some((&self.ty_info).into()), - minimum: range.min.get_inclusive(), - maximum: range.max.get_inclusive(), - exclusive_minimum: range.min.get_exclusive(), - exclusive_maximum: range.max.get_exclusive(), - schema_type: self.into(), - reflect_type_data: self.reflect_type_data.clone().unwrap_or_default(), - ..default() - }; - match self.internal_schema_type.clone() { - InternalSchemaType::Map { key, value } => { - let key = key.to_schema_type_info(); - let value = value.to_schema_type_info(); - if key.ty_info.try_get_primitive_type().is_some() { - if let Some(p) = key.ty_info.try_get_regex_for_type() { - schema.pattern_properties = [(p, value.to_ref_schema().into())].into(); - schema.additional_properties = Some(JsonSchemaVariant::BoolValue(false)); - } - } else { - { - let SchemaDefinition { - id, - schema: _, - definitions: field_definitions, - dependencies: key_dependencies, - } = key.to_definition(); - dependencies.extend(key_dependencies); - if let Some(id) = id { - definitions.insert(id, key.clone()); - definitions.extend(field_definitions); - } - } - } - if !value.ty_info.is_primitive_type() { - let SchemaDefinition { - id, - schema: _, - definitions: field_definitions, - dependencies: value_dependencies, - } = value.to_definition(); + // let mut schema = JsonSchemaBevyType { + // description: self.ty_info.get_docs(), + // not, + // type_path, + // short_path, + // crate_name, + // module_path, + // kind: Some((&self.ty_info).into()), + // minimum: range.min.get_inclusive(), + // maximum: range.max.get_inclusive(), + // exclusive_minimum: range.min.get_exclusive(), + // exclusive_maximum: range.max.get_exclusive(), + // schema_type: self.into(), + // reflect_type_data: self.reflect_type_data.clone().unwrap_or_default(), + // ..default() + // }; + // match self.internal_schema_type.clone() { + // InternalSchemaType::Map { key, value } => { + // let key = key.to_schema_type_info(); + // let value = value.to_schema_type_info(); + // if key.ty_info.try_get_primitive_type().is_some() { + // if let Some(p) = key.ty_info.try_get_regex_for_type() { + // schema.pattern_properties = [(p, value.to_ref_schema().into())].into(); + // schema.additional_properties = Some(JsonSchemaVariant::BoolValue(false)); + // } + // } else { + // { + // let SchemaDefinition { + // id, + // schema: _, + // definitions: field_definitions, + // dependencies: key_dependencies, + // } = key.to_definition(); + // dependencies.extend(key_dependencies); + // if let Some(id) = id { + // definitions.insert(id, key.clone()); + // definitions.extend(field_definitions); + // } + // } + // } + // if !value.ty_info.is_primitive_type() { + // let SchemaDefinition { + // id, + // schema: _, + // definitions: field_definitions, + // dependencies: value_dependencies, + // } = value.to_definition(); - dependencies.extend(value_dependencies); - if let Some(id) = id { - definitions.insert(id, value.clone()); - definitions.extend(field_definitions); - } - } - schema.value_type = Some(value.to_ref_schema().into()); - schema.key_type = Some(key.to_ref_schema().into()); - } - InternalSchemaType::Regular(_) - | InternalSchemaType::RegularType(_) - | InternalSchemaType::NoInfo => {} - InternalSchemaType::EnumHolder(variants) => { - let schema_fields: Vec<(Cow<'static, str>, SchemaDefinition)> = variants - .iter() - .map(|variant| { - ( - variant.name().into(), - SchemaTypeInfo::from(variant).to_definition(), - ) - }) - .collect(); - schema.one_of = schema_fields - .iter() - .map(|(_, definition)| definition.schema.clone().into()) - .collect(); - definitions.extend(schema_fields.iter().flat_map(|s| s.1.definitions.clone())); - } - InternalSchemaType::EnumVariant(variant_info) => { - schema.kind = Some(SchemaKind::Value); - schema.schema_type = Some(SchemaTypeVariant::Single(SchemaType::Object)); - let ty_info: TypeInformation = (&variant_info).into(); - let field_data: Option = Some((&variant_info).into()); - id = None; - match &variant_info { - VariantInfo::Struct(struct_variant_info) => { - let fields = get_fields_information(struct_variant_info.iter()); + // dependencies.extend(value_dependencies); + // if let Some(id) = id { + // definitions.insert(id, value.clone()); + // definitions.extend(field_definitions); + // } + // } + // schema.value_type = Some(value.to_ref_schema().into()); + // schema.key_type = Some(key.to_ref_schema().into()); + // } + // InternalSchemaType::Regular(_) + // | InternalSchemaType::RegularType(_) + // | InternalSchemaType::NoInfo => {} + // InternalSchemaType::EnumHolder(variants) => { + // let schema_fields: Vec<(Cow<'static, str>, SchemaDefinition)> = variants + // .iter() + // .map(|variant| { + // ( + // variant.name().into(), + // SchemaTypeInfo::from(variant).to_definition(), + // ) + // }) + // .collect(); + // schema.one_of = schema_fields + // .iter() + // .map(|(_, definition)| definition.schema.clone().into()) + // .collect(); + // definitions.extend(schema_fields.iter().flat_map(|s| s.1.definitions.clone())); + // } + // InternalSchemaType::EnumVariant(variant_info) => { + // schema.kind = Some(SchemaKind::Value); + // schema.schema_type = Some(SchemaTypeVariant::Single(SchemaType::Object)); + // let ty_info: TypeInformation = (&variant_info).into(); + // let field_data: Option = Some((&variant_info).into()); + // id = None; + // match &variant_info { + // VariantInfo::Struct(struct_variant_info) => { + // let fields = get_fields_information(struct_variant_info.iter()); - let schema_field = SchemaTypeInfo { - ty_info, - field_data, - internal_schema_type: InternalSchemaType::FieldsHolder( - FieldsInformation { - fields, - fields_type: FieldType::Named, - }, - ), - reflect_type_data: None, - }; - let definition = schema_field.to_definition(); + // let schema_field = SchemaTypeInfo { + // ty_info, + // field_data, + // internal_schema_type: InternalSchemaType::FieldsHolder( + // FieldsInformation { + // fields, + // fields_type: FieldType::Named, + // }, + // ), + // reflect_type_data: None, + // }; + // let definition = schema_field.to_definition(); - schema.properties = - [(variant_info.name().into(), definition.schema.into())].into(); - schema.required = vec![variant_info.name().into()]; - definitions.extend(definition.definitions); - } - VariantInfo::Tuple(tuple_variant_info) => { - let stored_fields = get_fields_information(tuple_variant_info.iter()); - let schema_field = SchemaTypeInfo { - ty_info, - field_data: None, - internal_schema_type: InternalSchemaType::FieldsHolder( - FieldsInformation { - fields: stored_fields, + // schema.properties = + // [(variant_info.name().into(), definition.schema.into())].into(); + // schema.required = vec![variant_info.name().into()]; + // definitions.extend(definition.definitions); + // } + // VariantInfo::Tuple(tuple_variant_info) => { + // let stored_fields = get_fields_information(tuple_variant_info.iter()); + // let schema_field = SchemaTypeInfo { + // ty_info, + // field_data: None, + // internal_schema_type: InternalSchemaType::FieldsHolder( + // FieldsInformation { + // fields: stored_fields, - fields_type: FieldType::Unnamed, - }, - ), - reflect_type_data: None, - }; - let definition = schema_field.to_definition(); + // fields_type: FieldType::Unnamed, + // }, + // ), + // reflect_type_data: None, + // }; + // let definition = schema_field.to_definition(); - schema.properties = - [(variant_info.name().into(), definition.schema.into())].into(); - definitions.extend(definition.definitions); - schema.required = vec![variant_info.name().into()]; - } - VariantInfo::Unit(unit_variant_info) => { - let internal_schema_type = (&ty_info).into(); - let schema_field = SchemaTypeInfo { - ty_info, - field_data, - internal_schema_type, - reflect_type_data: None, - }; - return SchemaDefinition { - id: None, - schema: JsonSchemaBevyType { - const_value: Some(unit_variant_info.name().to_string().into()), - schema_type: Some(SchemaTypeVariant::Single(SchemaType::String)), - description: schema_field.get_docs(), - kind: Some(SchemaKind::Value), - ..Default::default() - }, - definitions: HashMap::new(), - dependencies: dependencies.iter().cloned().collect(), - }; - } - } - } - InternalSchemaType::FieldsHolder(fields) => match fields.fields_type { - FieldType::Named => { - schema.additional_properties = Some(JsonSchemaVariant::BoolValue(false)); - schema.schema_type = Some(SchemaTypeVariant::Single(SchemaType::Object)); - let schema_fields: Vec<(Cow<'static, str>, SchemaTypeInfo)> = fields - .fields - .iter() - .map(|field| (field.field.to_name(), SchemaTypeInfo::from(field))) - .collect(); - schema.properties = schema_fields - .iter() - .map(|(name, schema)| (name.clone(), schema.to_ref_schema().into())) - .collect(); - for (_, field_schema) in schema_fields { - if field_schema.ty_info.is_primitive_type() { - continue; - } - dependencies.insert(field_schema.ty_info.type_id()); - // let SchemaDefinition { - // 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) { - // definitions.insert(id, field_schema); - // } - } - schema.required = fields - .fields - .iter() - .map(|field| field.field.to_name()) - .collect(); - } - FieldType::Unnamed if fields.fields.len() == 1 => { - let field_schema = SchemaTypeInfo::from(&fields.fields[0]); + // schema.properties = + // [(variant_info.name().into(), definition.schema.into())].into(); + // definitions.extend(definition.definitions); + // schema.required = vec![variant_info.name().into()]; + // } + // VariantInfo::Unit(unit_variant_info) => { + // let internal_schema_type = (&ty_info).into(); + // let schema_field = SchemaTypeInfo { + // ty_info, + // field_data, + // internal_schema_type, + // reflect_type_data: None, + // }; + // return SchemaDefinition { + // id: None, + // schema: JsonSchemaBevyType { + // const_value: Some(unit_variant_info.name().to_string().into()), + // schema_type: Some(SchemaTypeVariant::Single(SchemaType::String)), + // description: schema_field.get_docs(), + // kind: Some(SchemaKind::Value), + // ..Default::default() + // }, + // definitions: HashMap::new(), + // dependencies: dependencies.iter().cloned().collect(), + // }; + // } + // } + // } + // InternalSchemaType::FieldsHolder(fields) => match fields.fields_type { + // FieldType::Named => { + // schema.additional_properties = Some(JsonSchemaVariant::BoolValue(false)); + // schema.schema_type = Some(SchemaTypeVariant::Single(SchemaType::Object)); + // let schema_fields: Vec<(Cow<'static, str>, SchemaTypeInfo)> = fields + // .fields + // .iter() + // .map(|field| (field.field.to_name(), SchemaTypeInfo::from(field))) + // .collect(); + // schema.properties = schema_fields + // .iter() + // .map(|(name, schema)| (name.clone(), schema.to_ref_schema().into())) + // .collect(); + // for (_, field_schema) in schema_fields { + // if field_schema.ty_info.is_primitive_type() { + // continue; + // } + // dependencies.insert(field_schema.ty_info.type_id()); + // // let SchemaDefinition { + // // 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) { + // // definitions.insert(id, field_schema); + // // } + // } + // schema.required = fields + // .fields + // .iter() + // .map(|field| field.field.to_name()) + // .collect(); + // } + // FieldType::Unnamed if fields.fields.len() == 1 => { + // let field_schema = SchemaTypeInfo::from(&fields.fields[0]); - let SchemaDefinition { - id, - schema: new_schema_type, - definitions: field_definitions, - dependencies: field_dependencies, - } = field_schema.to_definition(); - dependencies.extend(field_dependencies); - definitions.extend(field_definitions); - if let Some(id) = id { - definitions.insert(id.clone(), field_schema); - schema.ref_type = Some(TypeReferencePath::definition(id)); - } else { - let (type_path, short_path, crate_name, module_path) = - if let Some(type_path_table) = self.ty_info.try_get_type_path_table() { - ( - type_path_table.path().into(), - type_path_table.short_path().into(), - type_path_table.crate_name().map(Into::into), - type_path_table.module_path().map(Into::into), - ) - } else { - (Cow::default(), Cow::default(), None, None) - }; - schema = JsonSchemaBevyType { - short_path, - type_path, - module_path, - crate_name, - kind: Some(SchemaKind::TupleStruct), - schema_type: self.into(), - description: self.get_docs(), - ..new_schema_type - }; - } - } - s => { - let schema_fields: Vec = - fields.fields.iter().map(SchemaTypeInfo::from).collect(); - schema.prefix_items = schema_fields - .iter() - .map(|field| { - let field_schema = if s == FieldType::ForceUnnamed - && field - .field_data - .as_ref() - .is_some_and(|f| f.description.is_none()) - { - if let Some(field_data) = field.field_data.as_ref() { - let description = field_data.name.clone(); - SchemaTypeInfo { - field_data: Some(SchemaFieldData { - description, - ..field_data.clone() - }), - ..field.clone() - } - .to_ref_schema() - } else { - field.to_ref_schema() - } - } else { - field.to_ref_schema() - }; + // let SchemaDefinition { + // id, + // schema: new_schema_type, + // definitions: field_definitions, + // dependencies: field_dependencies, + // } = field_schema.to_definition(); + // dependencies.extend(field_dependencies); + // definitions.extend(field_definitions); + // if let Some(id) = id { + // definitions.insert(id.clone(), field_schema); + // schema.ref_type = Some(TypeReferencePath::definition(id)); + // } else { + // let (type_path, short_path, crate_name, module_path) = + // if let Some(type_path_table) = self.ty_info.try_get_type_path_table() { + // ( + // type_path_table.path().into(), + // type_path_table.short_path().into(), + // type_path_table.crate_name().map(Into::into), + // type_path_table.module_path().map(Into::into), + // ) + // } else { + // (Cow::default(), Cow::default(), None, None) + // }; + // schema = JsonSchemaBevyType { + // short_path, + // type_path, + // module_path, + // crate_name, + // kind: Some(SchemaKind::TupleStruct), + // schema_type: self.into(), + // description: self.get_docs(), + // ..new_schema_type + // }; + // } + // } + // s => { + // let schema_fields: Vec = + // fields.fields.iter().map(SchemaTypeInfo::from).collect(); + // schema.prefix_items = schema_fields + // .iter() + // .map(|field| { + // let field_schema = if s == FieldType::ForceUnnamed + // && field + // .field_data + // .as_ref() + // .is_some_and(|f| f.description.is_none()) + // { + // if let Some(field_data) = field.field_data.as_ref() { + // let description = field_data.name.clone(); + // SchemaTypeInfo { + // field_data: Some(SchemaFieldData { + // description, + // ..field_data.clone() + // }), + // ..field.clone() + // } + // .to_ref_schema() + // } else { + // field.to_ref_schema() + // } + // } else { + // field.to_ref_schema() + // }; - field_schema.into() - }) - .collect(); - for field_schema in schema_fields { - if field_schema.ty_info.is_primitive_type() { - continue; - } - let SchemaDefinition { - id, - schema: _, - definitions: field_definitions, - dependencies: field_dependencies, - } = field_schema.to_definition(); - definitions.extend(field_definitions); - dependencies.extend(field_dependencies); - let Some(id) = id else { continue }; - if !definitions.contains_key(&id) { - definitions.insert(id, field_schema); - } - } - schema.min_items = Some(fields.fields.len() as u64); - schema.max_items = Some(fields.fields.len() as u64); - } - }, - InternalSchemaType::Array { - element_ty, - min_size, - max_size, - } => { - id = None; - let items_schema = element_ty.to_schema_type_info(); - schema.items = Some(items_schema.to_ref_schema().into()); - schema.min_items = min_size; - schema.max_items = max_size; + // field_schema.into() + // }) + // .collect(); + // for field_schema in schema_fields { + // if field_schema.ty_info.is_primitive_type() { + // continue; + // } + // let SchemaDefinition { + // id, + // schema: _, + // definitions: field_definitions, + // dependencies: field_dependencies, + // } = field_schema.to_definition(); + // definitions.extend(field_definitions); + // dependencies.extend(field_dependencies); + // let Some(id) = id else { continue }; + // if !definitions.contains_key(&id) { + // definitions.insert(id, field_schema); + // } + // } + // schema.min_items = Some(fields.fields.len() as u64); + // schema.max_items = Some(fields.fields.len() as u64); + // } + // }, + // InternalSchemaType::Array { + // element_ty, + // min_size, + // max_size, + // } => { + // id = None; + // let items_schema = element_ty.to_schema_type_info(); + // schema.items = Some(items_schema.to_ref_schema().into()); + // schema.min_items = min_size; + // schema.max_items = max_size; - if !items_schema.ty_info.is_primitive_type() { - let SchemaDefinition { - id, - schema: _, - definitions: field_definitions, - dependencies: field_dependencies, - } = items_schema.to_definition(); - dependencies.extend(field_dependencies); - definitions.extend(field_definitions); - if let Some(id) = id { - definitions.insert(id, items_schema); - } - } - } - InternalSchemaType::Optional { - generic, - ref schema_type_info, - } => { - let schema_optional = SchemaTypeInfo { - ty_info: TypeInformation::Type(Box::new(*generic.ty())), - ..(**schema_type_info).clone() - }; - if SchemaType::try_get_primitive_type_from_type_id(generic.type_id()).is_none() { - dependencies.insert(generic.type_id()); - } - schema.ref_type = None; - schema.schema_type = None; - schema.one_of = vec![ - Box::new(JsonSchemaBevyType { - schema_type: Some(SchemaTypeVariant::Single(SchemaType::Null)), - ..Default::default() - }), - Box::new(schema_optional.to_ref_schema()), - ]; - } - } - SchemaDefinition { - id, - schema, - definitions, - dependencies: dependencies.into_iter().collect(), - } - } + // if !items_schema.ty_info.is_primitive_type() { + // let SchemaDefinition { + // id, + // schema: _, + // definitions: field_definitions, + // dependencies: field_dependencies, + // } = items_schema.to_definition(); + // dependencies.extend(field_dependencies); + // definitions.extend(field_definitions); + // if let Some(id) = id { + // definitions.insert(id, items_schema); + // } + // } + // } + // InternalSchemaType::Optional { + // generic, + // ref schema_type_info, + // } => { + // let schema_optional = SchemaTypeInfo { + // ty_info: TypeInformation::Type(Box::new(*generic.ty())), + // ..(**schema_type_info).clone() + // }; + // if SchemaType::try_get_primitive_type_from_type_id(generic.type_id()).is_none() { + // dependencies.insert(generic.type_id()); + // } + // schema.ref_type = None; + // schema.schema_type = None; + // schema.one_of = vec![ + // Box::new(JsonSchemaBevyType { + // schema_type: Some(SchemaTypeVariant::Single(SchemaType::Null)), + // ..Default::default() + // }), + // Box::new(schema_optional.to_ref_schema()), + // ]; + // } + // } + // SchemaDefinition { + // id, + // schema, + // definitions, + // dependencies: dependencies.into_iter().collect(), + // } + // } } /// Traits for getting attribute information from a reflected value. @@ -1929,6 +1900,71 @@ where .collect() } +pub(crate) fn variant_to_definition( + variant: &VariantInfo, + registry: &TypeRegistry, +) -> JsonSchemaBevyType { + let field_data: SchemaFieldData = variant.into(); + let mut schema = JsonSchemaBevyType { + description: field_data.description.clone(), + kind: Some(SchemaKind::Value), + schema_type: Some(SchemaTypeVariant::Single(SchemaType::Object)), + additional_properties: Some(JsonSchemaVariant::BoolValue(false)), + ..Default::default() + }; + match variant { + VariantInfo::Unit(unit_variant_info) => { + schema.const_value = Some(unit_variant_info.name().to_string().into()); + schema.schema_type = Some(SchemaTypeVariant::Single(SchemaType::String)); + schema.additional_properties = None; + } + VariantInfo::Struct(struct_variant_info) => { + let mut subschema = JsonSchemaBevyType { + schema_type: Some(SchemaTypeVariant::Single(SchemaType::Object)), + ..Default::default() + }; + for field in struct_variant_info.iter() { + let Some(field_schema) = registry + .build_schema_reference_for_type_id(field.type_id(), &Default::default()) + else { + continue; + }; + subschema.properties.insert( + field.name().into(), + JsonSchemaVariant::Schema(Box::new(field_schema)), + ); + } + schema.properties = [(struct_variant_info.name().into(), subschema.into())].into(); + } + VariantInfo::Tuple(tuple_variant_info) => { + let mut subschema = JsonSchemaBevyType::default(); + if tuple_variant_info.field_len() == 1 { + if let Some(field) = tuple_variant_info.iter().next() { + subschema = registry + .build_schema_reference_for_type_id(field.type_id(), &Default::default()) + .unwrap_or_default(); + } + } else { + subschema.schema_type = Some(SchemaTypeVariant::Single(SchemaType::Array)); + for field in tuple_variant_info.iter() { + let Some(field_schema) = registry + .build_schema_reference_for_type_id(field.type_id(), &Default::default()) + else { + continue; + }; + subschema + .prefix_items + .push(JsonSchemaVariant::Schema(Box::new(field_schema))); + } + subschema.min_items = Some(tuple_variant_info.field_len() as u64); + subschema.max_items = Some(tuple_variant_info.field_len() as u64); + } + schema.properties = [(tuple_variant_info.name().into(), subschema.into())].into(); + } + }; + schema +} + pub(crate) trait TypeDefinitionBuilder { /// Builds a JSON schema for a given type ID. fn build_schema_for_type_id( @@ -1944,6 +1980,13 @@ pub(crate) trait TypeDefinitionBuilder { type_id: TypeId, metadata: &SchemaTypesMetadata, ) -> Option; + + /// Builds a schema reference for a given type ID. + fn build_schema_reference_for_type_id( + &self, + type_id: TypeId, + metadata: &SchemaTypesMetadata, + ) -> Option; } impl TypeDefinitionBuilder for TypeRegistry { @@ -1956,7 +1999,6 @@ impl TypeDefinitionBuilder for TypeRegistry { let type_info: TypeInformation = type_reg.into(); let schema_info = type_info.to_schema_type_info_with_metadata(metadata); let mut id: Option = schema_info.ty_info.try_get_type_reference_id(); - let mut definitions: HashMap = HashMap::new(); if let Some(custom_schema) = &schema_info.ty_info.try_get_custom_schema() { return Some((id, custom_schema.0.clone())); } @@ -2014,89 +2056,16 @@ impl TypeDefinitionBuilder for TypeRegistry { } InternalSchemaType::Regular(_) | InternalSchemaType::RegularType(_) - | InternalSchemaType::NoInfo => {} + | InternalSchemaType::NoInfo + | InternalSchemaType::EnumVariant(_) => {} InternalSchemaType::EnumHolder(variants) => { - let schema_fields: Vec<(Cow<'static, str>, SchemaDefinition)> = variants + schema.one_of = variants .iter() .map(|variant| { - ( - variant.name().into(), - SchemaTypeInfo::from(variant).to_definition(), - ) + let schema = variant_to_definition(variant, self); + Box::new(schema) }) .collect(); - schema.one_of = schema_fields - .iter() - .map(|(_, definition)| definition.schema.clone().into()) - .collect(); - } - InternalSchemaType::EnumVariant(variant_info) => { - schema.kind = Some(SchemaKind::Value); - schema.schema_type = Some(SchemaTypeVariant::Single(SchemaType::Object)); - let ty_info: TypeInformation = (&variant_info).into(); - let field_data: Option = Some((&variant_info).into()); - id = None; - match &variant_info { - VariantInfo::Struct(struct_variant_info) => { - let fields = get_fields_information(struct_variant_info.iter()); - - let schema_field = SchemaTypeInfo { - ty_info, - field_data, - internal_schema_type: InternalSchemaType::FieldsHolder( - FieldsInformation { - fields, - fields_type: FieldType::Named, - }, - ), - reflect_type_data: None, - }; - let definition = schema_field.to_definition(); - - schema.properties = - [(variant_info.name().into(), definition.schema.into())].into(); - schema.required = vec![variant_info.name().into()]; - } - VariantInfo::Tuple(tuple_variant_info) => { - let stored_fields = get_fields_information(tuple_variant_info.iter()); - let schema_field = SchemaTypeInfo { - ty_info, - field_data: None, - internal_schema_type: InternalSchemaType::FieldsHolder( - FieldsInformation { - fields: stored_fields, - - fields_type: FieldType::Unnamed, - }, - ), - reflect_type_data: None, - }; - let definition = schema_field.to_definition(); - - schema.properties = - [(variant_info.name().into(), definition.schema.into())].into(); - schema.required = vec![variant_info.name().into()]; - } - VariantInfo::Unit(unit_variant_info) => { - let internal_schema_type = (&ty_info).into(); - let schema_field = SchemaTypeInfo { - ty_info, - field_data, - internal_schema_type, - reflect_type_data: None, - }; - return Some(( - None, - JsonSchemaBevyType { - const_value: Some(unit_variant_info.name().to_string().into()), - schema_type: Some(SchemaTypeVariant::Single(SchemaType::String)), - description: schema_field.get_docs(), - kind: Some(SchemaKind::Value), - ..Default::default() - }, - )); - } - } } InternalSchemaType::FieldsHolder(fields) => match fields.fields_type { FieldType::Named => { @@ -2118,42 +2087,13 @@ impl TypeDefinitionBuilder for TypeRegistry { .collect(); } FieldType::Unnamed if fields.fields.len() == 1 => { - let field_schema = SchemaTypeInfo::from(&fields.fields[0]); - - let SchemaDefinition { - id, - schema: new_schema_type, - definitions: _, - dependencies: _, - } = field_schema.to_definition(); - if let Some(id) = id { - definitions.insert(id.clone(), field_schema); - schema.ref_type = Some(TypeReferencePath::definition(id)); - } else { - let (type_path, short_path, crate_name, module_path) = - if let Some(type_path_table) = - schema_info.ty_info.try_get_type_path_table() - { - ( - type_path_table.path().into(), - type_path_table.short_path().into(), - type_path_table.crate_name().map(Into::into), - type_path_table.module_path().map(Into::into), - ) - } else { - (Cow::default(), Cow::default(), None, None) - }; - schema = JsonSchemaBevyType { - short_path, - type_path, - module_path, - crate_name, - kind: Some(SchemaKind::TupleStruct), - schema_type: (&schema_info).into(), - description: schema_info.get_docs(), - ..new_schema_type - }; - } + schema = SchemaTypeInfo::from(&fields.fields[0]).to_ref_schema(); + schema.kind = Some(SchemaKind::Tuple); + // if let Some((_, sub_schema)) = self.build_schema_for_type_id( + // fields.fields[0].type_info.type_id(), + // &Default::default(), + // ) { + // } } s => { let schema_fields: Vec = @@ -2238,11 +2178,9 @@ impl TypeDefinitionBuilder for TypeRegistry { type_id: TypeId, metadata: &SchemaTypesMetadata, ) -> Option { - let Some((_, mut schema)) = self.build_schema_for_type_id(type_id, metadata) else { - return None; - }; + let (_, mut schema) = self.build_schema_for_type_id(type_id, metadata)?; let dependencies = self.get_type_dependencies(type_id); - eprintln!("{} -> {:#?}", schema.type_path, dependencies.len()); + schema.schema = Some(super::json_schema::SchemaMarker.into()); schema.definitions = dependencies .into_iter() .flat_map(|id| { @@ -2255,11 +2193,46 @@ impl TypeDefinitionBuilder for TypeRegistry { .collect(); Some(schema) } + + fn build_schema_reference_for_type_id( + &self, + type_id: TypeId, + metadata: &SchemaTypesMetadata, + ) -> Option { + let type_reg = self.get(type_id)?; + if let Some(primitive_type) = SchemaType::try_get_primitive_type_from_type_id(type_id) { + let kind = Some(SchemaKind::Value); + let not = if is_non_zero_number_type(type_id) { + Some(Box::new(JsonSchemaBevyType { + const_value: Some(0.into()), + ..default() + })) + } else { + None + }; + let range: MinMaxValues = type_id.into(); + return Some(JsonSchemaBevyType { + kind, + not, + type_path: type_reg.type_info().type_path().into(), + minimum: range.min.get_inclusive(), + maximum: range.max.get_inclusive(), + exclusive_minimum: range.min.get_exclusive(), + exclusive_maximum: range.max.get_exclusive(), + schema_type: Some(SchemaTypeVariant::Single(primitive_type)), + ..default() + }); + } + let type_info: TypeInformation = type_reg.into(); + let schema_info = type_info.to_schema_type_info_with_metadata(metadata); + let schema = schema_info.to_ref_schema(); + Some(schema) + } } #[cfg(test)] pub(super) mod tests { - use bevy_ecs::{component::Component, name::Name, reflect::AppTypeRegistry}; + use bevy_ecs::{component::Component, reflect::AppTypeRegistry}; use bevy_platform::collections::HashMap; use bevy_reflect::GetTypeRegistration; @@ -2278,7 +2251,13 @@ pub(super) mod tests { let schema_validator = jsonschema::options() .with_draft(jsonschema::Draft::Draft202012) .build(&schema_value) - .expect("Failed to build schema validator"); + .unwrap_or_else(|errors| { + panic!( + "Failed to build schema validator because of errors: {:?}, schema: {}", + errors, + serde_json::to_string_pretty(&schema).unwrap_or_default() + ) + }); let default_value = serde_json::to_value(T::default()).unwrap(); assert!( schema_validator.validate(&default_value).is_ok(), @@ -2297,7 +2276,7 @@ pub(super) mod tests { } } for value in valid_values { - if let Err(error) = schema_validator.validate(&value) { + if let Err(error) = schema_validator.validate(value) { errors.push((error, value.clone())); } } @@ -2309,7 +2288,7 @@ pub(super) mod tests { ); for value in invalid_values { assert!( - schema_validator.validate(&value).is_err(), + schema_validator.validate(value).is_err(), "Validation should fail for invalid value: {}, schema: {}", value, serde_json::to_string_pretty(&schema_value).unwrap_or_default() @@ -2397,11 +2376,19 @@ pub(super) mod tests { /// Test doc a: u16, } + let atr = AppTypeRegistry::default(); + { + let mut register = atr.write(); + register.register::(); + register.register::(); + register.register_type_data::(); + } + let type_registry = atr.read(); + let (_, schema) = type_registry + .build_schema_for_type_id(TypeId::of::(), &Default::default()) + .expect(""); validate::( - TypeInformation::TypeRegistration(Foo::get_type_registration()) - .to_schema_type_info() - .to_definition() - .into(), + schema, &[Foo { a: 5 }, Foo { a: 1111 }], &[serde_json::json!({"a": 5}), serde_json::json!({"a": 1})], &[ @@ -2410,20 +2397,9 @@ pub(super) mod tests { serde_json::json!({"a": 5555,"b": 5555}), ], ); - let atr = AppTypeRegistry::default(); - { - let mut register = atr.write(); - register.register::(); - register.register_type_data::(); - } - let type_registry = atr.read(); - let declaration = TypeInformation::from( - type_registry - .get(TypeId::of::()) - .expect(""), - ) - .to_schema_type_info(); - let schema: JsonSchemaBevyType = declaration.to_definition().into(); + let (_, schema) = type_registry + .build_schema_for_type_id(TypeId::of::(), &Default::default()) + .expect(""); validate::( schema, @@ -2451,27 +2427,29 @@ pub(super) mod tests { #[reflect(@..13_usize)] pub usize, ); - - let struct_info = TupleTest::get_type_registration() - .type_info() - .as_tuple_struct() - .expect("Should not fail"); - let field_info = struct_info.iter().next().expect("Should not fail"); - let type_info = SchemaTypeInfo::from(field_info); - let schema_type: Option = (&type_info).into(); - let range = type_info.get_range(); + let atr = AppTypeRegistry::default(); + { + let mut register = atr.write(); + register.register::(); + } + let types = atr.read(); + let schema = types + .build_schema_for_type_id_with_definitions( + TypeId::of::(), + &Default::default(), + ) + .expect(""); + let range: MinMaxValues = (&schema).into(); assert_eq!(range.min, Some(BoundValue::Inclusive(0.into()))); assert_eq!(range.max, Some(BoundValue::Exclusive(13.into()))); assert_eq!( - schema_type, + schema.schema_type, Some(SchemaTypeVariant::Single(SchemaType::Integer)) ); - assert_eq!(type_info.get_docs(), Some("Test documentation".into())); + #[cfg(feature = "documentation")] + assert_eq!(schema.description, Some("Test documentation".into())); validate::( - TypeInformation::TypeRegistration(TupleTest::get_type_registration()) - .to_schema_type_info() - .to_definition() - .into(), + schema, &[TupleTest(10), TupleTest(11), TupleTest(0)], &[serde_json::json!(5), serde_json::json!(1)], &[serde_json::json!(55), serde_json::json!(-5555)], @@ -2492,9 +2470,18 @@ pub(super) mod tests { Variant3(isize, usize), Variant4(usize), } - let type_info = - TypeInformation::from(&EnumTest::get_type_registration()).to_schema_type_info(); - let schema: JsonSchemaBevyType = type_info.to_definition().into(); + let atr = AppTypeRegistry::default(); + { + let mut register = atr.write(); + register.register::(); + } + let types = atr.read(); + let schema = types + .build_schema_for_type_id_with_definitions( + TypeId::of::(), + &Default::default(), + ) + .expect(""); validate::( schema, &[ @@ -2511,21 +2498,6 @@ pub(super) mod tests { ); } - #[test] - fn name_field_test() { - #[derive(Reflect, Default, Deserialize, Serialize)] - pub struct EnumTest { - pub name: Name, - } - let type_info = - TypeInformation::from(&EnumTest::get_type_registration()).to_schema_type_info(); - let schema: JsonSchemaBevyType = type_info.to_definition().into(); - eprintln!( - "{}", - serde_json::to_string_pretty(&schema).unwrap_or_default() - ); - } - #[test] fn optional_tests() { #[derive(Reflect, Default, Deserialize, Serialize)] @@ -2559,9 +2531,18 @@ pub(super) mod tests { pub struct ArrayComponent { pub array: [u8; 3], } - let type_info = - TypeInformation::from(&ArrayComponent::get_type_registration()).to_schema_type_info(); - let schema: JsonSchemaBevyType = type_info.to_definition().into(); + let atr = AppTypeRegistry::default(); + { + let mut register = atr.write(); + register.register::(); + } + let types = atr.read(); + let schema = types + .build_schema_for_type_id_with_definitions( + TypeId::of::(), + &Default::default(), + ) + .expect(""); validate::( schema, &[ @@ -2628,9 +2609,18 @@ pub(super) mod tests { pub struct HashMapStruct { pub map: HashMap>, } - let type_info = - TypeInformation::from(&HashMapStruct::get_type_registration()).to_schema_type_info(); - let schema: JsonSchemaBevyType = type_info.to_definition().into(); + let atr = AppTypeRegistry::default(); + { + let mut register = atr.write(); + register.register::(); + } + let types = atr.read(); + let schema = types + .build_schema_for_type_id_with_definitions( + TypeId::of::(), + &Default::default(), + ) + .expect(""); validate::( schema, &[HashMapStruct { @@ -2702,6 +2692,24 @@ pub(super) mod tests { ); } + #[test] + fn test_out_optional_tuple() { + let atr = AppTypeRegistry::default(); + { + let mut register = atr.write(); + register.register::>(); + } + let types = atr.read(); + + let schema = types + .build_schema_for_type_id_with_definitions( + TypeId::of::>(), + &Default::default(), + ) + .expect(""); + validate::>(schema, &[None], &[], &[]); + } + #[test] fn reflect_tuple_struct_with_one_field_that_is_struct() { use bevy_ecs::prelude::ReflectComponent; @@ -2716,9 +2724,18 @@ pub(super) mod tests { /// A tuple struct with one field. pub struct TupleStruct(pub HashMap); - let type_info = - TypeInformation::from(&TupleStruct::get_type_registration()).to_schema_type_info(); - let schema: JsonSchemaBevyType = type_info.to_definition().into(); + let atr = AppTypeRegistry::default(); + { + let mut register = atr.write(); + register.register::(); + } + let types = atr.read(); + let schema = types + .build_schema_for_type_id_with_definitions( + TypeId::of::(), + &Default::default(), + ) + .expect(""); validate::( schema, &[TupleStruct( @@ -2744,10 +2761,18 @@ pub(super) mod tests { TupleStruct(NonZeroI8::new(15i8).expect("Should not fail")) } } - - let type_info = - TypeInformation::from(&TupleStruct::get_type_registration()).to_schema_type_info(); - let schema: JsonSchemaBevyType = type_info.to_definition().into(); + let atr = AppTypeRegistry::default(); + { + let mut register = atr.write(); + register.register::(); + } + let types = atr.read(); + let schema = types + .build_schema_for_type_id_with_definitions( + TypeId::of::(), + &Default::default(), + ) + .expect(""); validate::( schema, @@ -2774,17 +2799,26 @@ pub(super) mod tests { TupleStruct(15) } } - let type_info = - TypeInformation::from(&TupleStruct::get_type_registration()).to_schema_type_info(); - let s: JsonSchemaBevyType = type_info.to_definition().into(); - let range: MinMaxValues = (&s).into(); + let atr = AppTypeRegistry::default(); + { + let mut register = atr.write(); + register.register::(); + } + let types = atr.read(); + let schema = types + .build_schema_for_type_id_with_definitions( + TypeId::of::(), + &Default::default(), + ) + .expect(""); + let range: MinMaxValues = (&schema).into(); assert!(!range.in_range(51)); assert!(range.in_range(15)); assert!(range.in_range(50)); assert!(!range.in_range(51)); validate::( - s, + schema, &[TupleStruct(15)], &[ serde_json::json!(15),