Path: blob/main/crates/bevy_remote/src/builtin_methods.rs
6598 views
//! Built-in verbs for the Bevy Remote Protocol.12use core::any::TypeId;34use anyhow::{anyhow, Result as AnyhowResult};5use bevy_ecs::{6component::ComponentId,7entity::Entity,8event::EventCursor,9hierarchy::ChildOf,10lifecycle::RemovedComponentEntity,11query::QueryBuilder,12reflect::{AppTypeRegistry, ReflectComponent, ReflectResource},13system::{In, Local},14world::{EntityRef, EntityWorldMut, FilteredEntityRef, World},15};16use bevy_log::warn_once;17use bevy_platform::collections::HashMap;18use bevy_reflect::{19serde::{ReflectSerializer, TypedReflectDeserializer},20GetPath, PartialReflect, TypeRegistration, TypeRegistry,21};22use serde::{de::DeserializeSeed as _, Deserialize, Serialize};23use serde_json::{Map, Value};2425use crate::{26error_codes,27schemas::{28json_schema::{export_type, JsonSchemaBevyType},29open_rpc::OpenRpcDocument,30},31BrpError, BrpResult,32};3334#[cfg(all(feature = "http", not(target_family = "wasm")))]35use {crate::schemas::open_rpc::ServerObject, bevy_utils::default};3637/// The method path for a `world.get_components` request.38pub const BRP_GET_COMPONENTS_METHOD: &str = "world.get_components";3940/// The method path for a `world.query` request.41pub const BRP_QUERY_METHOD: &str = "world.query";4243/// The method path for a `world.spawn_entity` request.44pub const BRP_SPAWN_ENTITY_METHOD: &str = "world.spawn_entity";4546/// The method path for a `world.insert_components` request.47pub const BRP_INSERT_COMPONENTS_METHOD: &str = "world.insert_components";4849/// The method path for a `world.remove_components` request.50pub const BRP_REMOVE_COMPONENTS_METHOD: &str = "world.remove_components";5152/// The method path for a `world.despawn_entity` request.53pub const BRP_DESPAWN_COMPONENTS_METHOD: &str = "world.despawn_entity";5455/// The method path for a `world.reparent_entities` request.56pub const BRP_REPARENT_ENTITIES_METHOD: &str = "world.reparent_entities";5758/// The method path for a `world.list_components` request.59pub const BRP_LIST_COMPONENTS_METHOD: &str = "world.list_components";6061/// The method path for a `world.mutate_components` request.62pub const BRP_MUTATE_COMPONENTS_METHOD: &str = "world.mutate_components";6364/// The method path for a `world.get_components+watch` request.65pub const BRP_GET_COMPONENTS_AND_WATCH_METHOD: &str = "world.get_components+watch";6667/// The method path for a `world.list_components+watch` request.68pub const BRP_LIST_COMPONENTS_AND_WATCH_METHOD: &str = "world.list_components+watch";6970/// The method path for a `world.get_resources` request.71pub const BRP_GET_RESOURCE_METHOD: &str = "world.get_resources";7273/// The method path for a `world.insert_resources` request.74pub const BRP_INSERT_RESOURCE_METHOD: &str = "world.insert_resources";7576/// The method path for a `world.remove_resources` request.77pub const BRP_REMOVE_RESOURCE_METHOD: &str = "world.remove_resources";7879/// The method path for a `world.mutate_resources` request.80pub const BRP_MUTATE_RESOURCE_METHOD: &str = "world.mutate_resources";8182/// The method path for a `world.list_resources` request.83pub const BRP_LIST_RESOURCES_METHOD: &str = "world.list_resources";8485/// The method path for a `registry.schema` request.86pub const BRP_REGISTRY_SCHEMA_METHOD: &str = "registry.schema";8788/// The method path for a `rpc.discover` request.89pub const RPC_DISCOVER_METHOD: &str = "rpc.discover";9091/// `world.get_components`: Retrieves one or more components from the entity with the given92/// ID.93///94/// The server responds with a [`BrpGetComponentsResponse`].95#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]96pub struct BrpGetComponentsParams {97/// The ID of the entity from which components are to be requested.98pub entity: Entity,99100/// The [full paths] of the component types that are to be requested101/// from the entity.102///103/// Note that these strings must consist of the *full* type paths: e.g.104/// `bevy_transform::components::transform::Transform`, not just105/// `Transform`.106///107/// [full paths]: bevy_reflect::TypePath::type_path108pub components: Vec<String>,109110/// An optional flag to fail when encountering an invalid component rather111/// than skipping it. Defaults to false.112#[serde(default)]113pub strict: bool,114}115116/// `world.get_resources`: Retrieves the value of a given resource.117#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]118pub struct BrpGetResourcesParams {119/// The [full path] of the resource type being requested.120///121/// [full path]: bevy_reflect::TypePath::type_path122pub resource: String,123}124125/// `world.query`: Performs a query over components in the ECS, returning entities126/// and component values that match.127///128/// The server responds with a [`BrpQueryResponse`].129#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]130pub struct BrpQueryParams {131/// The components to select.132pub data: BrpQuery,133134/// An optional filter that specifies which entities to include or135/// exclude from the results.136#[serde(default)]137pub filter: BrpQueryFilter,138139/// An optional flag to fail when encountering an invalid component rather140/// than skipping it. Defaults to false.141#[serde(default)]142pub strict: bool,143}144145/// `world.spawn_entity`: Creates a new entity with the given components and responds146/// with its ID.147///148/// The server responds with a [`BrpSpawnEntityResponse`].149#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]150pub struct BrpSpawnEntityParams {151/// A map from each component's full path to its serialized value.152///153/// These components will be added to the entity.154///155/// Note that the keys of the map must be the [full type paths]: e.g.156/// `bevy_transform::components::transform::Transform`, not just157/// `Transform`.158///159/// [full type paths]: bevy_reflect::TypePath::type_path160pub components: HashMap<String, Value>,161}162163/// `world.despawn_entity`: Given an ID, despawns the entity with that ID.164///165/// The server responds with an okay.166#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]167pub struct BrpDespawnEntityParams {168/// The ID of the entity to despawn.169pub entity: Entity,170}171172/// `world.remove_components`: Deletes one or more components from an entity.173///174/// The server responds with a null.175#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]176pub struct BrpRemoveComponentsParams {177/// The ID of the entity from which components are to be removed.178pub entity: Entity,179180/// The full paths of the component types that are to be removed from181/// the entity.182///183/// Note that these strings must consist of the [full type paths]: e.g.184/// `bevy_transform::components::transform::Transform`, not just185/// `Transform`.186///187/// [full type paths]: bevy_reflect::TypePath::type_path188pub components: Vec<String>,189}190191/// `world.remove_resources`: Removes the given resource from the world.192#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]193pub struct BrpRemoveResourcesParams {194/// The [full path] of the resource type to remove.195///196/// [full path]: bevy_reflect::TypePath::type_path197pub resource: String,198}199200/// `world.insert_components`: Adds one or more components to an entity.201///202/// The server responds with a null.203#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]204pub struct BrpInsertComponentsParams {205/// The ID of the entity that components are to be added to.206pub entity: Entity,207208/// A map from each component's full path to its serialized value.209///210/// These components will be added to the entity.211///212/// Note that the keys of the map must be the [full type paths]: e.g.213/// `bevy_transform::components::transform::Transform`, not just214/// `Transform`.215///216/// [full type paths]: bevy_reflect::TypePath::type_path217pub components: HashMap<String, Value>,218}219220/// `world.insert_resources`: Inserts a resource into the world with a given221/// value.222#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]223pub struct BrpInsertResourcesParams {224/// The [full path] of the resource type to insert.225///226/// [full path]: bevy_reflect::TypePath::type_path227pub resource: String,228229/// The serialized value of the resource to be inserted.230pub value: Value,231}232233/// `world.reparent_entities`: Assign a new parent to one or more entities.234///235/// The server responds with a null.236#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]237pub struct BrpReparentEntitiesParams {238/// The IDs of the entities that are to become the new children of the239/// `parent`.240pub entities: Vec<Entity>,241242/// The IDs of the entity that will become the new parent of the243/// `entities`.244///245/// If this is `None`, then the entities are removed from all parents.246#[serde(default)]247pub parent: Option<Entity>,248}249250/// `world.list_components`: Returns a list of all type names of registered components in the251/// system (no params provided), or those on an entity (params provided).252///253/// The server responds with a [`BrpListComponentsResponse`]254#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]255pub struct BrpListComponentsParams {256/// The entity to query.257pub entity: Entity,258}259260/// `world.mutate_components`:261///262/// The server responds with a null.263#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]264pub struct BrpMutateComponentsParams {265/// The entity of the component to mutate.266pub entity: Entity,267268/// The [full path] of the component to mutate.269///270/// [full path]: bevy_reflect::TypePath::type_path271pub component: String,272273/// The [path] of the field within the component.274///275/// [path]: bevy_reflect::GetPath276pub path: String,277278/// The value to insert at `path`.279pub value: Value,280}281282/// `world.mutate_resources`:283///284/// The server responds with a null.285#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]286pub struct BrpMutateResourcesParams {287/// The [full path] of the resource to mutate.288///289/// [full path]: bevy_reflect::TypePath::type_path290pub resource: String,291292/// The [path] of the field within the resource.293///294/// [path]: bevy_reflect::GetPath295pub path: String,296297/// The value to insert at `path`.298pub value: Value,299}300301/// Describes the data that is to be fetched in a query.302#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq)]303pub struct BrpQuery {304/// The [full path] of the type name of each component that is to be305/// fetched.306///307/// [full path]: bevy_reflect::TypePath::type_path308#[serde(default)]309pub components: Vec<String>,310311/// The [full path] of the type name of each component that is to be312/// optionally fetched.313///314/// [full path]: bevy_reflect::TypePath::type_path315#[serde(default)]316pub option: ComponentSelector,317318/// The [full path] of the type name of each component that is to be checked319/// for presence.320///321/// [full path]: bevy_reflect::TypePath::type_path322#[serde(default)]323pub has: Vec<String>,324}325326/// Additional constraints that can be placed on a query to include or exclude327/// certain entities.328#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq)]329pub struct BrpQueryFilter {330/// The [full path] of the type name of each component that must not be331/// present on the entity for it to be included in the results.332///333/// [full path]: bevy_reflect::TypePath::type_path334#[serde(default)]335pub without: Vec<String>,336337/// The [full path] of the type name of each component that must be present338/// on the entity for it to be included in the results.339///340/// [full path]: bevy_reflect::TypePath::type_path341#[serde(default)]342pub with: Vec<String>,343}344345/// Constraints that can be placed on a query to include or exclude346/// certain definitions.347#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq)]348pub struct BrpJsonSchemaQueryFilter {349/// The crate name of the type name of each component that must not be350/// present on the entity for it to be included in the results.351#[serde(skip_serializing_if = "Vec::is_empty", default)]352pub without_crates: Vec<String>,353354/// The crate name of the type name of each component that must be present355/// on the entity for it to be included in the results.356#[serde(skip_serializing_if = "Vec::is_empty", default)]357pub with_crates: Vec<String>,358359/// Constrain resource by type360#[serde(default)]361pub type_limit: JsonSchemaTypeLimit,362}363364/// Additional [`BrpJsonSchemaQueryFilter`] constraints that can be placed on a query to include or exclude365/// certain definitions.366#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq)]367pub struct JsonSchemaTypeLimit {368/// Schema cannot have specified reflect types369#[serde(skip_serializing_if = "Vec::is_empty", default)]370pub without: Vec<String>,371372/// Schema needs to have specified reflect types373#[serde(skip_serializing_if = "Vec::is_empty", default)]374pub with: Vec<String>,375}376377/// A response from the world to the client that specifies a single entity.378///379/// This is sent in response to `world.spawn_entity`.380#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]381pub struct BrpSpawnEntityResponse {382/// The ID of the entity in question.383pub entity: Entity,384}385386/// The response to a `world.get_components` request.387#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]388#[serde(untagged)]389pub enum BrpGetComponentsResponse {390/// The non-strict response that reports errors separately without failing the entire request.391Lenient {392/// A map of successful components with their values.393components: HashMap<String, Value>,394/// A map of unsuccessful components with their errors.395errors: HashMap<String, Value>,396},397/// The strict response that will fail if any components are not present or aren't398/// reflect-able.399Strict(HashMap<String, Value>),400}401402/// The response to a `world.get_resources` request.403#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]404pub struct BrpGetResourcesResponse {405/// The value of the requested resource.406pub value: Value,407}408409/// A single response from a `world.get_components+watch` request.410#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]411#[serde(untagged)]412pub enum BrpGetComponentsWatchingResponse {413/// The non-strict response that reports errors separately without failing the entire request.414Lenient {415/// A map of successful components with their values that were added or changes in the last416/// tick.417components: HashMap<String, Value>,418/// An array of components that were been removed in the last tick.419removed: Vec<String>,420/// A map of unsuccessful components with their errors.421errors: HashMap<String, Value>,422},423/// The strict response that will fail if any components are not present or aren't424/// reflect-able.425Strict {426/// A map of successful components with their values that were added or changes in the last427/// tick.428components: HashMap<String, Value>,429/// An array of components that were been removed in the last tick.430removed: Vec<String>,431},432}433434/// The response to a `world.list_components` request.435pub type BrpListComponentsResponse = Vec<String>;436437/// The response to a `world.list_resources` request.438pub type BrpListResourcesResponse = Vec<String>;439440/// A single response from a `world.list_components+watch` request.441#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]442pub struct BrpListComponentsWatchingResponse {443added: Vec<String>,444removed: Vec<String>,445}446447/// The response to a `world.query` request.448pub type BrpQueryResponse = Vec<BrpQueryRow>;449450/// One query match result: a single entity paired with the requested components.451#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]452pub struct BrpQueryRow {453/// The ID of the entity that matched.454pub entity: Entity,455456/// The serialized values of the requested components.457pub components: HashMap<String, Value>,458459/// The boolean-only containment query results.460#[serde(skip_serializing_if = "HashMap::is_empty", default)]461pub has: HashMap<String, Value>,462}463464/// A helper function used to parse a `serde_json::Value`.465fn parse<T: for<'de> Deserialize<'de>>(value: Value) -> Result<T, BrpError> {466serde_json::from_value(value).map_err(|err| BrpError {467code: error_codes::INVALID_PARAMS,468message: err.to_string(),469data: None,470})471}472473/// A helper function used to parse a `serde_json::Value` wrapped in an `Option`.474fn parse_some<T: for<'de> Deserialize<'de>>(value: Option<Value>) -> Result<T, BrpError> {475match value {476Some(value) => parse(value),477None => Err(BrpError {478code: error_codes::INVALID_PARAMS,479message: String::from("Params not provided"),480data: None,481}),482}483}484485/// Handles a `world.get_components` request coming from a client.486pub fn process_remote_get_components_request(487In(params): In<Option<Value>>,488world: &World,489) -> BrpResult {490let BrpGetComponentsParams {491entity,492components,493strict,494} = parse_some(params)?;495496let app_type_registry = world.resource::<AppTypeRegistry>();497let type_registry = app_type_registry.read();498let entity_ref = get_entity(world, entity)?;499500let response =501reflect_components_to_response(components, strict, entity, entity_ref, &type_registry)?;502serde_json::to_value(response).map_err(BrpError::internal)503}504505/// Handles a `world.get_resources` request coming from a client.506pub fn process_remote_get_resources_request(507In(params): In<Option<Value>>,508world: &World,509) -> BrpResult {510let BrpGetResourcesParams {511resource: resource_path,512} = parse_some(params)?;513514let app_type_registry = world.resource::<AppTypeRegistry>();515let type_registry = app_type_registry.read();516let reflect_resource =517get_reflect_resource(&type_registry, &resource_path).map_err(BrpError::resource_error)?;518519let Ok(reflected) = reflect_resource.reflect(world) else {520return Err(BrpError::resource_not_present(&resource_path));521};522523// Use the `ReflectSerializer` to serialize the value of the resource;524// this produces a map with a single item.525let reflect_serializer = ReflectSerializer::new(reflected.as_partial_reflect(), &type_registry);526let Value::Object(serialized_object) =527serde_json::to_value(&reflect_serializer).map_err(BrpError::resource_error)?528else {529return Err(BrpError {530code: error_codes::RESOURCE_ERROR,531message: format!("Resource `{resource_path}` could not be serialized"),532data: None,533});534};535536// Get the single value out of the map.537let value = serialized_object.into_values().next().ok_or_else(|| {538BrpError::internal(anyhow!("Unexpected format of serialized resource value"))539})?;540let response = BrpGetResourcesResponse { value };541serde_json::to_value(response).map_err(BrpError::internal)542}543544/// Handles a `world.get_components+watch` request coming from a client.545pub fn process_remote_get_components_watching_request(546In(params): In<Option<Value>>,547world: &World,548mut removal_cursors: Local<HashMap<ComponentId, EventCursor<RemovedComponentEntity>>>,549) -> BrpResult<Option<Value>> {550let BrpGetComponentsParams {551entity,552components,553strict,554} = parse_some(params)?;555556let app_type_registry = world.resource::<AppTypeRegistry>();557let type_registry = app_type_registry.read();558let entity_ref = get_entity(world, entity)?;559560let mut changed = Vec::new();561let mut removed = Vec::new();562let mut errors = <HashMap<_, _>>::default();563564'component_loop: for component_path in components {565let Ok(type_registration) =566get_component_type_registration(&type_registry, &component_path)567else {568let err =569BrpError::component_error(format!("Unknown component type: `{component_path}`"));570if strict {571return Err(err);572}573errors.insert(574component_path,575serde_json::to_value(err).map_err(BrpError::internal)?,576);577continue;578};579let Some(component_id) = world.components().get_valid_id(type_registration.type_id())580else {581let err = BrpError::component_error(format!("Unknown component: `{component_path}`"));582if strict {583return Err(err);584}585errors.insert(586component_path,587serde_json::to_value(err).map_err(BrpError::internal)?,588);589continue;590};591592if let Some(ticks) = entity_ref.get_change_ticks_by_id(component_id)593&& ticks.is_changed(world.last_change_tick(), world.read_change_tick())594{595changed.push(component_path);596continue;597};598599let Some(events) = world.removed_components().get(component_id) else {600continue;601};602let cursor = removal_cursors603.entry(component_id)604.or_insert_with(|| events.get_cursor());605for event in cursor.read(events) {606if Entity::from(event.clone()) == entity {607removed.push(component_path);608continue 'component_loop;609}610}611}612613if changed.is_empty() && removed.is_empty() {614return Ok(None);615}616617let response =618reflect_components_to_response(changed, strict, entity, entity_ref, &type_registry)?;619620let response = match response {621BrpGetComponentsResponse::Lenient {622components,623errors: mut errs,624} => BrpGetComponentsWatchingResponse::Lenient {625components,626removed,627errors: {628errs.extend(errors);629errs630},631},632BrpGetComponentsResponse::Strict(components) => BrpGetComponentsWatchingResponse::Strict {633components,634removed,635},636};637638Ok(Some(639serde_json::to_value(response).map_err(BrpError::internal)?,640))641}642643/// Reflect a list of components on an entity into a [`BrpGetComponentsResponse`].644fn reflect_components_to_response(645components: Vec<String>,646strict: bool,647entity: Entity,648entity_ref: EntityRef,649type_registry: &TypeRegistry,650) -> BrpResult<BrpGetComponentsResponse> {651let mut response = if strict {652BrpGetComponentsResponse::Strict(Default::default())653} else {654BrpGetComponentsResponse::Lenient {655components: Default::default(),656errors: Default::default(),657}658};659660for component_path in components {661match reflect_component(&component_path, entity, entity_ref, type_registry) {662Ok(serialized_object) => match response {663BrpGetComponentsResponse::Strict(ref mut components)664| BrpGetComponentsResponse::Lenient {665ref mut components, ..666} => {667components.extend(serialized_object.into_iter());668}669},670Err(err) => match response {671BrpGetComponentsResponse::Strict(_) => return Err(err),672BrpGetComponentsResponse::Lenient { ref mut errors, .. } => {673let err_value = serde_json::to_value(err).map_err(BrpError::internal)?;674errors.insert(component_path, err_value);675}676},677}678}679680Ok(response)681}682683/// Reflect a single component on an entity with the given component path.684fn reflect_component(685component_path: &str,686entity: Entity,687entity_ref: EntityRef,688type_registry: &TypeRegistry,689) -> BrpResult<Map<String, Value>> {690let reflect_component =691get_reflect_component(type_registry, component_path).map_err(BrpError::component_error)?;692693// Retrieve the reflected value for the given specified component on the given entity.694let Some(reflected) = reflect_component.reflect(entity_ref) else {695return Err(BrpError::component_not_present(component_path, entity));696};697698// Each component value serializes to a map with a single entry.699let reflect_serializer = ReflectSerializer::new(reflected.as_partial_reflect(), type_registry);700let Value::Object(serialized_object) =701serde_json::to_value(&reflect_serializer).map_err(BrpError::component_error)?702else {703return Err(BrpError {704code: error_codes::COMPONENT_ERROR,705message: format!("Component `{component_path}` could not be serialized"),706data: None,707});708};709710Ok(serialized_object)711}712713/// A selector for components in a query.714///715/// This can either be a list of component paths or an "all" selector that716/// indicates that all components should be selected.717/// The "all" selector is useful when you want to retrieve all components718/// present on an entity without specifying each one individually.719/// The paths in the `Paths` variant must be the [full type paths]: e.g.720/// `bevy_transform::components::transform::Transform`, not just721/// `Transform`.722///723#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]724#[serde(rename_all = "snake_case")]725pub enum ComponentSelector {726/// An "all" selector that indicates all components should be selected.727All,728/// A list of component paths to select as optional components.729#[serde(untagged)]730Paths(Vec<String>),731}732733impl Default for ComponentSelector {734fn default() -> Self {735Self::Paths(Vec::default())736}737}738739/// Handles a `world.query` request coming from a client.740pub fn process_remote_query_request(In(params): In<Option<Value>>, world: &mut World) -> BrpResult {741let BrpQueryParams {742data: BrpQuery {743components,744option,745has,746},747filter,748strict,749} = match params {750Some(params) => parse_some(Some(params))?,751None => BrpQueryParams {752data: BrpQuery {753components: Vec::new(),754option: ComponentSelector::default(),755has: Vec::new(),756},757filter: BrpQueryFilter::default(),758strict: false,759},760};761762let app_type_registry = world.resource::<AppTypeRegistry>().clone();763let type_registry = app_type_registry.read();764765// Required components: must be present766let (required, unregistered_in_required) =767get_component_ids(&type_registry, world, components.clone(), strict)768.map_err(BrpError::component_error)?;769770// Optional components: Option<&T> or all reflectable if "all"771let (optional, _) = match &option {772ComponentSelector::Paths(paths) => {773get_component_ids(&type_registry, world, paths.clone(), strict)774.map_err(BrpError::component_error)?775}776ComponentSelector::All => (Vec::new(), Vec::new()),777};778779// Has components: presence check780let (has_ids, unregistered_in_has) =781get_component_ids(&type_registry, world, has, strict).map_err(BrpError::component_error)?;782783// Filters784let (without, _) = get_component_ids(&type_registry, world, filter.without.clone(), strict)785.map_err(BrpError::component_error)?;786let (with, unregistered_in_with) =787get_component_ids(&type_registry, world, filter.with.clone(), strict)788.map_err(BrpError::component_error)?;789790// When "strict" is false:791// - Unregistered components in "option" and "without" are ignored.792// - Unregistered components in "has" are considered absent from the entity.793// - Unregistered components in "components" and "with" result in an empty794// response since they specify hard requirements.795// If strict, fail if any required or with components are unregistered796if !unregistered_in_required.is_empty() || !unregistered_in_with.is_empty() {797return serde_json::to_value(BrpQueryResponse::default()).map_err(BrpError::internal);798}799800let mut query = QueryBuilder::<FilteredEntityRef>::new(world);801for (_, component) in &required {802query.ref_id(*component);803}804for (_, option) in &optional {805query.optional(|query| {806query.ref_id(*option);807});808}809for (_, has) in &has_ids {810query.optional(|query| {811query.ref_id(*has);812});813}814for (_, without) in without {815query.without_id(without);816}817for (_, with) in with {818query.with_id(with);819}820821// Prepare has reflect info822let has_paths_and_reflect_components: Vec<(&str, &ReflectComponent)> = has_ids823.iter()824.map(|(type_id, _)| reflect_component_from_id(*type_id, &type_registry))825.collect::<AnyhowResult<Vec<(&str, &ReflectComponent)>>>()826.map_err(BrpError::component_error)?;827828let mut response = BrpQueryResponse::default();829let mut query = query.build();830831for row in query.iter(world) {832let entity_id = row.id();833let entity_ref = world.get_entity(entity_id).expect("Entity should exist");834835// Required components836let mut components_map = serialize_components(837entity_ref,838&type_registry,839required840.iter()841.map(|(type_id, component_id)| (*type_id, Some(*component_id))),842);843844// Optional components845match &option {846ComponentSelector::All => {847// Add all reflectable components present on the entity (as Option<&T>)848let all_optionals =849entity_ref850.archetype()851.components()852.filter_map(|component_id| {853let info = world.components().get_info(component_id)?;854let type_id = info.type_id()?;855// Skip required components (already included)856if required.iter().any(|(_, cid)| cid == &component_id) {857return None;858}859Some((type_id, Some(component_id)))860});861components_map.extend(serialize_components(862entity_ref,863&type_registry,864all_optionals,865));866}867ComponentSelector::Paths(_) => {868// Add only the requested optional components (as Option<&T>)869let optionals = optional.iter().filter(|(_, component_id)| {870// Skip required components (already included)871!required.iter().any(|(_, cid)| cid == component_id)872});873components_map.extend(serialize_components(874entity_ref,875&type_registry,876optionals877.clone()878.map(|(type_id, component_id)| (*type_id, Some(*component_id))),879));880}881}882883// The map of boolean-valued component presences:884let has_map = build_has_map(885row,886has_paths_and_reflect_components.iter().copied(),887&unregistered_in_has,888);889890let query_row = BrpQueryRow {891entity: row.id(),892components: components_map,893has: has_map,894};895896response.push(query_row);897}898899serde_json::to_value(response).map_err(BrpError::internal)900}901902/// Serializes the specified components for an entity.903/// The iterator yields ([`TypeId`], Option<[`ComponentId`]>).904fn serialize_components(905entity_ref: EntityRef,906type_registry: &TypeRegistry,907components: impl Iterator<Item = (TypeId, Option<ComponentId>)>,908) -> HashMap<String, Value> {909let mut components_map = HashMap::new();910for (type_id, component_id_opt) in components {911let Some(type_registration) = type_registry.get(type_id) else {912continue;913};914if let Some(reflect_component) = type_registration.data::<ReflectComponent>() {915// If a component_id is provided, check if the entity has it916if let Some(component_id) = component_id_opt917&& !entity_ref.contains_id(component_id)918{919continue;920}921if let Some(reflected) = reflect_component.reflect(entity_ref) {922let reflect_serializer =923ReflectSerializer::new(reflected.as_partial_reflect(), type_registry);924if let Ok(Value::Object(obj)) = serde_json::to_value(&reflect_serializer) {925components_map.extend(obj);926} else {927warn_once!(928"Failed to serialize component `{}` for entity {:?}",929type_registration.type_info().type_path(),930entity_ref.id()931);932}933}934}935}936components_map937}938939/// Handles a `world.spawn_entity` request coming from a client.940pub fn process_remote_spawn_entity_request(941In(params): In<Option<Value>>,942world: &mut World,943) -> BrpResult {944let BrpSpawnEntityParams { components } = parse_some(params)?;945946let app_type_registry = world.resource::<AppTypeRegistry>().clone();947let type_registry = app_type_registry.read();948949let reflect_components =950deserialize_components(&type_registry, components).map_err(BrpError::component_error)?;951952let entity = world.spawn_empty();953let entity_id = entity.id();954insert_reflected_components(&type_registry, entity, reflect_components)955.map_err(BrpError::component_error)?;956957let response = BrpSpawnEntityResponse { entity: entity_id };958serde_json::to_value(response).map_err(BrpError::internal)959}960961/// Handles a `rpc.discover` request coming from a client.962pub fn process_remote_list_methods_request(963In(_params): In<Option<Value>>,964world: &mut World,965) -> BrpResult {966let remote_methods = world.resource::<crate::RemoteMethods>();967968#[cfg(all(feature = "http", not(target_family = "wasm")))]969let servers = match (970world.get_resource::<crate::http::HostAddress>(),971world.get_resource::<crate::http::HostPort>(),972) {973(Some(url), Some(port)) => Some(vec![ServerObject {974name: "Server".to_owned(),975url: format!("{}:{}", url.0, port.0),976..default()977}]),978(Some(url), None) => Some(vec![ServerObject {979name: "Server".to_owned(),980url: url.0.to_string(),981..default()982}]),983_ => None,984};985986#[cfg(any(not(feature = "http"), target_family = "wasm"))]987let servers = None;988989let doc = OpenRpcDocument {990info: Default::default(),991methods: remote_methods.into(),992openrpc: "1.3.2".to_owned(),993servers,994};995996serde_json::to_value(doc).map_err(BrpError::internal)997}998999/// Handles a `world.insert_components` request (insert components) coming from a client.1000pub fn process_remote_insert_components_request(1001In(params): In<Option<Value>>,1002world: &mut World,1003) -> BrpResult {1004let BrpInsertComponentsParams { entity, components } = parse_some(params)?;10051006let app_type_registry = world.resource::<AppTypeRegistry>().clone();1007let type_registry = app_type_registry.read();10081009let reflect_components =1010deserialize_components(&type_registry, components).map_err(BrpError::component_error)?;10111012insert_reflected_components(1013&type_registry,1014get_entity_mut(world, entity)?,1015reflect_components,1016)1017.map_err(BrpError::component_error)?;10181019Ok(Value::Null)1020}10211022/// Handles a `world.insert_resources` request coming from a client.1023pub fn process_remote_insert_resources_request(1024In(params): In<Option<Value>>,1025world: &mut World,1026) -> BrpResult {1027let BrpInsertResourcesParams {1028resource: resource_path,1029value,1030} = parse_some(params)?;10311032let app_type_registry = world.resource::<AppTypeRegistry>().clone();1033let type_registry = app_type_registry.read();10341035let reflected_resource = deserialize_resource(&type_registry, &resource_path, value)1036.map_err(BrpError::resource_error)?;10371038let reflect_resource =1039get_reflect_resource(&type_registry, &resource_path).map_err(BrpError::resource_error)?;1040reflect_resource.insert(world, &*reflected_resource, &type_registry);10411042Ok(Value::Null)1043}10441045/// Handles a `world.mutate_components` request coming from a client.1046///1047/// This method allows you to mutate a single field inside an Entity's1048/// component.1049pub fn process_remote_mutate_components_request(1050In(params): In<Option<Value>>,1051world: &mut World,1052) -> BrpResult {1053let BrpMutateComponentsParams {1054entity,1055component,1056path,1057value,1058} = parse_some(params)?;1059let app_type_registry = world.resource::<AppTypeRegistry>().clone();1060let type_registry = app_type_registry.read();10611062// Get the fully-qualified type names of the component to be mutated.1063let component_type: &TypeRegistration = type_registry1064.get_with_type_path(&component)1065.ok_or_else(|| {1066BrpError::component_error(anyhow!("Unknown component type: `{}`", component))1067})?;10681069// Get the reflected representation of the component.1070let mut reflected = component_type1071.data::<ReflectComponent>()1072.ok_or_else(|| {1073BrpError::component_error(anyhow!("Component `{}` isn't registered", component))1074})?1075.reflect_mut(world.entity_mut(entity))1076.ok_or_else(|| {1077BrpError::component_error(anyhow!("Cannot reflect component `{}`", component))1078})?;10791080// Get the type of the field in the component that is to be1081// mutated.1082let value_type: &TypeRegistration = type_registry1083.get_with_type_path(1084reflected1085.reflect_path(path.as_str())1086.map_err(BrpError::component_error)?1087.reflect_type_path(),1088)1089.ok_or_else(|| {1090BrpError::component_error(anyhow!("Unknown component field type: `{}`", component))1091})?;10921093// Get the reflected representation of the value to be inserted1094// into the component.1095let value: Box<dyn PartialReflect> = TypedReflectDeserializer::new(value_type, &type_registry)1096.deserialize(&value)1097.map_err(BrpError::component_error)?;10981099// Apply the mutation.1100reflected1101.reflect_path_mut(path.as_str())1102.map_err(BrpError::component_error)?1103.try_apply(value.as_ref())1104.map_err(BrpError::component_error)?;11051106Ok(Value::Null)1107}11081109/// Handles a `world.mutate_resources` request coming from a client.1110pub fn process_remote_mutate_resources_request(1111In(params): In<Option<Value>>,1112world: &mut World,1113) -> BrpResult {1114let BrpMutateResourcesParams {1115resource: resource_path,1116path: field_path,1117value,1118} = parse_some(params)?;11191120let app_type_registry = world.resource::<AppTypeRegistry>().clone();1121let type_registry = app_type_registry.read();11221123// Get the `ReflectResource` for the given resource path.1124let reflect_resource =1125get_reflect_resource(&type_registry, &resource_path).map_err(BrpError::resource_error)?;11261127// Get the actual resource value from the world as a `dyn Reflect`.1128let mut reflected_resource = reflect_resource1129.reflect_mut(world)1130.map_err(|_| BrpError::resource_not_present(&resource_path))?;11311132// Get the type registration for the field with the given path.1133let value_registration = type_registry1134.get_with_type_path(1135reflected_resource1136.reflect_path(field_path.as_str())1137.map_err(BrpError::resource_error)?1138.reflect_type_path(),1139)1140.ok_or_else(|| {1141BrpError::resource_error(anyhow!("Unknown resource field type: `{}`", resource_path))1142})?;11431144// Use the field's type registration to deserialize the given value.1145let deserialized_value: Box<dyn PartialReflect> =1146TypedReflectDeserializer::new(value_registration, &type_registry)1147.deserialize(&value)1148.map_err(BrpError::resource_error)?;11491150// Apply the value to the resource.1151reflected_resource1152.reflect_path_mut(field_path.as_str())1153.map_err(BrpError::resource_error)?1154.try_apply(&*deserialized_value)1155.map_err(BrpError::resource_error)?;11561157Ok(Value::Null)1158}11591160/// Handles a `world.remove_components` request (remove components) coming from a client.1161pub fn process_remote_remove_components_request(1162In(params): In<Option<Value>>,1163world: &mut World,1164) -> BrpResult {1165let BrpRemoveComponentsParams { entity, components } = parse_some(params)?;11661167let app_type_registry = world.resource::<AppTypeRegistry>().clone();1168let type_registry = app_type_registry.read();11691170let component_ids = get_component_ids(&type_registry, world, components, true)1171.and_then(|(registered, unregistered)| {1172if unregistered.is_empty() {1173Ok(registered)1174} else {1175Err(anyhow!("Unregistered component types: {:?}", unregistered))1176}1177})1178.map_err(BrpError::component_error)?;11791180// Remove the components.1181let mut entity_world_mut = get_entity_mut(world, entity)?;1182for (_, component_id) in component_ids.iter() {1183entity_world_mut.remove_by_id(*component_id);1184}11851186Ok(Value::Null)1187}11881189/// Handles a `world.remove_resources` request coming from a client.1190pub fn process_remote_remove_resources_request(1191In(params): In<Option<Value>>,1192world: &mut World,1193) -> BrpResult {1194let BrpRemoveResourcesParams {1195resource: resource_path,1196} = parse_some(params)?;11971198let app_type_registry = world.resource::<AppTypeRegistry>().clone();1199let type_registry = app_type_registry.read();12001201let reflect_resource =1202get_reflect_resource(&type_registry, &resource_path).map_err(BrpError::resource_error)?;1203reflect_resource.remove(world);12041205Ok(Value::Null)1206}12071208/// Handles a `world.despawn_entity` (despawn entity) request coming from a client.1209pub fn process_remote_despawn_entity_request(1210In(params): In<Option<Value>>,1211world: &mut World,1212) -> BrpResult {1213let BrpDespawnEntityParams { entity } = parse_some(params)?;12141215get_entity_mut(world, entity)?.despawn();12161217Ok(Value::Null)1218}12191220/// Handles a `world.reparent_entities` request coming from a client.1221pub fn process_remote_reparent_entities_request(1222In(params): In<Option<Value>>,1223world: &mut World,1224) -> BrpResult {1225let BrpReparentEntitiesParams {1226entities,1227parent: maybe_parent,1228} = parse_some(params)?;12291230// If `Some`, reparent the entities.1231if let Some(parent) = maybe_parent {1232let mut parent_commands =1233get_entity_mut(world, parent).map_err(|_| BrpError::entity_not_found(parent))?;1234for entity in entities {1235if entity == parent {1236return Err(BrpError::self_reparent(entity));1237}1238parent_commands.add_child(entity);1239}1240}1241// If `None`, remove the entities' parents.1242else {1243for entity in entities {1244get_entity_mut(world, entity)?.remove::<ChildOf>();1245}1246}12471248Ok(Value::Null)1249}12501251/// Handles a `world.list_components` request (list all components) coming from a client.1252pub fn process_remote_list_components_request(1253In(params): In<Option<Value>>,1254world: &World,1255) -> BrpResult {1256let app_type_registry = world.resource::<AppTypeRegistry>();1257let type_registry = app_type_registry.read();12581259let mut response = BrpListComponentsResponse::default();12601261// If `Some`, return all components of the provided entity.1262if let Some(BrpListComponentsParams { entity }) = params.map(parse).transpose()? {1263let entity = get_entity(world, entity)?;1264for component_id in entity.archetype().components() {1265let Some(component_info) = world.components().get_info(component_id) else {1266continue;1267};1268response.push(component_info.name().to_string());1269}1270}1271// If `None`, list all registered components.1272else {1273for registered_type in type_registry.iter() {1274if registered_type.data::<ReflectComponent>().is_some() {1275response.push(registered_type.type_info().type_path().to_owned());1276}1277}1278}12791280// Sort both for cleanliness and to reduce the risk that clients start1281// accidentally depending on the order.1282response.sort();12831284serde_json::to_value(response).map_err(BrpError::internal)1285}12861287/// Handles a `world.list_resources` request coming from a client.1288pub fn process_remote_list_resources_request(1289In(_params): In<Option<Value>>,1290world: &World,1291) -> BrpResult {1292let mut response = BrpListResourcesResponse::default();12931294let app_type_registry = world.resource::<AppTypeRegistry>();1295let type_registry = app_type_registry.read();12961297for registered_type in type_registry.iter() {1298if registered_type.data::<ReflectResource>().is_some() {1299response.push(registered_type.type_info().type_path().to_owned());1300}1301}13021303response.sort();13041305serde_json::to_value(response).map_err(BrpError::internal)1306}13071308/// Handles a `world.list_components+watch` request coming from a client.1309pub fn process_remote_list_components_watching_request(1310In(params): In<Option<Value>>,1311world: &World,1312mut removal_cursors: Local<HashMap<ComponentId, EventCursor<RemovedComponentEntity>>>,1313) -> BrpResult<Option<Value>> {1314let BrpListComponentsParams { entity } = parse_some(params)?;1315let entity_ref = get_entity(world, entity)?;1316let mut response = BrpListComponentsWatchingResponse::default();13171318for component_id in entity_ref.archetype().components() {1319let ticks = entity_ref1320.get_change_ticks_by_id(component_id)1321.ok_or(BrpError::internal("Failed to get ticks"))?;13221323if ticks.is_added(world.last_change_tick(), world.read_change_tick()) {1324let Some(component_info) = world.components().get_info(component_id) else {1325continue;1326};1327response.added.push(component_info.name().to_string());1328}1329}13301331for (component_id, events) in world.removed_components().iter() {1332let cursor = removal_cursors1333.entry(*component_id)1334.or_insert_with(|| events.get_cursor());1335for event in cursor.read(events) {1336if Entity::from(event.clone()) == entity {1337let Some(component_info) = world.components().get_info(*component_id) else {1338continue;1339};1340response.removed.push(component_info.name().to_string());1341}1342}1343}13441345if response.added.is_empty() && response.removed.is_empty() {1346Ok(None)1347} else {1348Ok(Some(1349serde_json::to_value(response).map_err(BrpError::internal)?,1350))1351}1352}13531354/// Handles a `registry.schema` request (list all registry types in form of schema) coming from a client.1355pub fn export_registry_types(In(params): In<Option<Value>>, world: &World) -> BrpResult {1356let filter: BrpJsonSchemaQueryFilter = match params {1357None => Default::default(),1358Some(params) => parse(params)?,1359};13601361let extra_info = world.resource::<crate::schemas::SchemaTypesMetadata>();1362let types = world.resource::<AppTypeRegistry>();1363let types = types.read();1364let schemas = types1365.iter()1366.filter_map(|type_reg| {1367let path_table = type_reg.type_info().type_path_table();1368if let Some(crate_name) = &path_table.crate_name() {1369if !filter.with_crates.is_empty()1370&& !filter.with_crates.iter().any(|c| crate_name.eq(c))1371{1372return None;1373}1374if !filter.without_crates.is_empty()1375&& filter.without_crates.iter().any(|c| crate_name.eq(c))1376{1377return None;1378}1379}1380let (id, schema) = export_type(type_reg, extra_info);13811382if !filter.type_limit.with.is_empty()1383&& !filter1384.type_limit1385.with1386.iter()1387.any(|c| schema.reflect_types.iter().any(|cc| c.eq(cc)))1388{1389return None;1390}1391if !filter.type_limit.without.is_empty()1392&& filter1393.type_limit1394.without1395.iter()1396.any(|c| schema.reflect_types.iter().any(|cc| c.eq(cc)))1397{1398return None;1399}1400Some((id.to_string(), schema))1401})1402.collect::<HashMap<String, JsonSchemaBevyType>>();14031404serde_json::to_value(schemas).map_err(BrpError::internal)1405}14061407/// Immutably retrieves an entity from the [`World`], returning an error if the1408/// entity isn't present.1409fn get_entity(world: &World, entity: Entity) -> Result<EntityRef<'_>, BrpError> {1410world1411.get_entity(entity)1412.map_err(|_| BrpError::entity_not_found(entity))1413}14141415/// Mutably retrieves an entity from the [`World`], returning an error if the1416/// entity isn't present.1417fn get_entity_mut(world: &mut World, entity: Entity) -> Result<EntityWorldMut<'_>, BrpError> {1418world1419.get_entity_mut(entity)1420.map_err(|_| BrpError::entity_not_found(entity))1421}14221423/// Given components full path, returns a tuple that contains1424/// - A list of corresponding [`TypeId`] and [`ComponentId`] for registered components.1425/// - A list of unregistered component paths.1426///1427/// Note that the supplied path names must be *full* path names: e.g.1428/// `bevy_transform::components::transform::Transform` instead of `Transform`.1429fn get_component_ids(1430type_registry: &TypeRegistry,1431world: &World,1432component_paths: Vec<String>,1433strict: bool,1434) -> AnyhowResult<(Vec<(TypeId, ComponentId)>, Vec<String>)> {1435let mut component_ids = vec![];1436let mut unregistered_components = vec![];14371438for component_path in component_paths {1439let maybe_component_tuple = get_component_type_registration(type_registry, &component_path)1440.ok()1441.and_then(|type_registration| {1442let type_id = type_registration.type_id();1443world1444.components()1445.get_valid_id(type_id)1446.map(|component_id| (type_id, component_id))1447});1448if let Some((type_id, component_id)) = maybe_component_tuple {1449component_ids.push((type_id, component_id));1450} else if strict {1451return Err(anyhow!(1452"Component `{}` isn't registered or used in the world",1453component_path1454));1455} else {1456unregistered_components.push(component_path);1457}1458}14591460Ok((component_ids, unregistered_components))1461}14621463/// Given an entity (`entity_ref`),1464/// a list of reflected component information (`paths_and_reflect_components`)1465/// and a list of unregistered components,1466/// return a map which associates each component to a boolean value indicating1467/// whether or not that component is present on the entity.1468/// Unregistered components are considered absent from the entity.1469fn build_has_map<'a>(1470entity_ref: FilteredEntityRef,1471paths_and_reflect_components: impl Iterator<Item = (&'a str, &'a ReflectComponent)>,1472unregistered_components: &[String],1473) -> HashMap<String, Value> {1474let mut has_map = <HashMap<_, _>>::default();14751476for (type_path, reflect_component) in paths_and_reflect_components {1477let has = reflect_component.contains(entity_ref);1478has_map.insert(type_path.to_owned(), Value::Bool(has));1479}1480unregistered_components.iter().for_each(|component| {1481has_map.insert(component.to_owned(), Value::Bool(false));1482});14831484has_map1485}14861487/// Given a component ID, return the associated [type path] and `ReflectComponent` if possible.1488///1489/// The `ReflectComponent` part is the meat of this; the type path is only used for error messages.1490///1491/// [type path]: bevy_reflect::TypePath::type_path1492fn reflect_component_from_id(1493component_type_id: TypeId,1494type_registry: &TypeRegistry,1495) -> AnyhowResult<(&str, &ReflectComponent)> {1496let Some(type_registration) = type_registry.get(component_type_id) else {1497return Err(anyhow!(1498"Component `{:?}` isn't registered",1499component_type_id1500));1501};15021503let type_path = type_registration.type_info().type_path();15041505let Some(reflect_component) = type_registration.data::<ReflectComponent>() else {1506return Err(anyhow!("Component `{}` isn't reflectable", type_path));1507};15081509Ok((type_path, reflect_component))1510}15111512/// Given a collection of component paths and their associated serialized values (`components`),1513/// return the associated collection of deserialized reflected values.1514fn deserialize_components(1515type_registry: &TypeRegistry,1516components: HashMap<String, Value>,1517) -> AnyhowResult<Vec<Box<dyn PartialReflect>>> {1518let mut reflect_components = vec![];15191520for (component_path, component) in components {1521let Some(component_type) = type_registry.get_with_type_path(&component_path) else {1522return Err(anyhow!("Unknown component type: `{}`", component_path));1523};1524let reflected: Box<dyn PartialReflect> =1525TypedReflectDeserializer::new(component_type, type_registry)1526.deserialize(&component)1527.map_err(|err| anyhow!("{component_path} is invalid: {err}"))?;1528reflect_components.push(reflected);1529}15301531Ok(reflect_components)1532}15331534/// Given a resource path and an associated serialized value (`value`), return the1535/// deserialized value.1536fn deserialize_resource(1537type_registry: &TypeRegistry,1538resource_path: &str,1539value: Value,1540) -> AnyhowResult<Box<dyn PartialReflect>> {1541let Some(resource_type) = type_registry.get_with_type_path(resource_path) else {1542return Err(anyhow!("Unknown resource type: `{}`", resource_path));1543};1544let reflected: Box<dyn PartialReflect> =1545TypedReflectDeserializer::new(resource_type, type_registry)1546.deserialize(&value)1547.map_err(|err| anyhow!("{resource_path} is invalid: {err}"))?;1548Ok(reflected)1549}15501551/// Given a collection `reflect_components` of reflected component values, insert them into1552/// the given entity (`entity_world_mut`).1553fn insert_reflected_components(1554type_registry: &TypeRegistry,1555mut entity_world_mut: EntityWorldMut,1556reflect_components: Vec<Box<dyn PartialReflect>>,1557) -> AnyhowResult<()> {1558for reflected in reflect_components {1559let reflect_component =1560get_reflect_component(type_registry, reflected.reflect_type_path())?;1561reflect_component.insert(&mut entity_world_mut, &*reflected, type_registry);1562}15631564Ok(())1565}15661567/// Given a component's type path, return the associated [`ReflectComponent`] from the given1568/// `type_registry` if possible.1569fn get_reflect_component<'r>(1570type_registry: &'r TypeRegistry,1571component_path: &str,1572) -> AnyhowResult<&'r ReflectComponent> {1573let component_registration = get_component_type_registration(type_registry, component_path)?;15741575component_registration1576.data::<ReflectComponent>()1577.ok_or_else(|| anyhow!("Component `{}` isn't reflectable", component_path))1578}15791580/// Given a component's type path, return the associated [`TypeRegistration`] from the given1581/// `type_registry` if possible.1582fn get_component_type_registration<'r>(1583type_registry: &'r TypeRegistry,1584component_path: &str,1585) -> AnyhowResult<&'r TypeRegistration> {1586type_registry1587.get_with_type_path(component_path)1588.ok_or_else(|| anyhow!("Unknown component type: `{}`", component_path))1589}15901591/// Given a resource's type path, return the associated [`ReflectResource`] from the given1592/// `type_registry` if possible.1593fn get_reflect_resource<'r>(1594type_registry: &'r TypeRegistry,1595resource_path: &str,1596) -> AnyhowResult<&'r ReflectResource> {1597let resource_registration = get_resource_type_registration(type_registry, resource_path)?;15981599resource_registration1600.data::<ReflectResource>()1601.ok_or_else(|| anyhow!("Resource `{}` isn't reflectable", resource_path))1602}16031604/// Given a resource's type path, return the associated [`TypeRegistration`] from the given1605/// `type_registry` if possible.1606fn get_resource_type_registration<'r>(1607type_registry: &'r TypeRegistry,1608resource_path: &str,1609) -> AnyhowResult<&'r TypeRegistration> {1610type_registry1611.get_with_type_path(resource_path)1612.ok_or_else(|| anyhow!("Unknown resource type: `{}`", resource_path))1613}16141615#[cfg(test)]1616mod tests {1617/// A generic function that tests serialization and deserialization of any type1618/// implementing Serialize and Deserialize traits.1619fn test_serialize_deserialize<T>(value: T)1620where1621T: Serialize + for<'a> Deserialize<'a> + PartialEq + core::fmt::Debug,1622{1623// Serialize the value to JSON string1624let serialized = serde_json::to_string(&value).expect("Failed to serialize");16251626// Deserialize the JSON string back into the original type1627let deserialized: T = serde_json::from_str(&serialized).expect("Failed to deserialize");16281629// Assert that the deserialized value is the same as the original1630assert_eq!(1631&value, &deserialized,1632"Deserialized value does not match original"1633);1634}16351636use super::*;16371638#[test]1639fn serialization_tests() {1640test_serialize_deserialize(BrpQueryRow {1641components: Default::default(),1642entity: Entity::from_raw_u32(0).unwrap(),1643has: Default::default(),1644});1645test_serialize_deserialize(BrpListComponentsWatchingResponse::default());1646test_serialize_deserialize(BrpQuery::default());1647test_serialize_deserialize(BrpJsonSchemaQueryFilter::default());1648test_serialize_deserialize(BrpJsonSchemaQueryFilter {1649type_limit: JsonSchemaTypeLimit {1650with: vec!["Resource".to_owned()],1651..Default::default()1652},1653..Default::default()1654});1655test_serialize_deserialize(BrpListComponentsParams {1656entity: Entity::from_raw_u32(0).unwrap(),1657});1658}1659}166016611662