use anyhow::{Result, bail};
use bevy_ecs::prelude::*;
use wasmtime::component::Resource;
use crate::{
bindings::wasvy::ecs::app::{ComponentIndex, HostComponent, SerializedComponent},
component::{with_component_mut, with_component_ref},
host::WasmHost,
methods::MethodTarget,
query::QueryId,
runner::State,
};
pub struct WasmComponent {
index: ComponentIndex,
id: QueryId,
entity: Entity,
}
impl WasmComponent {
pub(crate) fn new(index: ComponentIndex, id: QueryId, entity: Entity) -> Self {
Self { index, id, entity }
}
}
impl HostComponent for WasmHost {
fn get(&mut self, component: Resource<WasmComponent>) -> Result<SerializedComponent> {
let State::RunSystem {
table,
queries,
query_resolver,
type_registry,
codec,
..
} = self.access()
else {
bail!("Component can only be accessed in systems")
};
let component = table.get(&component)?;
query_resolver.get(
component.id,
component.entity,
component.index,
queries,
type_registry,
codec,
)
}
fn set(
&mut self,
component: Resource<WasmComponent>,
value: SerializedComponent,
) -> Result<()> {
let State::RunSystem {
table,
queries,
query_resolver,
type_registry,
codec,
..
} = self.access()
else {
bail!("Component can only be accessed in systems")
};
let component = table.get(&component)?;
query_resolver.set(
component.id,
component.entity,
component.index,
value,
queries,
type_registry,
codec,
)
}
fn drop(&mut self, component: Resource<WasmComponent>) -> Result<()> {
let _ = self.table().delete(component)?;
Ok(())
}
fn invoke(
&mut self,
component: Resource<WasmComponent>,
method: String,
params: SerializedComponent,
) -> Result<SerializedComponent> {
invoke_component_method(self, component, &method, ¶ms)
}
}
pub fn invoke_component_method(
host: &mut WasmHost,
component: Resource<WasmComponent>,
method: &str,
params: &[u8],
) -> Result<SerializedComponent> {
let State::RunSystem {
table,
queries,
query_resolver,
type_registry,
codec,
function_index,
..
} = host.access()
else {
bail!("Component can only be accessed in systems")
};
let component = table.get(&component)?;
let query_for = query_resolver.query_for(component.id, component.index)?;
let component_ref = query_for.component();
let mut query = queries.get_mut(component.id.index());
let output = if query_for.mutable() {
let mut entity = query.get_mut(component.entity)?;
with_component_mut(&mut entity, component_ref, type_registry, |reflect| {
function_index.invoke(
component_ref.type_path(),
method,
MethodTarget::Write(reflect),
params,
type_registry,
codec,
)
})?
} else {
let entity = query.get(component.entity)?;
with_component_ref(&entity, component_ref, type_registry, |reflect| {
function_index.invoke(
component_ref.type_path(),
method,
MethodTarget::Read(reflect),
params,
type_registry,
codec,
)
})?
};
Ok(output)
}