Missing definitions handling
This commit is contained in:
parent
23ca435e73
commit
0ff3201c3c
@ -3,8 +3,7 @@
|
|||||||
use alloc::borrow::Cow;
|
use alloc::borrow::Cow;
|
||||||
use bevy_platform::collections::HashMap;
|
use bevy_platform::collections::HashMap;
|
||||||
use bevy_reflect::{
|
use bevy_reflect::{
|
||||||
prelude::ReflectDefault, serde::ReflectSerializer, GetTypeRegistration, Reflect,
|
prelude::ReflectDefault, serde::ReflectSerializer, GetTypeRegistration, Reflect, TypeRegistry,
|
||||||
TypeRegistration, TypeRegistry,
|
|
||||||
};
|
};
|
||||||
use core::any::TypeId;
|
use core::any::TypeId;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@ -44,7 +43,26 @@ impl TypeRegistrySchemaReader for TypeRegistry {
|
|||||||
extra_info: &SchemaTypesMetadata,
|
extra_info: &SchemaTypesMetadata,
|
||||||
) -> Option<JsonSchemaBevyType> {
|
) -> Option<JsonSchemaBevyType> {
|
||||||
let type_reg = self.get(type_id)?;
|
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.schema = Some(SchemaMarker.into());
|
||||||
schema.default_value = self.try_get_default_value_for_type_id(type_id);
|
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<Self, Self::Error> {
|
|
||||||
let (reg, metadata) = value;
|
|
||||||
// if let Some(s) = reg.data::<ReflectJsonSchema>() {
|
|
||||||
// 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<Self, Self::Error> {
|
|
||||||
(value, &SchemaTypesMetadata::default()).try_into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Identifies the JSON Schema version used in the schema.
|
/// Identifies the JSON Schema version used in the schema.
|
||||||
#[derive(Deserialize, Serialize, Debug, Reflect, PartialEq, Clone)]
|
#[derive(Deserialize, Serialize, Debug, Reflect, PartialEq, Clone)]
|
||||||
pub struct SchemaMarker;
|
pub struct SchemaMarker;
|
||||||
|
@ -1071,6 +1071,9 @@ pub struct SchemaDefinition {
|
|||||||
pub schema: JsonSchemaBevyType,
|
pub schema: JsonSchemaBevyType,
|
||||||
/// The properties of the schema.
|
/// The properties of the schema.
|
||||||
pub definitions: HashMap<TypeReferenceId, SchemaTypeInfo>,
|
pub definitions: HashMap<TypeReferenceId, SchemaTypeInfo>,
|
||||||
|
/// Missing definitions of the schema.
|
||||||
|
/// Could be the case for the types that are stored as generic arguments.
|
||||||
|
pub missing_definitions: Vec<TypeId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<SchemaDefinition> for JsonSchemaBevyType {
|
impl From<SchemaDefinition> for JsonSchemaBevyType {
|
||||||
@ -1237,11 +1240,13 @@ impl SchemaTypeInfo {
|
|||||||
pub fn to_definition(&self) -> SchemaDefinition {
|
pub fn to_definition(&self) -> SchemaDefinition {
|
||||||
let mut id: Option<TypeReferenceId> = self.ty_info.try_get_type_reference_id();
|
let mut id: Option<TypeReferenceId> = self.ty_info.try_get_type_reference_id();
|
||||||
let mut definitions: HashMap<TypeReferenceId, SchemaTypeInfo> = HashMap::new();
|
let mut definitions: HashMap<TypeReferenceId, SchemaTypeInfo> = HashMap::new();
|
||||||
|
let mut missing_definitions: Vec<TypeId> = Vec::new();
|
||||||
if let Some(custom_schema) = &self.ty_info.try_get_custom_schema() {
|
if let Some(custom_schema) = &self.ty_info.try_get_custom_schema() {
|
||||||
return SchemaDefinition {
|
return SchemaDefinition {
|
||||||
id,
|
id,
|
||||||
schema: custom_schema.0.clone(),
|
schema: custom_schema.0.clone(),
|
||||||
definitions,
|
definitions,
|
||||||
|
missing_definitions,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
let range = self.get_range();
|
let range = self.get_range();
|
||||||
@ -1298,7 +1303,9 @@ impl SchemaTypeInfo {
|
|||||||
id,
|
id,
|
||||||
schema: _,
|
schema: _,
|
||||||
definitions: field_definitions,
|
definitions: field_definitions,
|
||||||
|
missing_definitions: key_missing_definitions,
|
||||||
} = key.to_definition();
|
} = key.to_definition();
|
||||||
|
missing_definitions.extend(key_missing_definitions);
|
||||||
if let Some(id) = id {
|
if let Some(id) = id {
|
||||||
definitions.insert(id, key.clone());
|
definitions.insert(id, key.clone());
|
||||||
definitions.extend(field_definitions);
|
definitions.extend(field_definitions);
|
||||||
@ -1310,7 +1317,10 @@ impl SchemaTypeInfo {
|
|||||||
id,
|
id,
|
||||||
schema: _,
|
schema: _,
|
||||||
definitions: field_definitions,
|
definitions: field_definitions,
|
||||||
|
missing_definitions: value_missing_definitions,
|
||||||
} = value.to_definition();
|
} = value.to_definition();
|
||||||
|
|
||||||
|
missing_definitions.extend(value_missing_definitions);
|
||||||
if let Some(id) = id {
|
if let Some(id) = id {
|
||||||
definitions.insert(id, value.clone());
|
definitions.insert(id, value.clone());
|
||||||
definitions.extend(field_definitions);
|
definitions.extend(field_definitions);
|
||||||
@ -1405,6 +1415,7 @@ impl SchemaTypeInfo {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
definitions: HashMap::new(),
|
definitions: HashMap::new(),
|
||||||
|
missing_definitions,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1430,7 +1441,9 @@ impl SchemaTypeInfo {
|
|||||||
id,
|
id,
|
||||||
schema: _,
|
schema: _,
|
||||||
definitions: field_definitions,
|
definitions: field_definitions,
|
||||||
|
missing_definitions: field_missing_definitions,
|
||||||
} = field_schema.to_definition();
|
} = field_schema.to_definition();
|
||||||
|
missing_definitions.extend(field_missing_definitions);
|
||||||
definitions.extend(field_definitions);
|
definitions.extend(field_definitions);
|
||||||
let Some(id) = id else { continue };
|
let Some(id) = id else { continue };
|
||||||
if !definitions.contains_key(&id) {
|
if !definitions.contains_key(&id) {
|
||||||
@ -1450,7 +1463,9 @@ impl SchemaTypeInfo {
|
|||||||
id,
|
id,
|
||||||
schema: new_schema_type,
|
schema: new_schema_type,
|
||||||
definitions: field_definitions,
|
definitions: field_definitions,
|
||||||
|
missing_definitions: field_missing_definitions,
|
||||||
} = field_schema.to_definition();
|
} = field_schema.to_definition();
|
||||||
|
missing_definitions.extend(field_missing_definitions);
|
||||||
definitions.extend(field_definitions);
|
definitions.extend(field_definitions);
|
||||||
if let Some(id) = id {
|
if let Some(id) = id {
|
||||||
definitions.insert(id.clone(), field_schema);
|
definitions.insert(id.clone(), field_schema);
|
||||||
@ -1519,8 +1534,10 @@ impl SchemaTypeInfo {
|
|||||||
id,
|
id,
|
||||||
schema: _,
|
schema: _,
|
||||||
definitions: field_definitions,
|
definitions: field_definitions,
|
||||||
|
missing_definitions: field_missing_definitions,
|
||||||
} = field_schema.to_definition();
|
} = field_schema.to_definition();
|
||||||
definitions.extend(field_definitions);
|
definitions.extend(field_definitions);
|
||||||
|
missing_definitions.extend(field_missing_definitions);
|
||||||
let Some(id) = id else { continue };
|
let Some(id) = id else { continue };
|
||||||
if !definitions.contains_key(&id) {
|
if !definitions.contains_key(&id) {
|
||||||
definitions.insert(id, field_schema);
|
definitions.insert(id, field_schema);
|
||||||
@ -1546,7 +1563,9 @@ impl SchemaTypeInfo {
|
|||||||
id,
|
id,
|
||||||
schema: _,
|
schema: _,
|
||||||
definitions: field_definitions,
|
definitions: field_definitions,
|
||||||
|
missing_definitions: field_missing_definitions,
|
||||||
} = items_schema.to_definition();
|
} = items_schema.to_definition();
|
||||||
|
missing_definitions.extend(field_missing_definitions);
|
||||||
definitions.extend(field_definitions);
|
definitions.extend(field_definitions);
|
||||||
if let Some(id) = id {
|
if let Some(id) = id {
|
||||||
definitions.insert(id, items_schema);
|
definitions.insert(id, items_schema);
|
||||||
@ -1561,7 +1580,8 @@ impl SchemaTypeInfo {
|
|||||||
ty_info: TypeInformation::Type(Box::new(*generic.ty())),
|
ty_info: TypeInformation::Type(Box::new(*generic.ty())),
|
||||||
..(**schema_type_info).clone()
|
..(**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);
|
definitions.extend(definition.definitions);
|
||||||
schema.ref_type = None;
|
schema.ref_type = None;
|
||||||
schema.schema_type = None;
|
schema.schema_type = None;
|
||||||
@ -1570,7 +1590,7 @@ impl SchemaTypeInfo {
|
|||||||
schema_type: Some(SchemaTypeVariant::Single(SchemaType::Null)),
|
schema_type: Some(SchemaTypeVariant::Single(SchemaType::Null)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
Box::new(definition.schema),
|
Box::new(schema_optional.to_ref_schema()),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1578,6 +1598,7 @@ impl SchemaTypeInfo {
|
|||||||
id,
|
id,
|
||||||
schema,
|
schema,
|
||||||
definitions,
|
definitions,
|
||||||
|
missing_definitions,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1813,10 +1834,12 @@ where
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(super) mod tests {
|
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_platform::collections::HashMap;
|
||||||
use bevy_reflect::GetTypeRegistration;
|
use bevy_reflect::GetTypeRegistration;
|
||||||
|
|
||||||
|
use crate::schemas::json_schema::TypeRegistrySchemaReader;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
/// Validate a JSON schema against a set of valid and invalid instances.
|
/// 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}),
|
serde_json::json!({"a": 5555,"b": 5555}),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
let atr = bevy_ecs::reflect::AppTypeRegistry::default();
|
let atr = AppTypeRegistry::default();
|
||||||
{
|
{
|
||||||
let mut register = atr.write();
|
let mut register = atr.write();
|
||||||
register.register::<bevy_math::Vec3>();
|
register.register::<bevy_math::Vec3>();
|
||||||
@ -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::<ArrayComponent>();
|
||||||
|
register.register::<Option<ArrayComponent>>();
|
||||||
|
}
|
||||||
|
let type_registry = atr.read();
|
||||||
|
let schema = type_registry
|
||||||
|
.export_type_json_schema::<Option<ArrayComponent>>(&Default::default())
|
||||||
|
.expect("Failed to export type JSON schema");
|
||||||
|
validate::<Option<ArrayComponent>>(
|
||||||
|
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]
|
#[test]
|
||||||
fn reflect_struct_with_array() {
|
fn reflect_struct_with_array() {
|
||||||
#[derive(Reflect, Default, Deserialize, Serialize)]
|
#[derive(Reflect, Default, Deserialize, Serialize)]
|
||||||
|
Loading…
Reference in New Issue
Block a user