Allow providing CustomInternalSchemaData instead of only adding data for

forcing as array.
This commit is contained in:
Piotr Siuszko 2025-07-09 19:14:49 +02:00
parent 5ad2351c2a
commit 2800e77ef9
3 changed files with 210 additions and 132 deletions

View File

@ -1866,12 +1866,12 @@ mod tests {
let atr = AppTypeRegistry::default(); let atr = AppTypeRegistry::default();
{ {
use crate::schemas::ReflectJsonSchemaForceAsArray; use crate::schemas::RegisterReflectJsonSchemas;
let mut register = atr.write(); let mut register = atr.write();
register.register::<NestedStruct>(); register.register::<NestedStruct>();
register.register::<bevy_math::Vec3>(); register.register::<bevy_math::Vec3>();
register.register_type_data::<bevy_math::Vec3, ReflectJsonSchemaForceAsArray>(); register.register_force_as_array::<bevy_math::Vec3>();
} }
let mut world = World::new(); let mut world = World::new();
world.insert_resource(atr); world.insert_resource(atr);

View File

@ -7,12 +7,16 @@ use bevy_ecs::{
}; };
use bevy_platform::collections::HashMap; use bevy_platform::collections::HashMap;
use bevy_reflect::{ use bevy_reflect::{
prelude::ReflectDefault, FromType, Reflect, ReflectDeserialize, ReflectSerialize, TypeData, prelude::ReflectDefault, FromType, GetTypeRegistration, Reflect, ReflectDeserialize,
TypeRegistration, ReflectSerialize, TypeData, TypePath, TypeRegistration,
}; };
use core::any::TypeId; use core::any::TypeId;
use crate::schemas::{json_schema::JsonSchemaBevyType, open_rpc::OpenRpcDocument}; use crate::schemas::{
json_schema::JsonSchemaBevyType,
open_rpc::OpenRpcDocument,
reflect_info::{FieldsInformation, InternalSchemaType},
};
pub mod json_schema; pub mod json_schema;
pub mod open_rpc; pub mod open_rpc;
@ -27,13 +31,23 @@ pub struct SchemaTypesMetadata {
pub type_data_map: HashMap<TypeId, Cow<'static, str>>, pub type_data_map: HashMap<TypeId, Cow<'static, str>>,
} }
/// Reflect-compatible custom JSON Schema for this type /// Custom internal schema data.
#[derive(Clone)] #[derive(Debug, Resource, Reflect, Clone)]
pub struct ReflectJsonSchemaForceAsArray; #[reflect(Resource)]
pub struct CustomInternalSchemaData(pub InternalSchemaType);
impl<T: Reflect> FromType<T> for ReflectJsonSchemaForceAsArray { impl CustomInternalSchemaData {
fn from_type() -> Self { /// Creates a new `CustomInternalSchema` with a forced array type. Works for structs only.
ReflectJsonSchemaForceAsArray pub fn force_array<T: GetTypeRegistration>() -> Option<Self> {
match T::get_type_registration().type_info() {
bevy_reflect::TypeInfo::Struct(struct_info) => Some(CustomInternalSchemaData(
InternalSchemaType::FieldsHolder(FieldsInformation::new(
struct_info.iter(),
reflect_info::FieldType::ForceUnnamed,
)),
)),
_ => None,
}
} }
} }
@ -43,62 +57,76 @@ pub(crate) trait RegisterReflectJsonSchemas {
fn register_schema_base_types(&mut self) { fn register_schema_base_types(&mut self) {
#[cfg(feature = "bevy_math")] #[cfg(feature = "bevy_math")]
{ {
self.register_type_data_internal::<bevy_math::Vec2, ReflectJsonSchemaForceAsArray>(); self.register_force_as_array::<bevy_math::Vec2>();
self.register_type_data_internal::<bevy_math::DVec2, ReflectJsonSchemaForceAsArray>(); self.register_force_as_array::<bevy_math::DVec2>();
self.register_type_data_internal::<bevy_math::I8Vec2, ReflectJsonSchemaForceAsArray>(); self.register_force_as_array::<bevy_math::I8Vec2>();
self.register_type_data_internal::<bevy_math::U8Vec2, ReflectJsonSchemaForceAsArray>(); self.register_force_as_array::<bevy_math::U8Vec2>();
self.register_type_data_internal::<bevy_math::I16Vec2, ReflectJsonSchemaForceAsArray>(); self.register_force_as_array::<bevy_math::I16Vec2>();
self.register_type_data_internal::<bevy_math::U16Vec2, ReflectJsonSchemaForceAsArray>(); self.register_force_as_array::<bevy_math::U16Vec2>();
self.register_type_data_internal::<bevy_math::IVec2, ReflectJsonSchemaForceAsArray>(); self.register_force_as_array::<bevy_math::IVec2>();
self.register_type_data_internal::<bevy_math::UVec2, ReflectJsonSchemaForceAsArray>(); self.register_force_as_array::<bevy_math::UVec2>();
self.register_type_data_internal::<bevy_math::I64Vec2, ReflectJsonSchemaForceAsArray>(); self.register_force_as_array::<bevy_math::I64Vec2>();
self.register_type_data_internal::<bevy_math::U64Vec2, ReflectJsonSchemaForceAsArray>(); self.register_force_as_array::<bevy_math::U64Vec2>();
self.register_type_data_internal::<bevy_math::BVec2, ReflectJsonSchemaForceAsArray>(); self.register_force_as_array::<bevy_math::BVec2>();
self.register_type_data_internal::<bevy_math::Vec3, ReflectJsonSchemaForceAsArray>(); self.register_force_as_array::<bevy_math::Vec3>();
self.register_type_data_internal::<bevy_math::DVec3, ReflectJsonSchemaForceAsArray>(); self.register_force_as_array::<bevy_math::DVec3>();
self.register_type_data_internal::<bevy_math::I8Vec3, ReflectJsonSchemaForceAsArray>(); self.register_force_as_array::<bevy_math::I8Vec3>();
self.register_type_data_internal::<bevy_math::U8Vec3, ReflectJsonSchemaForceAsArray>(); self.register_force_as_array::<bevy_math::U8Vec3>();
self.register_type_data_internal::<bevy_math::I16Vec3, ReflectJsonSchemaForceAsArray>(); self.register_force_as_array::<bevy_math::I16Vec3>();
self.register_type_data_internal::<bevy_math::U16Vec3, ReflectJsonSchemaForceAsArray>(); self.register_force_as_array::<bevy_math::U16Vec3>();
self.register_type_data_internal::<bevy_math::IVec3, ReflectJsonSchemaForceAsArray>(); self.register_force_as_array::<bevy_math::IVec3>();
self.register_type_data_internal::<bevy_math::UVec3, ReflectJsonSchemaForceAsArray>(); self.register_force_as_array::<bevy_math::UVec3>();
self.register_type_data_internal::<bevy_math::I64Vec3, ReflectJsonSchemaForceAsArray>(); self.register_force_as_array::<bevy_math::I64Vec3>();
self.register_type_data_internal::<bevy_math::U64Vec3, ReflectJsonSchemaForceAsArray>(); self.register_force_as_array::<bevy_math::U64Vec3>();
self.register_type_data_internal::<bevy_math::BVec3, ReflectJsonSchemaForceAsArray>(); self.register_force_as_array::<bevy_math::BVec3>();
self.register_type_data_internal::<bevy_math::Vec4, ReflectJsonSchemaForceAsArray>(); self.register_force_as_array::<bevy_math::Vec4>();
self.register_type_data_internal::<bevy_math::DVec4, ReflectJsonSchemaForceAsArray>(); self.register_force_as_array::<bevy_math::DVec4>();
self.register_type_data_internal::<bevy_math::I8Vec4, ReflectJsonSchemaForceAsArray>(); self.register_force_as_array::<bevy_math::I8Vec4>();
self.register_type_data_internal::<bevy_math::U8Vec4, ReflectJsonSchemaForceAsArray>(); self.register_force_as_array::<bevy_math::U8Vec4>();
self.register_type_data_internal::<bevy_math::I16Vec4, ReflectJsonSchemaForceAsArray>(); self.register_force_as_array::<bevy_math::I16Vec4>();
self.register_type_data_internal::<bevy_math::U16Vec4, ReflectJsonSchemaForceAsArray>(); self.register_force_as_array::<bevy_math::U16Vec4>();
self.register_type_data_internal::<bevy_math::IVec4, ReflectJsonSchemaForceAsArray>(); self.register_force_as_array::<bevy_math::IVec4>();
self.register_type_data_internal::<bevy_math::UVec4, ReflectJsonSchemaForceAsArray>(); self.register_force_as_array::<bevy_math::UVec4>();
self.register_type_data_internal::<bevy_math::I64Vec4, ReflectJsonSchemaForceAsArray>(); self.register_force_as_array::<bevy_math::I64Vec4>();
self.register_type_data_internal::<bevy_math::U64Vec4, ReflectJsonSchemaForceAsArray>(); self.register_force_as_array::<bevy_math::U64Vec4>();
self.register_type_data_internal::<bevy_math::BVec4, ReflectJsonSchemaForceAsArray>(); self.register_force_as_array::<bevy_math::BVec4>();
self.register_type_data_internal::<bevy_math::Quat, ReflectJsonSchemaForceAsArray>(); self.register_force_as_array::<bevy_math::Quat>();
self.register_type_data_internal::<bevy_math::DQuat, ReflectJsonSchemaForceAsArray>(); self.register_force_as_array::<bevy_math::DQuat>();
} }
self.register_type_internal::<OpenRpcDocument>(); self.register_type_internal::<OpenRpcDocument>();
self.register_type_data_internal::<OpenRpcDocument, ReflectJsonSchema>(); self.register_type_data_internal::<OpenRpcDocument, ReflectJsonSchema>();
} }
/// Registers a type by value.
fn register_data_type_by_value<T, D>(&mut self, data: D)
where
T: Reflect + TypePath + GetTypeRegistration,
D: TypeData;
fn register_type_internal<T>(&mut self) fn register_type_internal<T>(&mut self)
where where
T: bevy_reflect::GetTypeRegistration; T: GetTypeRegistration;
fn register_type_data_internal<T, D>(&mut self) fn register_type_data_internal<T, D>(&mut self)
where where
T: Reflect + bevy_reflect::TypePath + bevy_reflect::GetTypeRegistration, T: Reflect + TypePath + GetTypeRegistration,
D: TypeData + FromType<T>; D: TypeData + FromType<T>;
fn register_force_as_array<T>(&mut self)
where
T: Reflect + TypePath + GetTypeRegistration,
{
let Some(data) = CustomInternalSchemaData::force_array::<T>() else {
return;
};
self.register_data_type_by_value::<T, CustomInternalSchemaData>(data);
}
} }
impl RegisterReflectJsonSchemas for bevy_reflect::TypeRegistry { impl RegisterReflectJsonSchemas for bevy_reflect::TypeRegistry {
fn register_type_data_internal<T, D>(&mut self) fn register_type_data_internal<T, D>(&mut self)
where where
T: Reflect + bevy_reflect::TypePath + bevy_reflect::GetTypeRegistration, T: Reflect + TypePath + GetTypeRegistration,
D: TypeData + FromType<T>, D: TypeData + FromType<T>,
{ {
if !self.contains(TypeId::of::<T>()) { if !self.contains(TypeId::of::<T>()) {
@ -109,15 +137,25 @@ impl RegisterReflectJsonSchemas for bevy_reflect::TypeRegistry {
fn register_type_internal<T>(&mut self) fn register_type_internal<T>(&mut self)
where where
T: bevy_reflect::GetTypeRegistration, T: GetTypeRegistration,
{ {
self.register::<T>(); self.register::<T>();
} }
fn register_data_type_by_value<T, D>(&mut self, data: D)
where
T: Reflect + TypePath + GetTypeRegistration,
D: TypeData,
{
self.get_mut(TypeId::of::<T>())
.expect("SHOULD NOT HAPPENED")
.insert(data);
}
} }
impl RegisterReflectJsonSchemas for bevy_app::App { impl RegisterReflectJsonSchemas for bevy_app::App {
fn register_type_data_internal<T, D>(&mut self) fn register_type_data_internal<T, D>(&mut self)
where where
T: Reflect + bevy_reflect::TypePath + bevy_reflect::GetTypeRegistration, T: Reflect + TypePath + GetTypeRegistration,
D: TypeData + FromType<T>, D: TypeData + FromType<T>,
{ {
self.register_type::<T>(); self.register_type::<T>();
@ -126,10 +164,22 @@ impl RegisterReflectJsonSchemas for bevy_app::App {
fn register_type_internal<T>(&mut self) fn register_type_internal<T>(&mut self)
where where
T: bevy_reflect::GetTypeRegistration, T: GetTypeRegistration,
{ {
self.register_type::<T>(); self.register_type::<T>();
} }
fn register_data_type_by_value<T, D>(&mut self, data: D)
where
T: Reflect + TypePath + GetTypeRegistration,
D: TypeData,
{
let sub_app = self.main_mut();
let world = sub_app.world_mut();
let registry = world.resource_mut::<bevy_ecs::reflect::AppTypeRegistry>();
let mut r = registry.write();
r.register_data_type_by_value::<T, D>(data);
}
} }
/// Reflect-compatible custom JSON Schema for this type /// Reflect-compatible custom JSON Schema for this type

View File

@ -2,7 +2,7 @@
use crate::schemas::json_schema::{ use crate::schemas::json_schema::{
JsonSchemaBevyType, JsonSchemaVariant, SchemaKind, SchemaType, SchemaTypeVariant, JsonSchemaBevyType, JsonSchemaVariant, SchemaKind, SchemaType, SchemaTypeVariant,
}; };
use crate::schemas::{ReflectJsonSchemaForceAsArray, SchemaTypesMetadata}; use crate::schemas::{CustomInternalSchemaData, SchemaTypesMetadata};
use alloc::borrow::Cow; use alloc::borrow::Cow;
use alloc::sync::Arc; use alloc::sync::Arc;
use bevy_derive::{Deref, DerefMut}; use bevy_derive::{Deref, DerefMut};
@ -298,8 +298,8 @@ impl From<&TypePathTable> for TypeReferenceId {
} }
/// Information about the field type. /// Information about the field type.
#[derive(Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Hash, Default)] #[derive(Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Hash, Default, Reflect)]
pub(crate) enum FieldType { pub enum FieldType {
/// Named field type. /// Named field type.
Named, Named,
/// Unnamed field type. /// Unnamed field type.
@ -310,8 +310,8 @@ pub(crate) enum FieldType {
} }
/// Information about the attributes of a field. /// Information about the attributes of a field.
#[derive(Clone, Debug, Deref, DerefMut, Default)] #[derive(Clone, Debug, Deref, DerefMut, Default, Reflect)]
pub(crate) struct FieldsInformation { pub struct FieldsInformation {
/// Fields information. /// Fields information.
#[deref] #[deref]
fields: Vec<SchemaFieldData>, fields: Vec<SchemaFieldData>,
@ -319,6 +319,19 @@ pub(crate) struct FieldsInformation {
fields_type: FieldType, fields_type: FieldType,
} }
impl FieldsInformation {
/// Creates a new instance of `FieldsInformation`.
pub fn new<'a, 'b, T>(iterator: Iter<'a, T>, fields_type: FieldType) -> Self
where
SchemaFieldData: From<&'a T>,
{
FieldsInformation {
fields: get_fields_information(iterator),
fields_type,
}
}
}
#[derive(Clone, Debug, Deref)] #[derive(Clone, Debug, Deref)]
/// Information about the attributes of a field. /// Information about the attributes of a field.
pub(crate) struct AttributesInformation(Arc<TypeIdMap<Box<dyn Reflect>>>); pub(crate) struct AttributesInformation(Arc<TypeIdMap<Box<dyn Reflect>>>);
@ -385,30 +398,20 @@ fn try_get_regex_for_type(id: TypeId) -> Option<Cow<'static, str>> {
} }
/// Represents the data of a field in a schema. /// Represents the data of a field in a schema.
#[derive(Clone)] #[derive(Clone, Reflect, Debug)]
pub(crate) struct SchemaFieldData { pub struct SchemaFieldData {
/// Name of the field. /// Name of the field.
pub name: Option<Cow<'static, str>>, pub name: Option<Cow<'static, str>>,
/// Index of the field. Can be provided for named fields when the data is obtained from containing struct definition. /// Index of the field. Can be provided for named fields when the data is obtained from containing struct definition.
pub index: Option<usize>, pub index: Option<usize>,
/// Description of the field. /// Description of the field.
pub description: Option<Cow<'static, str>>, pub description: Option<Cow<'static, str>>,
/// Attributes of the field. /// Custom of the field.
pub attributes: AttributesInformation, pub range: Option<MinMaxValues>,
/// Type of the field. /// Type of the field.
pub type_id: TypeId, pub type_id: TypeId,
} }
impl Debug for SchemaFieldData {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("SchemaFieldData")
.field("name", &self.name)
.field("index", &self.index)
.field("description", &self.description)
.finish()
}
}
impl SchemaFieldData { impl SchemaFieldData {
/// Returns the name of the field. /// Returns the name of the field.
pub fn to_name(&self) -> Cow<'static, str> { pub fn to_name(&self) -> Cow<'static, str> {
@ -810,11 +813,28 @@ impl From<TypeId> for MinMaxValues {
} }
} }
} }
/// Enum representing the internal schema type information for different Rust types.
#[derive(Clone, Debug, Reflect)]
pub enum SchemaEnumType {
/// Represents a constant value.
Const,
/// Represents a set of fields with their respective types.
Fields(FieldsInformation),
}
/// Represents a variant of an enum type.
#[derive(Clone, Debug, Reflect)]
pub struct EnumVariantInfo {
/// Field data for the enum variant.
pub field_data: SchemaFieldData,
/// Information about the enum variant.
pub info: SchemaEnumType,
}
/// Enum representing the internal schema type information for different Rust types. /// Enum representing the internal schema type information for different Rust types.
/// This enum categorizes how different types should be represented in JSON schema. /// This enum categorizes how different types should be represented in JSON schema.
#[derive(Clone, Debug)] #[derive(Clone, Debug, Reflect)]
pub(crate) enum InternalSchemaType { pub enum InternalSchemaType {
/// Represents array-like types (Vec, arrays, lists, sets). /// Represents array-like types (Vec, arrays, lists, sets).
Array { Array {
/// Element type information for the array. /// Element type information for the array.
@ -825,13 +845,13 @@ pub(crate) enum InternalSchemaType {
max_size: Option<u64>, max_size: Option<u64>,
}, },
/// Holds all variants of an enum type. /// Holds all variants of an enum type.
EnumHolder(Vec<VariantInfo>), EnumHolder(Vec<EnumVariantInfo>),
/// Holds named fields for struct, tuple, and tuple struct types. /// Holds named fields for struct, tuple, and tuple struct types.
FieldsHolder(FieldsInformation), FieldsHolder(FieldsInformation),
/// Represents an Optional type (e.g., `Option<T>`). /// Represents an Optional type (e.g., `Option<T>`).
Optional { Optional {
/// Generic information about the wrapped type `T` in `Option<T>`. /// Type information for the wrapped type `T` in `Option<T>`.
generic: GenericInfo, generic: TypeId,
}, },
/// Represents a Map type (e.g., `HashMap`<K, V>). /// Represents a Map type (e.g., `HashMap`<K, V>).
Map { Map {
@ -867,6 +887,9 @@ impl InternalSchemaType {
value: &TypeRegistration, value: &TypeRegistration,
registry: &TypeRegistry, registry: &TypeRegistry,
) -> InternalSchemaType { ) -> InternalSchemaType {
if let Some(data) = value.data::<CustomInternalSchemaData>() {
return data.0.clone();
}
if let Some(primitive) = if let Some(primitive) =
SchemaType::try_get_primitive_type_from_type_id(value.type_info().type_id()) SchemaType::try_get_primitive_type_from_type_id(value.type_info().type_id())
{ {
@ -879,14 +902,9 @@ impl InternalSchemaType {
match value.type_info() { match value.type_info() {
TypeInfo::Struct(struct_info) => { TypeInfo::Struct(struct_info) => {
let fields = get_fields_information(struct_info.iter()); let fields = get_fields_information(struct_info.iter());
let fields_type = if value.data::<ReflectJsonSchemaForceAsArray>().is_some() {
FieldType::ForceUnnamed
} else {
FieldType::Named
};
InternalSchemaType::FieldsHolder(FieldsInformation { InternalSchemaType::FieldsHolder(FieldsInformation {
fields, fields,
fields_type, fields_type: FieldType::Named,
}) })
} }
TypeInfo::TupleStruct(info) => { TypeInfo::TupleStruct(info) => {
@ -916,8 +934,10 @@ impl InternalSchemaType {
fields_type: FieldType::Unnamed, fields_type: FieldType::Unnamed,
}), }),
TypeInfo::Enum(enum_info) => match enum_info.try_get_optional() { TypeInfo::Enum(enum_info) => match enum_info.try_get_optional() {
Some(e) => InternalSchemaType::Optional { generic: e.clone() }, Some(e) => InternalSchemaType::Optional {
None => InternalSchemaType::EnumHolder(enum_info.iter().cloned().collect()), generic: e.ty().id(),
},
None => InternalSchemaType::EnumHolder(get_enum_information(enum_info.iter())),
}, },
TypeInfo::List(list_info) => InternalSchemaType::Array { TypeInfo::List(list_info) => InternalSchemaType::Array {
@ -960,23 +980,15 @@ impl InternalSchemaType {
} }
} }
InternalSchemaType::EnumHolder(variant_infos) => { InternalSchemaType::EnumHolder(variant_infos) => {
for variant_info in variant_infos { for variant_info in variant_infos.iter() {
let subschema = match variant_info { let variant_dependencies = match &variant_info.info {
VariantInfo::Struct(struct_variant_info) => { SchemaEnumType::Const => continue,
InternalSchemaType::FieldsHolder(FieldsInformation { SchemaEnumType::Fields(fields_information) => {
fields: get_fields_information(struct_variant_info.iter()), InternalSchemaType::FieldsHolder(fields_information.clone())
fields_type: FieldType::Named, .get_dependencies(registry)
})
} }
VariantInfo::Tuple(tuple_variant_info) => {
InternalSchemaType::FieldsHolder(FieldsInformation {
fields: get_fields_information(tuple_variant_info.iter()),
fields_type: FieldType::Unnamed,
})
}
VariantInfo::Unit(_) => continue,
}; };
dependencies.extend(subschema.get_dependencies(registry)); dependencies.extend(variant_dependencies);
} }
} }
InternalSchemaType::FieldsHolder(fields_information) => { InternalSchemaType::FieldsHolder(fields_information) => {
@ -995,7 +1007,7 @@ impl InternalSchemaType {
} }
} }
InternalSchemaType::Optional { generic } => { InternalSchemaType::Optional { generic } => {
if let Some(reg) = registry.get(generic.type_id()) { if let Some(reg) = registry.get(*generic) {
if SchemaType::try_get_primitive_type_from_type_id(reg.type_id()).is_none() { if SchemaType::try_get_primitive_type_from_type_id(reg.type_id()).is_none() {
let subschema = InternalSchemaType::from_type_registration(reg, registry); let subschema = InternalSchemaType::from_type_registration(reg, registry);
if !subschema.is_optional() { if !subschema.is_optional() {
@ -1157,13 +1169,13 @@ pub trait AttributeInfoReflect {
impl From<&UnnamedField> for SchemaFieldData { impl From<&UnnamedField> for SchemaFieldData {
fn from(value: &UnnamedField) -> Self { fn from(value: &UnnamedField) -> Self {
let attributes: AttributesInformation = value.custom_attributes().into(); let range = value.custom_attributes().get_range_by_id(value.type_id());
#[cfg(feature = "documentation")] #[cfg(feature = "documentation")]
let description = value.docs().map(|s| Cow::Owned(s.to_owned())); let description = value.docs().map(|s| Cow::Owned(s.to_owned()));
#[cfg(not(feature = "documentation"))] #[cfg(not(feature = "documentation"))]
let description = None; let description = None;
SchemaFieldData { SchemaFieldData {
attributes, range,
name: None, name: None,
index: Some(value.index()), index: Some(value.index()),
description, description,
@ -1173,7 +1185,7 @@ impl From<&UnnamedField> for SchemaFieldData {
} }
impl From<&NamedField> for SchemaFieldData { impl From<&NamedField> for SchemaFieldData {
fn from(value: &NamedField) -> Self { fn from(value: &NamedField) -> Self {
let attributes: AttributesInformation = value.custom_attributes().into(); let range = value.custom_attributes().get_range_by_id(value.type_id());
#[cfg(feature = "documentation")] #[cfg(feature = "documentation")]
let description = value.docs().map(|s| Cow::Owned(s.to_owned())); let description = value.docs().map(|s| Cow::Owned(s.to_owned()));
#[cfg(not(feature = "documentation"))] #[cfg(not(feature = "documentation"))]
@ -1182,7 +1194,7 @@ impl From<&NamedField> for SchemaFieldData {
name: Some(value.name().into()), name: Some(value.name().into()),
index: None, index: None,
description, description,
attributes, range,
type_id: value.type_id(), type_id: value.type_id(),
} }
} }
@ -1190,6 +1202,7 @@ impl From<&NamedField> for SchemaFieldData {
impl From<&VariantInfo> for SchemaFieldData { impl From<&VariantInfo> for SchemaFieldData {
fn from(value: &VariantInfo) -> Self { fn from(value: &VariantInfo) -> Self {
let range = value.custom_attributes().get_range_by_id(value.type_id());
#[cfg(feature = "documentation")] #[cfg(feature = "documentation")]
let description = value.docs().map(|s| Cow::Owned(s.to_owned())); let description = value.docs().map(|s| Cow::Owned(s.to_owned()));
#[cfg(not(feature = "documentation"))] #[cfg(not(feature = "documentation"))]
@ -1198,12 +1211,38 @@ impl From<&VariantInfo> for SchemaFieldData {
name: Some(value.name().to_owned().into()), name: Some(value.name().to_owned().into()),
index: None, index: None,
description, description,
attributes: value.custom_attributes().into(), range,
type_id: value.type_id(), type_id: value.type_id(),
} }
} }
} }
fn get_enum_information<'a>(iterator: Iter<'a, VariantInfo>) -> Vec<EnumVariantInfo> {
iterator
.map(|variant| {
let info = match variant {
VariantInfo::Struct(struct_variant_info) => {
SchemaEnumType::Fields(FieldsInformation {
fields: get_fields_information(struct_variant_info.iter()),
fields_type: FieldType::Named,
})
}
VariantInfo::Tuple(tuple_variant_info) => {
SchemaEnumType::Fields(FieldsInformation {
fields: get_fields_information(tuple_variant_info.iter()),
fields_type: FieldType::Unnamed,
})
}
VariantInfo::Unit(_) => SchemaEnumType::Const,
};
EnumVariantInfo {
info,
field_data: variant.into(),
}
})
.collect()
}
fn get_fields_information<'a, 'b, T>(iterator: Iter<'a, T>) -> Vec<SchemaFieldData> fn get_fields_information<'a, 'b, T>(iterator: Iter<'a, T>) -> Vec<SchemaFieldData>
where where
SchemaFieldData: From<&'a T>, SchemaFieldData: From<&'a T>,
@ -1218,36 +1257,29 @@ where
} }
pub(crate) fn variant_to_definition( pub(crate) fn variant_to_definition(
variant: &VariantInfo, variant: &EnumVariantInfo,
registry: &TypeRegistry, registry: &TypeRegistry,
) -> JsonSchemaBevyType { ) -> JsonSchemaBevyType {
let field_data: SchemaFieldData = variant.into();
let mut schema = JsonSchemaBevyType { let mut schema = JsonSchemaBevyType {
description: field_data.to_description(), description: variant.field_data.to_description(),
kind: Some(SchemaKind::Value), kind: Some(SchemaKind::Value),
schema_type: Some(SchemaTypeVariant::Single(SchemaType::Object)), schema_type: Some(SchemaTypeVariant::Single(SchemaType::Object)),
additional_properties: Some(JsonSchemaVariant::BoolValue(false)), additional_properties: Some(JsonSchemaVariant::BoolValue(false)),
..Default::default() ..Default::default()
}; };
let fields_info = match variant { let name = variant.field_data.name.as_ref().expect("").to_string();
VariantInfo::Unit(unit_variant_info) => { let fields_info = match &variant.info {
schema.const_value = Some(unit_variant_info.name().to_string().into()); SchemaEnumType::Const => {
schema.const_value = Some(name.into());
schema.schema_type = Some(SchemaTypeVariant::Single(SchemaType::String)); schema.schema_type = Some(SchemaTypeVariant::Single(SchemaType::String));
schema.additional_properties = None; schema.additional_properties = None;
return schema; return schema;
} }
VariantInfo::Struct(struct_variant_info) => FieldsInformation { SchemaEnumType::Fields(fields_information) => fields_information,
fields: get_fields_information(struct_variant_info.iter()),
fields_type: FieldType::Named,
},
VariantInfo::Tuple(tuple_variant_info) => FieldsInformation {
fields: get_fields_information(tuple_variant_info.iter()),
fields_type: FieldType::Unnamed,
},
}; };
let mut subschema = JsonSchemaBevyType::default(); let mut subschema = JsonSchemaBevyType::default();
registry.update_schema_with_fields_info(&mut subschema, &fields_info); registry.update_schema_with_fields_info(&mut subschema, fields_info);
schema.properties = [(variant.name().into(), subschema.into())].into(); schema.properties = [(name.into(), subschema.into())].into();
schema schema
} }
@ -1444,7 +1476,7 @@ impl TypeDefinitionBuilder for TypeRegistry {
InternalSchemaType::Optional { generic } => { InternalSchemaType::Optional { generic } => {
id = None; id = None;
let optional_schema = self let optional_schema = self
.build_schema_reference_for_type_id(generic.type_id(), None) .build_schema_reference_for_type_id(generic, None)
.unwrap_or_default(); .unwrap_or_default();
schema.ref_type = None; schema.ref_type = None;
@ -1529,16 +1561,10 @@ impl TypeDefinitionBuilder for TypeRegistry {
}); });
schema.kind = Some(data.schema_kind); schema.kind = Some(data.schema_kind);
} }
if let Some(field_range) = field_data if let Some(field_range) = field_data.as_ref().and_then(|d| d.range) {
.as_ref()
.and_then(|d| d.attributes.get_range_by_id(type_id))
{
range = range.with(field_range); range = range.with(field_range);
} }
if let Some(field_range) = p_field_data if let Some(field_range) = p_field_data.as_ref().and_then(|d| d.range) {
.as_ref()
.and_then(|d| d.attributes.get_range_by_id(type_id))
{
range = range.with(field_range); range = range.with(field_range);
} }
@ -1584,7 +1610,7 @@ impl TypeDefinitionBuilder for TypeRegistry {
} }
InternalSchemaType::Optional { generic } => { InternalSchemaType::Optional { generic } => {
let schema_optional = self let schema_optional = self
.build_schema_reference_for_type_id(generic.ty().id(), None) .build_schema_reference_for_type_id(generic, None)
.unwrap_or_default(); .unwrap_or_default();
schema.ref_type = None; schema.ref_type = None;
schema.one_of = vec![ schema.one_of = vec![
@ -1749,10 +1775,12 @@ pub(super) mod tests {
} }
let atr = AppTypeRegistry::default(); let atr = AppTypeRegistry::default();
{ {
use crate::schemas::RegisterReflectJsonSchemas;
let mut register = atr.write(); let mut register = atr.write();
register.register::<bevy_math::Vec3>(); register.register::<bevy_math::Vec3>();
register.register::<Foo>(); register.register::<Foo>();
register.register_type_data::<bevy_math::Vec3, ReflectJsonSchemaForceAsArray>(); register.register_force_as_array::<bevy_math::Vec3>();
} }
let type_registry = atr.read(); let type_registry = atr.read();
let (_, schema) = type_registry let (_, schema) = type_registry