Start populating schema definitons field
This commit is contained in:
parent
27366c9f4f
commit
5ea21b4705
@ -11,7 +11,7 @@ use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::schemas::{
|
||||
reflect_info::{SchemaNumber, TypeReferenceId, TypeReferencePath},
|
||||
reflect_info::{SchemaNumber, TypeInformation, TypeReferenceId, TypeReferencePath},
|
||||
ReflectJsonSchema, SchemaTypesMetadata,
|
||||
};
|
||||
|
||||
@ -82,13 +82,12 @@ impl TryFrom<(&TypeRegistration, &SchemaTypesMetadata)> for JsonSchemaBevyType {
|
||||
if let Some(s) = reg.data::<ReflectJsonSchema>() {
|
||||
return Ok(s.0.clone());
|
||||
}
|
||||
let base_schema = super::reflect_info::build_schema(reg);
|
||||
let (_, mut typed_schema) = TypeInformation::from(reg)
|
||||
.to_schema_type_info()
|
||||
.to_definition();
|
||||
|
||||
let JsonSchemaVariant::Schema(mut typed_schema) = base_schema else {
|
||||
return Err(InvalidJsonSchema::InvalidType);
|
||||
};
|
||||
typed_schema.reflect_type_data = metadata.get_registered_reflect_types(reg);
|
||||
Ok(*typed_schema)
|
||||
Ok(typed_schema)
|
||||
}
|
||||
}
|
||||
|
||||
@ -142,7 +141,8 @@ pub struct JsonSchemaBevyType {
|
||||
#[serde(skip_serializing_if = "Vec::is_empty", default)]
|
||||
pub reflect_type_data: Vec<Cow<'static, str>>,
|
||||
/// Bevy specific field, [`TypeInfo`] type mapping.
|
||||
pub kind: SchemaKind,
|
||||
#[serde(skip_serializing_if = "Option::is_none", default)]
|
||||
pub kind: Option<SchemaKind>,
|
||||
/// JSON Schema specific field.
|
||||
/// This keyword is used to reference a constant value.
|
||||
#[serde(rename = "const")]
|
||||
@ -245,7 +245,7 @@ pub struct JsonSchemaBevyType {
|
||||
#[serde(skip_serializing_if = "HashMap::is_empty", default)]
|
||||
#[reflect(ignore)]
|
||||
#[serde(rename = "$defs")]
|
||||
pub definitions: HashMap<TypeReferenceId, Box<JsonSchemaVariant>>,
|
||||
pub definitions: HashMap<TypeReferenceId, JsonSchemaVariant>,
|
||||
}
|
||||
|
||||
/// Represents different types of JSON Schema values that can be used in schema definitions.
|
||||
@ -267,6 +267,12 @@ pub enum JsonSchemaVariant {
|
||||
Schema(#[reflect(ignore)] Box<JsonSchemaBevyType>),
|
||||
}
|
||||
|
||||
impl From<JsonSchemaBevyType> for JsonSchemaVariant {
|
||||
fn from(value: JsonSchemaBevyType) -> Self {
|
||||
JsonSchemaVariant::Schema(Box::new(value))
|
||||
}
|
||||
}
|
||||
|
||||
/// Kind of json schema, maps [`TypeInfo`] type
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default, Reflect)]
|
||||
pub enum SchemaKind {
|
||||
@ -372,9 +378,7 @@ impl From<TypeId> for SchemaType {
|
||||
|| value.eq(&TypeId::of::<u64>())
|
||||
|| value.eq(&TypeId::of::<u128>())
|
||||
|| value.eq(&TypeId::of::<usize>())
|
||||
{
|
||||
Self::Integer
|
||||
} else if value.eq(&TypeId::of::<i8>())
|
||||
|| value.eq(&TypeId::of::<i8>())
|
||||
|| value.eq(&TypeId::of::<i16>())
|
||||
|| value.eq(&TypeId::of::<i32>())
|
||||
|| value.eq(&TypeId::of::<i64>())
|
||||
@ -679,12 +683,9 @@ mod tests {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"a": {
|
||||
"kind": "Value",
|
||||
"maximum": 65535,
|
||||
"minimum": 0,
|
||||
"type": "integer",
|
||||
"shortPath": "u16",
|
||||
"typePath": "u16",
|
||||
"type": "integer"
|
||||
},
|
||||
},
|
||||
"required": [
|
||||
|
@ -99,6 +99,12 @@ impl From<&Type> for TypeReferenceId {
|
||||
TypeReferenceId(t.path().replace("::", "-").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())
|
||||
@ -126,14 +132,18 @@ pub enum FieldType {
|
||||
|
||||
/// Information about the attributes of a field.
|
||||
#[derive(Clone, Debug, Deref, DerefMut, Default)]
|
||||
pub struct FieldsInformation(#[deref] pub Vec<FieldInformation>, FieldType);
|
||||
pub struct FieldsInformation {
|
||||
/// Fields information.
|
||||
#[deref]
|
||||
fields: Vec<FieldInformation>,
|
||||
/// Field type information.
|
||||
fields_type: FieldType,
|
||||
}
|
||||
|
||||
impl From<&TypeInformation> for Option<FieldsInformation> {
|
||||
fn from(value: &TypeInformation) -> Self {
|
||||
let Some(info) = value.try_get_type_info() else {
|
||||
return None;
|
||||
};
|
||||
let (array, field_type) = match info {
|
||||
let info = value.try_get_type_info()?;
|
||||
let (fields, fields_type) = match info {
|
||||
TypeInfo::Struct(struct_info) => (
|
||||
get_fields_information(struct_info.iter()),
|
||||
if value.is_forced_as_array() {
|
||||
@ -150,14 +160,17 @@ impl From<&TypeInformation> for Option<FieldsInformation> {
|
||||
get_fields_information(tuple_info.iter()),
|
||||
FieldType::Unnamed,
|
||||
),
|
||||
TypeInfo::List(_) => return None,
|
||||
TypeInfo::Array(_) => return None,
|
||||
TypeInfo::Map(_) => return None,
|
||||
TypeInfo::Set(_) => return None,
|
||||
TypeInfo::Enum(_) => return None,
|
||||
TypeInfo::Opaque(_) => return None,
|
||||
TypeInfo::List(_)
|
||||
| TypeInfo::Array(_)
|
||||
| TypeInfo::Map(_)
|
||||
| TypeInfo::Set(_)
|
||||
| TypeInfo::Enum(_)
|
||||
| TypeInfo::Opaque(_) => return None,
|
||||
};
|
||||
Some(FieldsInformation(array, field_type))
|
||||
Some(FieldsInformation {
|
||||
fields,
|
||||
fields_type,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -225,6 +238,32 @@ pub enum TypeInformation {
|
||||
}
|
||||
|
||||
impl TypeInformation {
|
||||
/// Returns the reference type if the stored type is a reference one.
|
||||
pub fn get_reference_type(&self) -> Option<TypeReferencePath> {
|
||||
if self.is_primitive_type() {
|
||||
None
|
||||
} else {
|
||||
self.try_get_type_path_table()
|
||||
.map(TypeReferencePath::definition)
|
||||
}
|
||||
}
|
||||
/// Returns true if the stored type is a primitive one.
|
||||
pub fn is_primitive_type(&self) -> bool {
|
||||
self.try_get_primitive_type().is_some()
|
||||
}
|
||||
/// Returns the primitive type if the stored type is a primitive one.
|
||||
pub fn try_get_primitive_type(&self) -> Option<SchemaType> {
|
||||
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 stored_fields = (&self).into();
|
||||
SchemaTypeInfo {
|
||||
ty_info: self,
|
||||
field_data: None,
|
||||
stored_fields,
|
||||
}
|
||||
}
|
||||
/// Returns the documentation of the type.
|
||||
#[cfg(feature = "documentation")]
|
||||
pub fn get_docs(&self) -> Option<Cow<'static, str>> {
|
||||
@ -234,8 +273,7 @@ impl TypeInformation {
|
||||
}
|
||||
TypeInformation::TypeInfo(type_info) => type_info.docs(),
|
||||
TypeInformation::VariantInfo(variant_info) => variant_info.docs(),
|
||||
TypeInformation::Type(_) => None,
|
||||
TypeInformation::TypeId(_) => None,
|
||||
_ => None,
|
||||
};
|
||||
|
||||
docs.map(|docs| docs.trim().replace("\n", "").into())
|
||||
@ -256,15 +294,23 @@ impl TypeInformation {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the type of the type.
|
||||
pub fn try_get_type(&self) -> Option<&Type> {
|
||||
match self {
|
||||
TypeInformation::TypeInfo(type_info) => Some(type_info.ty()),
|
||||
TypeInformation::TypeRegistration(reg) => Some(reg.type_info().ty()),
|
||||
TypeInformation::Type(t) => Some(&**t),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether the type is forced as an array.
|
||||
pub fn is_forced_as_array(&self) -> bool {
|
||||
match self {
|
||||
TypeInformation::Type(_) => false,
|
||||
TypeInformation::TypeId(_) | TypeInformation::VariantInfo(_) => false,
|
||||
TypeInformation::TypeInfo(_) => false,
|
||||
TypeInformation::TypeRegistration(type_registration) => type_registration
|
||||
.data::<ReflectJsonSchemaForceAsArray>()
|
||||
.is_some(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
@ -321,9 +367,7 @@ impl TypeInformation {
|
||||
}
|
||||
/// Try to get the optional type information from the enum information.
|
||||
pub fn try_get_optional_from_info(enum_info: &EnumInfo) -> Option<&GenericInfo> {
|
||||
let Some(generic) = enum_info.generics().first() else {
|
||||
return None;
|
||||
};
|
||||
let generic = enum_info.generics().first()?;
|
||||
if enum_info.variant_len() != 2
|
||||
|| !enum_info.contains_variant("Some")
|
||||
|| !enum_info.contains_variant("None")
|
||||
@ -340,6 +384,7 @@ impl Default for TypeInformation {
|
||||
Self::TypeId(TypeId::of::<()>())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&TypeInformation> for SchemaKind {
|
||||
fn from(value: &TypeInformation) -> Self {
|
||||
let type_info = value.try_get_type_info();
|
||||
@ -374,11 +419,6 @@ impl From<&TypeInformation> for SchemaKind {
|
||||
}
|
||||
}
|
||||
}
|
||||
impl From<&TypePathTable> for TypeReferenceId {
|
||||
fn from(value: &TypePathTable) -> Self {
|
||||
value.path().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&TypeInformation> for TypeReferenceId {
|
||||
type Error = ();
|
||||
@ -803,10 +843,8 @@ pub enum InternalSchemaType {
|
||||
EnumHolder(Vec<VariantInfo>),
|
||||
/// Represents a single enum variant.
|
||||
EnumVariant(VariantInfo),
|
||||
/// Holds named fields for struct types.
|
||||
NamedFieldsHolder(FieldsInformation),
|
||||
/// Holds unnamed fields for tuple and tuple struct types.
|
||||
UnnamedFieldsHolder(FieldsInformation),
|
||||
/// Holds named fields for struct, tuple, and tuple struct types.
|
||||
FieldsHolder(FieldsInformation),
|
||||
/// Represents an Optional type (e.g., Option<T>).
|
||||
Optional {
|
||||
/// Generic information about the wrapped type T in Option<T>.
|
||||
@ -834,24 +872,26 @@ impl From<&TypeInformation> for InternalSchemaType {
|
||||
if let Some(type_info) = value.try_get_type_info() {
|
||||
match type_info {
|
||||
TypeInfo::Struct(struct_info) => {
|
||||
if value.is_forced_as_array() {
|
||||
InternalSchemaType::UnnamedFieldsHolder(FieldsInformation(
|
||||
get_fields_information(struct_info.iter()),
|
||||
FieldType::Unnamed,
|
||||
))
|
||||
let fields_type = if value.is_forced_as_array() {
|
||||
FieldType::Unnamed
|
||||
} else {
|
||||
InternalSchemaType::NamedFieldsHolder(FieldsInformation(
|
||||
get_fields_information(struct_info.iter()),
|
||||
FieldType::Named,
|
||||
))
|
||||
}
|
||||
FieldType::Named
|
||||
};
|
||||
InternalSchemaType::FieldsHolder(FieldsInformation {
|
||||
fields: get_fields_information(struct_info.iter()),
|
||||
fields_type,
|
||||
})
|
||||
}
|
||||
TypeInfo::TupleStruct(info) => InternalSchemaType::UnnamedFieldsHolder(
|
||||
FieldsInformation(get_fields_information(info.iter()), FieldType::Unnamed),
|
||||
),
|
||||
TypeInfo::Tuple(info) => InternalSchemaType::UnnamedFieldsHolder(
|
||||
FieldsInformation(get_fields_information(info.iter()), FieldType::Unnamed),
|
||||
),
|
||||
TypeInfo::TupleStruct(info) => {
|
||||
InternalSchemaType::FieldsHolder(FieldsInformation {
|
||||
fields: get_fields_information(info.iter()),
|
||||
fields_type: FieldType::Unnamed,
|
||||
})
|
||||
}
|
||||
TypeInfo::Tuple(info) => InternalSchemaType::FieldsHolder(FieldsInformation {
|
||||
fields: get_fields_information(info.iter()),
|
||||
fields_type: FieldType::Unnamed,
|
||||
}),
|
||||
TypeInfo::Enum(enum_info) => {
|
||||
match TypeInformation::try_get_optional_from_info(enum_info) {
|
||||
Some(e) => InternalSchemaType::Optional {
|
||||
@ -898,12 +938,10 @@ impl From<&TypeInformation> for InternalSchemaType {
|
||||
impl From<&SchemaTypeInfo> for InternalSchemaType {
|
||||
fn from(value: &SchemaTypeInfo) -> Self {
|
||||
if let Some(s) = &value.stored_fields {
|
||||
if s.1 == FieldType::Named {
|
||||
return InternalSchemaType::NamedFieldsHolder(s.clone());
|
||||
}
|
||||
return InternalSchemaType::UnnamedFieldsHolder(s.clone());
|
||||
InternalSchemaType::FieldsHolder(s.clone())
|
||||
} else {
|
||||
(&value.ty_info).into()
|
||||
}
|
||||
(&value.ty_info).into()
|
||||
}
|
||||
}
|
||||
|
||||
@ -911,7 +949,6 @@ impl From<&InternalSchemaType> for Option<SchemaTypeVariant> {
|
||||
fn from(value: &InternalSchemaType) -> Self {
|
||||
match value {
|
||||
InternalSchemaType::Array { .. } => Some(SchemaTypeVariant::Single(SchemaType::Array)),
|
||||
InternalSchemaType::EnumHolder(_) => None,
|
||||
InternalSchemaType::EnumVariant(variant) => match variant {
|
||||
VariantInfo::Tuple(t) => {
|
||||
if t.field_len() == 1 {
|
||||
@ -926,15 +963,16 @@ impl From<&InternalSchemaType> for Option<SchemaTypeVariant> {
|
||||
VariantInfo::Struct(_) => Some(SchemaTypeVariant::Single(SchemaType::Object)),
|
||||
VariantInfo::Unit(_) => Some(SchemaTypeVariant::Single(SchemaType::String)),
|
||||
},
|
||||
InternalSchemaType::NamedFieldsHolder { .. } => {
|
||||
Some(SchemaTypeVariant::Single(SchemaType::Object))
|
||||
}
|
||||
InternalSchemaType::UnnamedFieldsHolder(unnamed_fields) => {
|
||||
if unnamed_fields.len() == 1 {
|
||||
let schema: InternalSchemaType = (&unnamed_fields[0].type_info).into();
|
||||
(&schema).into()
|
||||
InternalSchemaType::FieldsHolder(fields) => {
|
||||
if fields.fields_type == FieldType::Unnamed {
|
||||
if fields.fields.len() == 1 {
|
||||
let schema: InternalSchemaType = (&fields.fields[0].type_info).into();
|
||||
(&schema).into()
|
||||
} else {
|
||||
Some(SchemaTypeVariant::Single(SchemaType::Array))
|
||||
}
|
||||
} else {
|
||||
Some(SchemaTypeVariant::Single(SchemaType::Array))
|
||||
Some(SchemaTypeVariant::Single(SchemaType::Object))
|
||||
}
|
||||
}
|
||||
InternalSchemaType::Optional {
|
||||
@ -954,7 +992,7 @@ impl From<&InternalSchemaType> for Option<SchemaTypeVariant> {
|
||||
Some(SchemaTypeVariant::Single((*type_id).into()))
|
||||
}
|
||||
InternalSchemaType::RegularType(ty) => Some(SchemaTypeVariant::Single(ty.id().into())),
|
||||
InternalSchemaType::NoInfo => None,
|
||||
InternalSchemaType::NoInfo | InternalSchemaType::EnumHolder(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1024,26 +1062,28 @@ impl SchemaTypeInfo {
|
||||
/// Converts the schema type information into a JSON schema reference.
|
||||
pub fn to_ref_schema(&self) -> JsonSchemaBevyType {
|
||||
let range = self.get_range();
|
||||
let description = self.get_docs();
|
||||
let (ref_type, schema_type) = (self.ty_info.get_reference_type(), self.into());
|
||||
JsonSchemaBevyType {
|
||||
description: self.get_docs(),
|
||||
description,
|
||||
minimum: range.min.get_inclusive(),
|
||||
maximum: range.max.get_inclusive(),
|
||||
exclusive_minimum: range.min.get_exclusive(),
|
||||
exclusive_maximum: range.max.get_exclusive(),
|
||||
ref_type: self
|
||||
.ty_info
|
||||
.try_get_type_path_table()
|
||||
.map(TypeReferencePath::definition),
|
||||
ref_type,
|
||||
kind: None,
|
||||
schema_type,
|
||||
..default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SchemaTypeInfo> for JsonSchemaVariant {
|
||||
fn from(val: SchemaTypeInfo) -> Self {
|
||||
let schema_type: Option<SchemaTypeVariant> = (&val).into();
|
||||
/// Converts the schema type information into a JSON schema definition.
|
||||
pub fn to_definition(&self) -> (Option<TypeReferenceId>, JsonSchemaBevyType) {
|
||||
let id: Option<TypeReferenceId> = self.ty_info.try_get_type_path_table().map(Into::into);
|
||||
let range = self.ty_info.get_range();
|
||||
|
||||
let (type_path, short_path, crate_name, module_path) =
|
||||
if let Some(type_path_table) = val.ty_info.try_get_type_path_table() {
|
||||
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(),
|
||||
@ -1053,24 +1093,21 @@ impl From<SchemaTypeInfo> for JsonSchemaVariant {
|
||||
} else {
|
||||
(Cow::default(), Cow::default(), None, None)
|
||||
};
|
||||
let kind: SchemaKind = (&val.ty_info).into();
|
||||
let range = val.get_range();
|
||||
let mut schema = JsonSchemaBevyType {
|
||||
schema_type: schema_type.clone(),
|
||||
kind,
|
||||
description: val.get_docs(),
|
||||
description: self.ty_info.get_docs(),
|
||||
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(),
|
||||
..Default::default()
|
||||
schema_type: self.into(),
|
||||
..default()
|
||||
};
|
||||
let internal_type: InternalSchemaType = (&val).into();
|
||||
|
||||
let internal_type: InternalSchemaType = (self).into();
|
||||
match internal_type {
|
||||
InternalSchemaType::Map { key, value } => {
|
||||
let key: SchemaTypeInfo = SchemaTypeInfo {
|
||||
@ -1083,9 +1120,9 @@ impl From<SchemaTypeInfo> for JsonSchemaVariant {
|
||||
field_data: None,
|
||||
stored_fields: None,
|
||||
};
|
||||
schema.additional_properties = Some(key.clone().into());
|
||||
schema.value_type = Some(value.into());
|
||||
schema.key_type = Some(key.into());
|
||||
schema.additional_properties = Some(key.clone().to_definition().1.into());
|
||||
schema.value_type = Some(value.to_definition().1.into());
|
||||
schema.key_type = Some(key.to_definition().1.into());
|
||||
}
|
||||
InternalSchemaType::Regular(_)
|
||||
| InternalSchemaType::RegularType(_)
|
||||
@ -1094,23 +1131,29 @@ impl From<SchemaTypeInfo> for JsonSchemaVariant {
|
||||
schema.one_of = variants.iter().map(build_schema).collect();
|
||||
}
|
||||
InternalSchemaType::EnumVariant(variant_info) => {
|
||||
schema.kind = SchemaKind::Value;
|
||||
schema.kind = Some(SchemaKind::Value);
|
||||
schema.schema_type = Some(SchemaTypeVariant::Single(SchemaType::Object));
|
||||
let ty_info: TypeInformation = (&variant_info).into();
|
||||
let field_data: Option<SchemaFieldData> = Some((&variant_info).into());
|
||||
|
||||
match &variant_info {
|
||||
VariantInfo::Struct(struct_variant_info) => {
|
||||
let stored_fields = get_fields_information(struct_variant_info.iter());
|
||||
let fields = get_fields_information(struct_variant_info.iter());
|
||||
|
||||
let schema_field = SchemaTypeInfo {
|
||||
ty_info,
|
||||
field_data,
|
||||
stored_fields: Some(FieldsInformation(stored_fields, FieldType::Named)),
|
||||
stored_fields: Some(FieldsInformation {
|
||||
fields,
|
||||
fields_type: FieldType::Named,
|
||||
}),
|
||||
};
|
||||
|
||||
schema.properties =
|
||||
[(variant_info.name().into(), schema_field.into())].into();
|
||||
schema.properties = [(
|
||||
variant_info.name().into(),
|
||||
schema_field.to_definition().1.into(),
|
||||
)]
|
||||
.into();
|
||||
schema.required = vec![variant_info.name().into()];
|
||||
}
|
||||
VariantInfo::Tuple(tuple_variant_info) => {
|
||||
@ -1118,13 +1161,18 @@ impl From<SchemaTypeInfo> for JsonSchemaVariant {
|
||||
let schema_field = SchemaTypeInfo {
|
||||
ty_info,
|
||||
field_data: None,
|
||||
stored_fields: Some(FieldsInformation(
|
||||
stored_fields,
|
||||
FieldType::Unnamed,
|
||||
)),
|
||||
stored_fields: Some(FieldsInformation {
|
||||
fields: stored_fields,
|
||||
|
||||
fields_type: FieldType::Unnamed,
|
||||
}),
|
||||
};
|
||||
schema.properties =
|
||||
[(variant_info.name().into(), schema_field.into())].into();
|
||||
|
||||
schema.properties = [(
|
||||
variant_info.name().into(),
|
||||
schema_field.to_definition().1.into(),
|
||||
)]
|
||||
.into();
|
||||
schema.required = vec![variant_info.name().into()];
|
||||
}
|
||||
VariantInfo::Unit(unit_variant_info) => {
|
||||
@ -1133,48 +1181,66 @@ impl From<SchemaTypeInfo> for JsonSchemaVariant {
|
||||
field_data,
|
||||
stored_fields: None,
|
||||
};
|
||||
return JsonSchemaVariant::Schema(Box::new(JsonSchemaBevyType {
|
||||
const_value: Some(unit_variant_info.name().to_string().into()),
|
||||
schema_type: Some(SchemaTypeVariant::Single(SchemaType::String)),
|
||||
description: schema_field.get_docs(),
|
||||
kind: SchemaKind::Value,
|
||||
..Default::default()
|
||||
}));
|
||||
return (
|
||||
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::NamedFieldsHolder(named_fields) => {
|
||||
schema.additional_properties = Some(JsonSchemaVariant::BoolValue(false));
|
||||
schema.schema_type = Some(SchemaTypeVariant::Single(SchemaType::Object));
|
||||
|
||||
schema.properties = named_fields
|
||||
.iter()
|
||||
.map(|field| (field.field.to_name(), SchemaTypeInfo::from(field).into()))
|
||||
.collect();
|
||||
schema.required = named_fields
|
||||
.iter()
|
||||
.map(|field| field.field.to_name())
|
||||
.collect();
|
||||
}
|
||||
InternalSchemaType::UnnamedFieldsHolder(unnamed_fields) => {
|
||||
if unnamed_fields.len() == 1 {
|
||||
let new_schema = SchemaTypeInfo::from(&unnamed_fields[0]).into();
|
||||
if let JsonSchemaVariant::Schema(new_schema_type) = new_schema {
|
||||
schema = *new_schema_type;
|
||||
schema.schema_type = schema_type.clone();
|
||||
schema.description = val.get_docs();
|
||||
} else {
|
||||
return new_schema;
|
||||
}
|
||||
} else {
|
||||
schema.prefix_items = unnamed_fields
|
||||
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| SchemaTypeInfo::from(field).into())
|
||||
.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;
|
||||
}
|
||||
let (id, definition) = field_schema.to_definition();
|
||||
let Some(id) = id else { continue };
|
||||
if !schema.definitions.contains_key(&id) {
|
||||
schema.definitions.insert(id, definition.into());
|
||||
}
|
||||
}
|
||||
schema.required = fields
|
||||
.fields
|
||||
.iter()
|
||||
.map(|field| field.field.to_name())
|
||||
.collect();
|
||||
schema.min_items = Some(unnamed_fields.len() as u64);
|
||||
schema.max_items = Some(unnamed_fields.len() as u64);
|
||||
}
|
||||
}
|
||||
FieldType::Unnamed => {
|
||||
if fields.fields.len() == 1 {
|
||||
let (_, new_schema_type) =
|
||||
SchemaTypeInfo::from(&fields.fields[0]).to_definition();
|
||||
schema = new_schema_type;
|
||||
schema.schema_type = self.into();
|
||||
schema.description = self.get_docs();
|
||||
} else {
|
||||
schema.prefix_items = fields
|
||||
.fields
|
||||
.iter()
|
||||
.map(|field| SchemaTypeInfo::from(field).to_definition().1.into())
|
||||
.collect();
|
||||
schema.min_items = Some(fields.fields.len() as u64);
|
||||
schema.max_items = Some(fields.fields.len() as u64);
|
||||
}
|
||||
}
|
||||
},
|
||||
InternalSchemaType::Array {
|
||||
element_ty,
|
||||
min_size,
|
||||
@ -1185,7 +1251,7 @@ impl From<SchemaTypeInfo> for JsonSchemaVariant {
|
||||
field_data: None,
|
||||
stored_fields: None,
|
||||
};
|
||||
schema.items = Some(items_schema.into());
|
||||
schema.items = Some(items_schema.to_definition().1.into());
|
||||
schema.min_items = min_size;
|
||||
schema.max_items = max_size;
|
||||
}
|
||||
@ -1193,23 +1259,22 @@ impl From<SchemaTypeInfo> for JsonSchemaVariant {
|
||||
generic: _,
|
||||
ref schema_type_info,
|
||||
} => {
|
||||
let schema_variant: JsonSchemaVariant = schema_type_info.as_ref().clone().into();
|
||||
let schema_variant: JsonSchemaVariant =
|
||||
schema_type_info.as_ref().clone().to_definition().1.into();
|
||||
if let JsonSchemaVariant::Schema(value) = schema_variant {
|
||||
let range = val.get_range();
|
||||
let range = self.get_range();
|
||||
schema = *value;
|
||||
schema.schema_type = schema_type.clone();
|
||||
schema.schema_type = self.into();
|
||||
schema.minimum = range.min.get_inclusive();
|
||||
schema.maximum = range.max.get_inclusive();
|
||||
schema.exclusive_minimum = range.min.get_exclusive();
|
||||
schema.exclusive_maximum = range.max.get_exclusive();
|
||||
schema.description = val.get_docs();
|
||||
schema.kind = SchemaKind::Optional;
|
||||
} else {
|
||||
return schema_variant;
|
||||
schema.description = self.get_docs();
|
||||
schema.kind = Some(SchemaKind::Optional);
|
||||
}
|
||||
}
|
||||
}
|
||||
JsonSchemaVariant::Schema(Box::new(schema))
|
||||
(id, schema)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1302,38 +1367,28 @@ impl<'a, T> From<&'a T> for SchemaTypeInfo
|
||||
where
|
||||
T: 'static,
|
||||
TypeInformation: From<&'a T>,
|
||||
SchemaFieldData: TryFrom<&'a T>,
|
||||
SchemaFieldData: From<&'a T>,
|
||||
{
|
||||
fn from(value: &'a T) -> Self {
|
||||
let ty_info: TypeInformation = value.into();
|
||||
let field_data: Option<SchemaFieldData> = value.try_into().ok();
|
||||
let field_data: SchemaFieldData = value.into();
|
||||
let stored_fields = (&ty_info).into();
|
||||
SchemaTypeInfo {
|
||||
ty_info,
|
||||
field_data,
|
||||
field_data: Some(field_data),
|
||||
stored_fields,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds a [`SchemaTypeInfo`] from a value.
|
||||
pub fn build_schema_type_info<'a, T>(value: &'a T) -> SchemaTypeInfo
|
||||
where
|
||||
T: 'static,
|
||||
TypeInformation: From<&'a T>,
|
||||
SchemaFieldData: TryFrom<&'a T>,
|
||||
{
|
||||
value.into()
|
||||
}
|
||||
|
||||
/// Builds a JSON schema variant from a value.
|
||||
pub fn build_schema<'a, T>(value: &'a T) -> JsonSchemaVariant
|
||||
where
|
||||
T: 'static,
|
||||
TypeInformation: From<&'a T>,
|
||||
SchemaFieldData: TryFrom<&'a T>,
|
||||
SchemaTypeInfo: From<&'a T>,
|
||||
{
|
||||
build_schema_type_info(value).into()
|
||||
let schema: SchemaTypeInfo = value.into();
|
||||
schema.to_definition().1.into()
|
||||
}
|
||||
|
||||
impl From<&UnnamedField> for SchemaFieldData {
|
||||
@ -1411,20 +1466,6 @@ impl From<&TypeInfo> for TypeInformation {
|
||||
TypeInformation::TypeInfo(Box::new(value.clone()))
|
||||
}
|
||||
}
|
||||
impl TryFrom<&TypeInfo> for SchemaFieldData {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(_: &TypeInfo) -> Result<Self, Self::Error> {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
impl TryFrom<&TypeRegistration> for SchemaFieldData {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(_: &TypeRegistration) -> Result<Self, Self::Error> {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&TypeRegistration> for TypeInformation {
|
||||
fn from(value: &TypeRegistration) -> Self {
|
||||
@ -1437,15 +1478,6 @@ impl From<Type> for TypeInformation {
|
||||
TypeInformation::Type(Box::new(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&Type> for SchemaFieldData {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(_: &Type) -> Result<Self, Self::Error> {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&TypeId> for TypeInformation {
|
||||
fn from(value: &TypeId) -> Self {
|
||||
TypeInformation::TypeId(*value)
|
||||
@ -1457,14 +1489,6 @@ impl From<TypeId> for TypeInformation {
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&TypeId> for SchemaFieldData {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(_: &TypeId) -> Result<Self, Self::Error> {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
fn get_fields_information<'a, 'b, T>(iterator: Iter<'a, T>) -> Vec<FieldInformation>
|
||||
where
|
||||
SchemaFieldData: From<&'a T>,
|
||||
@ -1491,7 +1515,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn integer_test() {
|
||||
let type_info = build_schema_type_info(&TypeId::of::<u16>());
|
||||
let type_info = TypeInformation::from(&TypeId::of::<u16>()).to_schema_type_info();
|
||||
let schema_type: Option<SchemaTypeVariant> = (&type_info).into();
|
||||
assert_eq!(
|
||||
type_info.get_range().min,
|
||||
@ -1520,7 +1544,7 @@ mod tests {
|
||||
.as_struct()
|
||||
.expect("Should not fail");
|
||||
let field_info = struct_info.field("no_value").expect("Should not fail");
|
||||
let type_info = build_schema_type_info(field_info);
|
||||
let type_info = SchemaTypeInfo::from(field_info);
|
||||
let schema_type: Option<SchemaTypeVariant> = (&type_info).into();
|
||||
let range = type_info.get_range();
|
||||
assert_eq!(range.min, Some(BoundValue::Inclusive(10.into())));
|
||||
@ -1545,7 +1569,7 @@ mod tests {
|
||||
.as_struct()
|
||||
.expect("Should not fail");
|
||||
let field_info = struct_info.field("no_value").expect("Should not fail");
|
||||
let type_info = build_schema_type_info(field_info);
|
||||
let type_info = SchemaTypeInfo::from(field_info);
|
||||
let schema_type: Option<SchemaTypeVariant> = (&type_info).into();
|
||||
let range = type_info.get_range();
|
||||
assert!(!range.in_range((-1).into()));
|
||||
@ -1574,12 +1598,13 @@ mod tests {
|
||||
register.register_type_data::<bevy_math::Vec3, ReflectJsonSchemaForceAsArray>();
|
||||
}
|
||||
let type_registry = atr.read();
|
||||
let declaration = build_schema_type_info(
|
||||
let declaration = TypeInformation::from(
|
||||
type_registry
|
||||
.get(TypeId::of::<bevy_math::Vec3>())
|
||||
.expect(""),
|
||||
);
|
||||
let _: JsonSchemaVariant = declaration.clone().into();
|
||||
)
|
||||
.to_schema_type_info();
|
||||
let _: JsonSchemaVariant = declaration.to_definition().1.into();
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -1596,7 +1621,7 @@ mod tests {
|
||||
.as_tuple_struct()
|
||||
.expect("Should not fail");
|
||||
let field_info = struct_info.iter().next().expect("Should not fail");
|
||||
let type_info = build_schema_type_info(field_info);
|
||||
let type_info = SchemaTypeInfo::from(field_info);
|
||||
let schema_type: Option<SchemaTypeVariant> = (&type_info).into();
|
||||
let range = type_info.get_range();
|
||||
assert_eq!(range.min, Some(BoundValue::Inclusive(0.into())));
|
||||
@ -1630,7 +1655,9 @@ mod tests {
|
||||
pub struct ArrayComponent {
|
||||
pub array: [i32; 3],
|
||||
}
|
||||
let _ = build_schema(&ArrayComponent::get_type_registration());
|
||||
let type_info =
|
||||
TypeInformation::from(&ArrayComponent::get_type_registration()).to_schema_type_info();
|
||||
let _: JsonSchemaVariant = type_info.to_definition().1.into();
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -1643,7 +1670,9 @@ mod tests {
|
||||
"{\"map\": {\"0\": 1, \"1\": 41, \"2\": null}}"
|
||||
)
|
||||
.is_ok());
|
||||
let _ = build_schema(&HashMapStruct::get_type_registration());
|
||||
let type_info =
|
||||
TypeInformation::from(&HashMapStruct::get_type_registration()).to_schema_type_info();
|
||||
let _: JsonSchemaVariant = type_info.to_definition().1.into();
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -1669,6 +1698,9 @@ mod tests {
|
||||
pub second: SecondStruct,
|
||||
pub third: ThirdStruct,
|
||||
}
|
||||
let _ = build_schema(&NestedStruct::get_type_registration());
|
||||
let type_info =
|
||||
TypeInformation::from(&NestedStruct::get_type_registration()).to_schema_type_info();
|
||||
let _s: JsonSchemaVariant = type_info.to_definition().1.into();
|
||||
// eprintln!("{}", serde_json::to_string_pretty(&s).expect("msg"));
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user