use anyhow::{Result, anyhow, bail};
use bevy_ecs::{
component::ComponentId, prelude::*, query::FilteredAccess, system::QueryParamBuilder,
world::FilteredEntityMut,
};
use crate::{
bindings::wasvy::ecs::app::{ComponentIndex, QueryFor},
component::{ComponentRef, get_component, set_component},
serialize::CodecResource,
system::Param,
};
pub(crate) type Queries<'w, 's> =
ParamSet<'w, 's, Vec<Query<'static, 'static, FilteredEntityMut<'static, 'static>>>>;
pub(crate) struct QueryResolver(Vec<Vec<QueryForComponent>>);
impl QueryResolver {
pub(crate) fn new(params: &[Param], world: &mut World) -> Result<Self> {
let mut result = Vec::new();
for component in params.iter().filter_map(|param| Param::filter_query(param)) {
let mut components = Vec::new();
for original in component {
if let Some(component) = QueryForComponent::new(original, world)? {
components.push(component);
}
}
result.push(components);
}
Ok(Self(result))
}
pub(crate) fn get(
&self,
id: QueryId,
entity: Entity,
index: ComponentIndex,
queries: &mut Queries<'_, '_>,
type_registry: &AppTypeRegistry,
codec: &CodecResource,
) -> Result<Vec<u8>> {
let query_for = self.query_for(id, index)?;
let query = queries.get_mut(id.0);
let entity = query.get(entity)?;
get_component(&entity, &query_for.component, type_registry, codec)
}
pub(crate) fn set(
&self,
id: QueryId,
entity: Entity,
index: ComponentIndex,
serialized_value: Vec<u8>,
queries: &mut Queries<'_, '_>,
type_registry: &AppTypeRegistry,
codec: &CodecResource,
) -> Result<()> {
let query_for = self.query_for(id, index)?;
if !query_for.mutable {
bail!("Component is not mutable!")
}
let mut query = queries.get_mut(id.0);
let mut entity = query.get_mut(entity)?;
set_component(
&mut entity,
&query_for.component,
serialized_value,
type_registry,
codec,
)
}
pub(crate) fn query_for(
&self,
id: QueryId,
index: ComponentIndex,
) -> Result<&QueryForComponent> {
let id = id.0;
self.0
.get(id)
.expect("Valid query index")
.get(index as usize)
.ok_or_else(|| anyhow!("Query index {id} does not have component index {index}"))
}
}
#[derive(Default)]
pub(crate) struct QueryIdGenerator(usize);
impl QueryIdGenerator {
pub(crate) fn generate(&mut self) -> QueryId {
let index = self.0;
self.0 += 1;
QueryId(index)
}
}
#[derive(Clone, Copy)]
pub(crate) struct QueryId(usize);
impl QueryId {
pub(crate) fn index(self) -> usize {
self.0
}
}
#[derive(Default, Clone, Copy)]
pub(crate) struct QueryCursor(usize);
impl QueryCursor {
pub(crate) fn increment(&mut self) -> Self {
let original = *self;
self.0 += 1;
original
}
pub(crate) fn entity(&self, queries: &mut Queries<'_, '_>, id: QueryId) -> Option<Entity> {
let query = queries.get_mut(id.0);
query.iter().nth(self.0).map(|a| a.id())
}
}
#[derive(Clone)]
pub(crate) struct QueryForComponent {
component: ComponentRef,
mutable: bool,
}
impl QueryForComponent {
fn new(original: &QueryFor, world: &mut World) -> Result<Option<Self>> {
Ok(match original {
QueryFor::Ref(type_path) => Some(Self {
component: ComponentRef::new(type_path, world)?,
mutable: false,
}),
QueryFor::Mut(type_path) => Some(Self {
component: ComponentRef::new(type_path, world)?,
mutable: true,
}),
QueryFor::With(_) => None,
QueryFor::Without(_) => None,
})
}
pub(crate) fn component(&self) -> &ComponentRef {
&self.component
}
pub(crate) fn mutable(&self) -> bool {
self.mutable
}
}
pub(crate) fn create_query_builder(
original_items: &[QueryFor],
world: &mut World,
access: FilteredAccess,
) -> Result<
QueryParamBuilder<Box<dyn FnOnce(&mut QueryBuilder<FilteredEntityMut<'static, 'static>>)>>,
> {
let mut items = Vec::with_capacity(original_items.len());
for original in original_items {
items.push(QueryForId::new(original, world)?);
}
Ok(QueryParamBuilder::new_box(move |builder| {
builder.extend_access(access);
for item in items {
match item {
QueryForId::Ref(component_id) => {
builder.ref_id(component_id);
}
QueryForId::Mut(component_id) => {
builder.mut_id(component_id);
}
QueryForId::With(component_id) => {
builder.with_id(component_id);
}
QueryForId::Without(component_id) => {
builder.without_id(component_id);
}
}
}
}))
}
enum QueryForId {
Ref(ComponentId),
Mut(ComponentId),
With(ComponentId),
Without(ComponentId),
}
impl QueryForId {
fn new(original: &QueryFor, world: &mut World) -> Result<Self> {
Ok(match original {
QueryFor::Ref(type_path) => {
Self::Ref(ComponentRef::new(type_path, world)?.component_id())
}
QueryFor::Mut(type_path) => {
Self::Mut(ComponentRef::new(type_path, world)?.component_id())
}
QueryFor::With(type_path) => {
Self::With(ComponentRef::new(type_path, world)?.component_id())
}
QueryFor::Without(type_path) => {
Self::Without(ComponentRef::new(type_path, world)?.component_id())
}
})
}
}