Use Cow instead of String in JSON schema types
This commit is contained in:
parent
16f9c87bdd
commit
e93680baa3
@ -1,6 +1,7 @@
|
|||||||
//! Built-in verbs for the Bevy Remote Protocol.
|
//! Built-in verbs for the Bevy Remote Protocol.
|
||||||
|
|
||||||
use core::any::TypeId;
|
use core::any::TypeId;
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use anyhow::{anyhow, Result as AnyhowResult};
|
use anyhow::{anyhow, Result as AnyhowResult};
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
@ -1268,7 +1269,7 @@ pub fn export_registry_types(In(params): In<Option<Value>>, world: &World) -> Br
|
|||||||
}
|
}
|
||||||
Some((type_reg.type_info().type_path().into(), schema))
|
Some((type_reg.type_info().type_path().into(), schema))
|
||||||
})
|
})
|
||||||
.collect::<HashMap<String, JsonSchemaBevyType>>();
|
.collect::<HashMap<Cow<'static, str>, JsonSchemaBevyType>>();
|
||||||
|
|
||||||
serde_json::to_value(schemas).map_err(BrpError::internal)
|
serde_json::to_value(schemas).map_err(BrpError::internal)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,6 +8,7 @@ use bevy_reflect::{
|
|||||||
use core::any::TypeId;
|
use core::any::TypeId;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use crate::schemas::{
|
use crate::schemas::{
|
||||||
reflect_info::{SchemaInfoReflect, SchemaNumber},
|
reflect_info::{SchemaInfoReflect, SchemaNumber},
|
||||||
@ -102,11 +103,11 @@ impl TryFrom<&TypeRegistration> for JsonSchemaBevyType {
|
|||||||
|
|
||||||
/// 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(String);
|
pub struct SchemaMarker(Cow<'static, str>);
|
||||||
|
|
||||||
impl Default for SchemaMarker {
|
impl Default for SchemaMarker {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self("https://json-schema.org/draft/2020-12/schema".to_string())
|
Self("https://json-schema.org/draft/2020-12/schema".into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,22 +126,22 @@ pub struct JsonSchemaBevyType {
|
|||||||
/// This keyword is used to reference a statically identified schema.
|
/// This keyword is used to reference a statically identified schema.
|
||||||
#[serde(rename = "$ref")]
|
#[serde(rename = "$ref")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none", default)]
|
#[serde(skip_serializing_if = "Option::is_none", default)]
|
||||||
pub ref_type: Option<String>,
|
pub ref_type: Option<Cow<'static, str>>,
|
||||||
/// Bevy specific field, short path of the type.
|
/// Bevy specific field, short path of the type.
|
||||||
#[serde(skip_serializing_if = "String::is_empty", default)]
|
#[serde(skip_serializing_if = "str::is_empty", default)]
|
||||||
pub short_path: String,
|
pub short_path: Cow<'static, str>,
|
||||||
/// Bevy specific field, full path of the type.
|
/// Bevy specific field, full path of the type.
|
||||||
#[serde(skip_serializing_if = "String::is_empty", default)]
|
#[serde(skip_serializing_if = "str::is_empty", default)]
|
||||||
pub type_path: String,
|
pub type_path: Cow<'static, str>,
|
||||||
/// Bevy specific field, path of the module that type is part of.
|
/// Bevy specific field, path of the module that type is part of.
|
||||||
#[serde(skip_serializing_if = "Option::is_none", default)]
|
#[serde(skip_serializing_if = "Option::is_none", default)]
|
||||||
pub module_path: Option<String>,
|
pub module_path: Option<Cow<'static, str>>,
|
||||||
/// Bevy specific field, name of the crate that type is part of.
|
/// Bevy specific field, name of the crate that type is part of.
|
||||||
#[serde(skip_serializing_if = "Option::is_none", default)]
|
#[serde(skip_serializing_if = "Option::is_none", default)]
|
||||||
pub crate_name: Option<String>,
|
pub crate_name: Option<Cow<'static, str>>,
|
||||||
/// Bevy specific field, names of the types that type reflects.
|
/// Bevy specific field, names of the types that type reflects.
|
||||||
#[serde(skip_serializing_if = "Vec::is_empty", default)]
|
#[serde(skip_serializing_if = "Vec::is_empty", default)]
|
||||||
pub reflect_types: Vec<String>,
|
pub reflect_types: Vec<Cow<'static, str>>,
|
||||||
/// Bevy specific field, [`TypeInfo`] type mapping.
|
/// Bevy specific field, [`TypeInfo`] type mapping.
|
||||||
pub kind: SchemaKind,
|
pub kind: SchemaKind,
|
||||||
/// Bevy specific field, provided when [`SchemaKind`] `kind` field is equal to [`SchemaKind::Map`].
|
/// Bevy specific field, provided when [`SchemaKind`] `kind` field is equal to [`SchemaKind::Map`].
|
||||||
@ -167,10 +168,10 @@ pub struct JsonSchemaBevyType {
|
|||||||
/// within this keyword's value, the child instance for that name successfully validates
|
/// within this keyword's value, the child instance for that name successfully validates
|
||||||
/// against the corresponding schema.
|
/// against the corresponding schema.
|
||||||
#[serde(skip_serializing_if = "HashMap::is_empty", default)]
|
#[serde(skip_serializing_if = "HashMap::is_empty", default)]
|
||||||
pub properties: HashMap<String, JsonSchemaVariant>,
|
pub properties: HashMap<Cow<'static, str>, JsonSchemaVariant>,
|
||||||
/// An object instance is valid against this keyword if every item in the array is the name of a property in the instance.
|
/// An object instance is valid against this keyword if every item in the array is the name of a property in the instance.
|
||||||
#[serde(skip_serializing_if = "Vec::is_empty", default)]
|
#[serde(skip_serializing_if = "Vec::is_empty", default)]
|
||||||
pub required: Vec<String>,
|
pub required: Vec<Cow<'static, str>>,
|
||||||
/// An instance validates successfully against this keyword if it validates successfully against exactly one schema defined by this keyword's value.
|
/// An instance validates successfully against this keyword if it validates successfully against exactly one schema defined by this keyword's value.
|
||||||
#[serde(skip_serializing_if = "Vec::is_empty", default)]
|
#[serde(skip_serializing_if = "Vec::is_empty", default)]
|
||||||
pub one_of: Vec<JsonSchemaVariant>,
|
pub one_of: Vec<JsonSchemaVariant>,
|
||||||
@ -452,19 +453,24 @@ mod tests {
|
|||||||
let schema = export_type::<Foo>();
|
let schema = export_type::<Foo>();
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
!schema.reflect_types.contains(&"Component".to_owned()),
|
!schema.reflect_types.contains(&Cow::Borrowed("Component")),
|
||||||
"Should not be a component"
|
"Should not be a component"
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
schema.reflect_types.contains(&"Resource".to_owned()),
|
schema.reflect_types.contains(&Cow::Borrowed("Resource")),
|
||||||
"Should be a resource"
|
"Should be a resource"
|
||||||
);
|
);
|
||||||
|
|
||||||
let _ = schema.properties.get("a").expect("Missing `a` field");
|
let _ = schema.properties.get("a").expect("Missing `a` field");
|
||||||
let _ = schema.properties.get("b").expect("Missing `b` field");
|
let _ = schema.properties.get("b").expect("Missing `b` field");
|
||||||
assert!(
|
assert!(
|
||||||
schema.required.contains(&"a".to_owned()),
|
schema.required.contains(&Cow::Borrowed("a")),
|
||||||
"Field a should be required"
|
"Field a should be required"
|
||||||
);
|
);
|
||||||
|
assert!(
|
||||||
|
schema.required.contains(&Cow::Borrowed("b")),
|
||||||
|
"Field b should be required"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -482,11 +488,11 @@ mod tests {
|
|||||||
}
|
}
|
||||||
let schema = export_type::<EnumComponent>();
|
let schema = export_type::<EnumComponent>();
|
||||||
assert!(
|
assert!(
|
||||||
schema.reflect_types.contains(&"Component".to_owned()),
|
schema.reflect_types.contains(&Cow::Borrowed("Component")),
|
||||||
"Should be a component"
|
"Should be a component"
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
!schema.reflect_types.contains(&"Resource".to_owned()),
|
!schema.reflect_types.contains(&Cow::Borrowed("Resource")),
|
||||||
"Should not be a resource"
|
"Should not be a resource"
|
||||||
);
|
);
|
||||||
assert!(schema.properties.is_empty(), "Should not have any field");
|
assert!(schema.properties.is_empty(), "Should not have any field");
|
||||||
@ -506,11 +512,11 @@ mod tests {
|
|||||||
}
|
}
|
||||||
let schema = export_type::<EnumComponent>();
|
let schema = export_type::<EnumComponent>();
|
||||||
assert!(
|
assert!(
|
||||||
!schema.reflect_types.contains(&"Component".to_owned()),
|
!schema.reflect_types.contains(&Cow::Borrowed("Component")),
|
||||||
"Should not be a component"
|
"Should not be a component"
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
!schema.reflect_types.contains(&"Resource".to_owned()),
|
!schema.reflect_types.contains(&Cow::Borrowed("Resource")),
|
||||||
"Should not be a resource"
|
"Should not be a resource"
|
||||||
);
|
);
|
||||||
assert!(schema.properties.is_empty(), "Should not have any field");
|
assert!(schema.properties.is_empty(), "Should not have any field");
|
||||||
@ -601,7 +607,7 @@ mod tests {
|
|||||||
.export_type_json_schema::<SomeType>(&SchemaTypesMetadata::default())
|
.export_type_json_schema::<SomeType>(&SchemaTypesMetadata::default())
|
||||||
.expect("Failed to export schema");
|
.expect("Failed to export schema");
|
||||||
assert!(
|
assert!(
|
||||||
!schema.reflect_types.contains(&"Component".to_owned()),
|
!schema.reflect_types.contains(&Cow::Borrowed("Component")),
|
||||||
"Should not be a component"
|
"Should not be a component"
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
@ -621,11 +627,11 @@ mod tests {
|
|||||||
|
|
||||||
let schema = export_type::<TupleStructType>();
|
let schema = export_type::<TupleStructType>();
|
||||||
assert!(
|
assert!(
|
||||||
schema.reflect_types.contains(&"Component".to_owned()),
|
schema.reflect_types.contains(&Cow::Borrowed("Component")),
|
||||||
"Should be a component"
|
"Should be a component"
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
!schema.reflect_types.contains(&"Resource".to_owned()),
|
!schema.reflect_types.contains(&Cow::Borrowed("Resource")),
|
||||||
"Should not be a resource"
|
"Should not be a resource"
|
||||||
);
|
);
|
||||||
assert!(schema.properties.is_empty(), "Should not have any field");
|
assert!(schema.properties.is_empty(), "Should not have any field");
|
||||||
|
|||||||
@ -10,6 +10,7 @@ use bevy_reflect::{
|
|||||||
TypeRegistration,
|
TypeRegistration,
|
||||||
};
|
};
|
||||||
use core::any::TypeId;
|
use core::any::TypeId;
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use crate::schemas::json_schema::JsonSchemaBevyType;
|
use crate::schemas::json_schema::JsonSchemaBevyType;
|
||||||
|
|
||||||
@ -23,7 +24,7 @@ pub mod reflect_info;
|
|||||||
#[reflect(Resource)]
|
#[reflect(Resource)]
|
||||||
pub struct SchemaTypesMetadata {
|
pub struct SchemaTypesMetadata {
|
||||||
/// Type Data id mapping to human-readable type names.
|
/// Type Data id mapping to human-readable type names.
|
||||||
pub type_data_map: HashMap<TypeId, String>,
|
pub type_data_map: HashMap<TypeId, Cow<'static, str>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reflect-compatible custom JSON Schema for this type
|
/// Reflect-compatible custom JSON Schema for this type
|
||||||
@ -62,12 +63,12 @@ impl Default for SchemaTypesMetadata {
|
|||||||
|
|
||||||
impl SchemaTypesMetadata {
|
impl SchemaTypesMetadata {
|
||||||
/// Map `TypeId` of `TypeData` to a human-readable type name
|
/// Map `TypeId` of `TypeData` to a human-readable type name
|
||||||
pub fn map_type_data<T: TypeData>(&mut self, name: impl Into<String>) {
|
pub fn map_type_data<T: TypeData>(&mut self, name: impl Into<Cow<'static, str>>) {
|
||||||
self.type_data_map.insert(TypeId::of::<T>(), name.into());
|
self.type_data_map.insert(TypeId::of::<T>(), name.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build reflect types list for a given type registration
|
/// Build reflect types list for a given type registration
|
||||||
pub fn get_registered_reflect_types(&self, reg: &TypeRegistration) -> Vec<String> {
|
pub fn get_registered_reflect_types(&self, reg: &TypeRegistration) -> Vec<Cow<'static, str>> {
|
||||||
self.type_data_map
|
self.type_data_map
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|(id, name)| reg.data_by_id(*id).and(Some(name.clone())))
|
.filter_map(|(id, name)| reg.data_by_id(*id).and(Some(name.clone())))
|
||||||
@ -75,12 +76,16 @@ impl SchemaTypesMetadata {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if slice contains a type name that matches the checked `TypeData`
|
/// Checks if slice contains a type name that matches the checked `TypeData`
|
||||||
pub fn has_type_data<T: TypeData>(&self, types_string_slice: &[String]) -> bool {
|
pub fn has_type_data<T: TypeData>(&self, types_string_slice: &[Cow<'static, str>]) -> bool {
|
||||||
self.has_type_data_by_id(TypeId::of::<T>(), types_string_slice)
|
self.has_type_data_by_id(TypeId::of::<T>(), types_string_slice)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if slice contains a type name that matches the checked `TypeData` by id.
|
/// Checks if slice contains a type name that matches the checked `TypeData` by id.
|
||||||
pub fn has_type_data_by_id(&self, id: TypeId, types_string_slice: &[String]) -> bool {
|
pub fn has_type_data_by_id(
|
||||||
|
&self,
|
||||||
|
id: TypeId,
|
||||||
|
types_string_slice: &[Cow<'static, str>],
|
||||||
|
) -> bool {
|
||||||
self.type_data_map
|
self.type_data_map
|
||||||
.get(&id)
|
.get(&id)
|
||||||
.is_some_and(|data_s| types_string_slice.iter().any(|e| e.eq(data_s)))
|
.is_some_and(|data_s| types_string_slice.iter().any(|e| e.eq(data_s)))
|
||||||
|
|||||||
@ -334,21 +334,21 @@ impl Into<JsonSchemaVariant> for SchemaTypeInfo {
|
|||||||
type_path: self
|
type_path: self
|
||||||
.type_info
|
.type_info
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|s| Some(s.type_path_table().path().to_owned()))
|
.and_then(|s| Some(s.type_path_table().path().into()))
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
short_path: self
|
short_path: self
|
||||||
.type_info
|
.type_info
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|s| Some(s.type_path_table().short_path().to_owned()))
|
.and_then(|s| Some(s.type_path_table().short_path().into()))
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
crate_name: self
|
crate_name: self
|
||||||
.type_info
|
.type_info
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|s| s.type_path_table().crate_name().map(str::to_owned)),
|
.and_then(|s| s.type_path_table().crate_name().map(Into::into)),
|
||||||
module_path: self
|
module_path: self
|
||||||
.type_info
|
.type_info
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|s| s.type_path_table().module_path().map(str::to_owned)),
|
.and_then(|s| s.type_path_table().module_path().map(Into::into)),
|
||||||
minimum: self.range.min.as_ref().and_then(|r| r.get_inclusive()),
|
minimum: self.range.min.as_ref().and_then(|r| r.get_inclusive()),
|
||||||
maximum: self.range.max.as_ref().and_then(|r| r.get_inclusive()),
|
maximum: self.range.max.as_ref().and_then(|r| r.get_inclusive()),
|
||||||
exclusive_minimum: self.range.min.as_ref().and_then(|r| r.get_exclusive()),
|
exclusive_minimum: self.range.min.as_ref().and_then(|r| r.get_exclusive()),
|
||||||
@ -396,9 +396,8 @@ impl Into<JsonSchemaVariant> for SchemaTypeInfo {
|
|||||||
range: MinMaxValues::default(),
|
range: MinMaxValues::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
schema.properties =
|
schema.properties = [(variant_info.name().into(), schema_field.into())].into();
|
||||||
[(variant_info.name().to_string(), schema_field.into())].into();
|
schema.required = vec![variant_info.name().into()];
|
||||||
schema.required = vec![variant_info.name().to_string()];
|
|
||||||
}
|
}
|
||||||
VariantInfo::Tuple(tuple_variant_info) => {
|
VariantInfo::Tuple(tuple_variant_info) => {
|
||||||
schema.kind = SchemaKind::Value;
|
schema.kind = SchemaKind::Value;
|
||||||
@ -414,9 +413,8 @@ impl Into<JsonSchemaVariant> for SchemaTypeInfo {
|
|||||||
type_id: Some(tuple_variant_info.type_id()),
|
type_id: Some(tuple_variant_info.type_id()),
|
||||||
range: MinMaxValues::default(),
|
range: MinMaxValues::default(),
|
||||||
};
|
};
|
||||||
schema.properties =
|
schema.properties = [(variant_info.name().into(), schema_field.into())].into();
|
||||||
[(variant_info.name().to_string(), schema_field.into())].into();
|
schema.required = vec![variant_info.name().into()];
|
||||||
schema.required = vec![variant_info.name().to_string()];
|
|
||||||
}
|
}
|
||||||
VariantInfo::Unit(unit_variant_info) => {
|
VariantInfo::Unit(unit_variant_info) => {
|
||||||
return JsonSchemaVariant::const_value(
|
return JsonSchemaVariant::const_value(
|
||||||
@ -430,11 +428,11 @@ impl Into<JsonSchemaVariant> for SchemaTypeInfo {
|
|||||||
schema.schema_type = Some(SchemaTypeVariant::Single(SchemaType::Object));
|
schema.schema_type = Some(SchemaTypeVariant::Single(SchemaType::Object));
|
||||||
schema.properties = named_fields
|
schema.properties = named_fields
|
||||||
.iter()
|
.iter()
|
||||||
.map(|field| (field.name().to_string(), field.build_schema()))
|
.map(|field| (field.name().into(), field.build_schema()))
|
||||||
.collect();
|
.collect();
|
||||||
schema.required = named_fields
|
schema.required = named_fields
|
||||||
.iter()
|
.iter()
|
||||||
.map(|field| field.name().to_string())
|
.map(|field| field.name().into())
|
||||||
.collect();
|
.collect();
|
||||||
}
|
}
|
||||||
InternalSchemaType::UnnamedFieldsHolder(unnamed_fields) => {
|
InternalSchemaType::UnnamedFieldsHolder(unnamed_fields) => {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user