#[cfg(feature = "bevy-reflection")]
use ::serde::{Deserialize, Serialize};
#[cfg(not(feature = "bevy-reflection"))]
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;
use tracing::{debug, info, warn};
use crate::server::bevy_reflection::inspector::{BevyReflectionInspector, ReflectionMetadata};
use crate::server::brp_messages::{BrpRequest, EntityData, ComponentValue};
use crate::server::brp_client::BrpClient;
use crate::server::error::{Error, Result};
#[derive(Clone)]
pub struct ReflectionQueryEngine {
reflection_inspector: BevyReflectionInspector,
query_cache: Arc<RwLock<HashMap<String, CachedQueryResult>>>,
stats: Arc<RwLock<QueryStats>>,
}
#[derive(Debug, Clone)]
struct CachedQueryResult {
result: ReflectionQueryResult,
cached_at: u64,
ttl_seconds: u64,
hit_count: u64,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct QueryStats {
pub total_queries: u64,
pub cache_hits: u64,
pub reflection_queries: u64,
pub type_discovery_queries: u64,
pub avg_execution_time_ms: f64,
pub complex_queries: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReflectionQueryResult {
pub query: String,
pub entities: Vec<ReflectedEntityData>,
pub metadata: QueryExecutionMetadata,
pub discovered_types: HashMap<String, ReflectionMetadata>,
pub suggestions: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReflectedEntityData {
pub entity: EntityData,
pub reflected_components: HashMap<String, ReflectedComponentData>,
pub entity_metadata: ReflectedEntityMetadata,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReflectedComponentData {
pub type_name: String,
pub raw_value: ComponentValue,
pub inspection: crate::server::bevy_reflection::ReflectionInspectionResult,
pub relationships: Option<ComponentRelationships>,
pub metrics: ComponentMetrics,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReflectedEntityMetadata {
pub archetype_signature: Vec<String>,
pub memory_usage_bytes: usize,
pub component_count: usize,
pub has_hierarchy: bool,
pub has_spatial: bool,
pub has_rendering: bool,
pub classifications: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComponentRelationships {
pub dependents: Vec<String>,
pub dependencies: Vec<String>,
pub conflicts: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComponentMetrics {
pub serialized_size: usize,
pub inspection_time_us: u64,
pub complexity_score: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QueryExecutionMetadata {
pub execution_time_ms: u64,
pub from_cache: bool,
pub entities_processed: usize,
pub components_inspected: usize,
pub types_discovered: usize,
pub complexity_score: f64,
}
#[derive(Debug, Clone)]
pub struct ReflectionQuery {
pub base_query: String,
pub reflection_params: ReflectionQueryParams,
pub limits: QueryLimits,
}
#[derive(Debug, Clone)]
pub struct ReflectionQueryParams {
pub include_reflection: bool,
pub deep_inspection: bool,
pub include_type_discovery: bool,
pub include_relationships: bool,
pub include_metrics: bool,
pub component_type_filters: Vec<String>,
pub field_value_filters: Vec<FieldValueFilter>,
}
#[derive(Debug, Clone)]
pub struct FieldValueFilter {
pub component_type: String,
pub field_path: String,
pub operation: FilterOperation,
pub value: Value,
}
#[derive(Debug, Clone)]
pub enum FilterOperation {
Equals,
NotEquals,
GreaterThan,
LessThan,
Contains,
StartsWith,
EndsWith,
Exists,
NotExists,
}
#[derive(Debug, Clone)]
pub struct QueryLimits {
pub max_entities: usize,
pub max_execution_time_ms: u64,
pub max_components_per_entity: usize,
pub max_memory_usage_bytes: usize,
}
impl ReflectionQueryEngine {
pub fn new() -> Self {
Self {
reflection_inspector: BevyReflectionInspector::new(),
query_cache: Arc::new(RwLock::new(HashMap::new())),
stats: Arc::new(RwLock::new(QueryStats::default())),
}
}
pub async fn execute_query(
&self,
query: ReflectionQuery,
brp_client: Arc<RwLock<BrpClient>>,
) -> Result<ReflectionQueryResult> {
let start_time = std::time::Instant::now();
debug!("Executing reflection query: {}", query.base_query);
if let Some(cached_result) = self.get_cached_result(&query.base_query).await {
info!("Query cache hit for: {}", query.base_query);
self.update_stats(true, start_time.elapsed().as_millis() as u64).await;
return Ok(cached_result);
}
let base_entities = self.execute_base_query(&query.base_query, brp_client).await?;
let reflected_entities = self.enhance_entities_with_reflection(
base_entities,
&query.reflection_params,
&query.limits,
).await?;
let discovered_types = if query.reflection_params.include_type_discovery {
self.discover_types_from_entities(&reflected_entities).await?
} else {
HashMap::new()
};
let suggestions = self.generate_query_suggestions(&query, &reflected_entities).await;
let execution_time = start_time.elapsed().as_millis() as u64;
let result = ReflectionQueryResult {
query: query.base_query.clone(),
entities: reflected_entities.clone(),
metadata: QueryExecutionMetadata {
execution_time_ms: execution_time,
from_cache: false,
entities_processed: reflected_entities.len(),
components_inspected: reflected_entities.iter()
.map(|e| e.reflected_components.len())
.sum(),
types_discovered: discovered_types.len(),
complexity_score: self.calculate_query_complexity(&query).await,
},
discovered_types,
suggestions,
};
self.cache_result(&query.base_query, &result).await;
self.update_stats(false, execution_time).await;
Ok(result)
}
async fn execute_base_query(
&self,
query: &str,
brp_client: Arc<RwLock<BrpClient>>,
) -> Result<Vec<EntityData>> {
let mut client = brp_client.write().await;
let brp_request = self.parse_query_to_brp(query)?;
let response = client.send_request(&brp_request).await?;
match response.payload {
crate::server::brp_messages::BrpPayload::Result(value) => {
Ok(crate::server::brp_messages::entity_data_from_query_result(&value)
.unwrap_or_default())
}
crate::server::brp_messages::BrpPayload::Error(err) => {
Err(Error::Brp(format!("BRP query failed: {}", err.message)))
}
}
}
fn parse_query_to_brp(&self, query: &str) -> Result<BrpRequest> {
let _ = query;
Ok(BrpRequest {
method: crate::server::brp_messages::builtin_methods::BRP_QUERY_METHOD.to_string(),
id: None,
params: Some(crate::server::brp_messages::query_params(None, true)),
})
}
async fn enhance_entities_with_reflection(
&self,
entities: Vec<EntityData>,
params: &ReflectionQueryParams,
limits: &QueryLimits,
) -> Result<Vec<ReflectedEntityData>> {
let mut reflected_entities = Vec::new();
for (processed_count, entity) in entities.into_iter().enumerate() {
if processed_count >= limits.max_entities {
warn!("Query limit reached: max_entities = {}", limits.max_entities);
break;
}
let reflected_entity = self.enhance_single_entity(entity, params, limits).await?;
reflected_entities.push(reflected_entity);
}
Ok(reflected_entities)
}
async fn enhance_single_entity(
&self,
entity: EntityData,
params: &ReflectionQueryParams,
limits: &QueryLimits,
) -> Result<ReflectedEntityData> {
let mut reflected_components = HashMap::new();
let mut component_count = 0;
for (component_type, component_value) in &entity.components {
if component_count >= limits.max_components_per_entity {
break;
}
if !params.component_type_filters.is_empty() {
let matches_filter = params.component_type_filters.iter()
.any(|filter| component_type.contains(filter));
if !matches_filter {
continue;
}
}
let start_inspection = std::time::Instant::now();
let inspection = if params.include_reflection {
Some(self.reflection_inspector.inspect_component(component_type, component_value).await?)
} else {
None
};
let inspection_time = start_inspection.elapsed().as_micros() as u64;
let metrics = ComponentMetrics {
serialized_size: serde_json::to_string(component_value)
.map(|s| s.len())
.unwrap_or(0),
inspection_time_us: inspection_time,
complexity_score: self.calculate_component_complexity(component_value).await,
};
let relationships = if params.include_relationships {
Some(self.analyze_component_relationships(component_type, &entity).await)
} else {
None
};
if let Some(inspection) = inspection {
reflected_components.insert(component_type.clone(), ReflectedComponentData {
type_name: component_type.clone(),
raw_value: component_value.clone(),
inspection,
relationships,
metrics,
});
}
component_count += 1;
}
let entity_metadata = self.build_entity_metadata(&entity, &reflected_components).await;
Ok(ReflectedEntityData {
entity,
reflected_components,
entity_metadata,
})
}
async fn analyze_component_relationships(
&self,
component_type: &str,
entity: &EntityData,
) -> ComponentRelationships {
let mut dependents = Vec::new();
let dependencies = Vec::new();
let conflicts = Vec::new();
match component_type {
"bevy_transform::components::transform::Transform" => {
if entity.components.contains_key("bevy_transform::components::global_transform::GlobalTransform") {
dependents.push("bevy_transform::components::global_transform::GlobalTransform".to_string());
}
for comp_type in entity.components.keys() {
if comp_type.contains("sprite") || comp_type.contains("mesh") {
dependents.push(comp_type.clone());
}
}
}
"bevy_hierarchy::components::parent::Parent" => {}
_ => {}
}
ComponentRelationships {
dependents,
dependencies,
conflicts,
}
}
async fn build_entity_metadata(
&self,
entity: &EntityData,
reflected_components: &HashMap<String, ReflectedComponentData>,
) -> ReflectedEntityMetadata {
let archetype_signature: Vec<String> = entity.components.keys().cloned().collect();
let memory_usage_bytes = reflected_components.values()
.map(|comp| comp.metrics.serialized_size)
.sum();
let has_hierarchy = entity.components.keys()
.any(|k| k.contains("parent") || k.contains("children") || k.contains("Parent") || k.contains("Children"));
let has_spatial = entity.components.keys()
.any(|k| k.contains("transform") || k.contains("Transform"));
let has_rendering = entity.components.keys()
.any(|k| k.contains("sprite") || k.contains("mesh") || k.contains("material"));
let mut classifications = Vec::new();
if has_hierarchy { classifications.push("Hierarchical".to_string()); }
if has_spatial { classifications.push("Spatial".to_string()); }
if has_rendering { classifications.push("Renderable".to_string()); }
ReflectedEntityMetadata {
archetype_signature,
memory_usage_bytes,
component_count: entity.components.len(),
has_hierarchy,
has_spatial,
has_rendering,
classifications,
}
}
async fn calculate_component_complexity(&self, component_value: &ComponentValue) -> f64 {
let mut complexity;
match component_value {
Value::Null => complexity = 0.1,
Value::Bool(_) => complexity = 0.2,
Value::Number(_) => complexity = 0.3,
Value::String(s) => complexity = 0.4 + (s.len() as f64 / 1000.0).min(0.3),
Value::Array(arr) => {
complexity = 0.6 + (arr.len() as f64 / 100.0).min(0.3);
for item in arr.iter().take(10) { complexity += Box::pin(self.calculate_component_complexity(item)).await * 0.1;
}
}
Value::Object(obj) => {
complexity = 0.7 + (obj.len() as f64 / 50.0).min(0.2);
for value in obj.values().take(10) { complexity += Box::pin(self.calculate_component_complexity(value)).await * 0.1;
}
}
}
complexity.clamp(0.0_f64, 1.0_f64)
}
async fn discover_types_from_entities(
&self,
entities: &[ReflectedEntityData],
) -> Result<HashMap<String, ReflectionMetadata>> {
let mut discovered_types = HashMap::new();
for entity in entities {
for comp_data in entity.reflected_components.values() {
if !discovered_types.contains_key(&comp_data.type_name) {
discovered_types.insert(
comp_data.type_name.clone(),
comp_data.inspection.metadata.clone(),
);
}
}
}
Ok(discovered_types)
}
async fn generate_query_suggestions(
&self,
query: &ReflectionQuery,
entities: &[ReflectedEntityData],
) -> Vec<String> {
let mut suggestions = Vec::new();
if entities.is_empty() {
suggestions.push("No entities matched your query. Try broadening the search criteria.".to_string());
} else if entities.len() > 100 {
suggestions.push("Large result set returned. Consider adding filters to narrow results.".to_string());
}
if !query.reflection_params.include_reflection {
suggestions.push("Enable reflection inspection to get detailed component analysis.".to_string());
}
if !query.reflection_params.include_type_discovery {
suggestions.push("Enable type discovery to learn about available component types.".to_string());
}
suggestions
}
async fn calculate_query_complexity(&self, query: &ReflectionQuery) -> f64 {
let mut complexity = 0.3_f64;
if query.reflection_params.include_reflection {
complexity += 0.3;
}
if query.reflection_params.deep_inspection {
complexity += 0.2;
}
if query.reflection_params.include_relationships {
complexity += 0.2;
}
complexity.clamp(0.0_f64, 1.0_f64)
}
async fn get_cached_result(&self, query: &str) -> Option<ReflectionQueryResult> {
let mut cache = self.query_cache.write().await;
if let Some(cached) = cache.get_mut(query) {
let now = chrono::Utc::now().timestamp_micros() as u64;
if now - cached.cached_at < cached.ttl_seconds * 1_000_000 {
cached.hit_count += 1;
return Some(cached.result.clone());
} else {
cache.remove(query);
}
}
None
}
async fn cache_result(&self, query: &str, result: &ReflectionQueryResult) {
let mut cache = self.query_cache.write().await;
if cache.len() > 1000 {
let cutoff = chrono::Utc::now().timestamp_micros() as u64 - 3600 * 1_000_000; cache.retain(|_, cached| cached.cached_at > cutoff);
}
cache.insert(query.to_string(), CachedQueryResult {
result: result.clone(),
cached_at: chrono::Utc::now().timestamp_micros() as u64,
ttl_seconds: 300, hit_count: 0,
});
}
async fn update_stats(&self, cache_hit: bool, execution_time_ms: u64) {
let mut stats = self.stats.write().await;
stats.total_queries += 1;
if cache_hit {
stats.cache_hits += 1;
}
let total_time = stats.avg_execution_time_ms * (stats.total_queries - 1) as f64;
stats.avg_execution_time_ms = (total_time + execution_time_ms as f64) / stats.total_queries as f64;
}
pub async fn get_stats(&self) -> QueryStats {
let stats = self.stats.read().await;
(*stats).clone()
}
pub async fn clear_cache(&self) {
let mut cache = self.query_cache.write().await;
cache.clear();
info!("Cleared reflection query cache");
}
}
impl Default for ReflectionQueryEngine {
fn default() -> Self {
Self::new()
}
}
impl Default for ReflectionQueryParams {
fn default() -> Self {
Self {
include_reflection: true,
deep_inspection: false,
include_type_discovery: false,
include_relationships: false,
include_metrics: true,
component_type_filters: Vec::new(),
field_value_filters: Vec::new(),
}
}
}
impl Default for QueryLimits {
fn default() -> Self {
Self {
max_entities: 1000,
max_execution_time_ms: 30000, max_components_per_entity: 50,
max_memory_usage_bytes: 100 * 1024 * 1024, }
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[tokio::test]
async fn test_reflection_query_engine_creation() {
let engine = ReflectionQueryEngine::new();
let stats = engine.get_stats().await;
assert_eq!(stats.total_queries, 0);
assert_eq!(stats.cache_hits, 0);
}
#[tokio::test]
async fn test_component_complexity_calculation() {
let engine = ReflectionQueryEngine::new();
assert!(engine.calculate_component_complexity(&json!(42)).await < 0.5);
assert!(engine.calculate_component_complexity(&json!("hello")).await < 0.5);
let complex_obj = json!({
"field1": "value1",
"field2": {"nested": "value"},
"field3": [1, 2, 3, 4, 5]
});
assert!(engine.calculate_component_complexity(&complex_obj).await > 0.7);
}
#[test]
fn test_filter_operation() {
let filter = FieldValueFilter {
component_type: "Transform".to_string(),
field_path: "translation.x".to_string(),
operation: FilterOperation::GreaterThan,
value: json!(10.0),
};
assert_eq!(filter.component_type, "Transform");
assert_eq!(filter.field_path, "translation.x");
}
#[test]
fn test_query_limits() {
let limits = QueryLimits::default();
assert_eq!(limits.max_entities, 1000);
assert_eq!(limits.max_execution_time_ms, 30000);
}
}