use std::sync::Arc;
use bevy::{
ecs::{component::ComponentId, entity::Entity, world::World},
math::{Vec2, Vec3},
};
use pybevy_core::{
ValueStorage,
storage_traits::{BorrowableStorage, FromBorrowedStorage},
};
use pybevy_math::{vec2::PyVec2, vec3::PyVec3};
use pyo3::{
exceptions::{PyAttributeError, PyRuntimeError},
prelude::*,
types::PyType,
};
use super::{
component_layout::{ComponentLayout, PrimitiveType},
helpers::validity_guard::ValidityFlagWithMode,
};
#[pyclass(name = "LazyWrapperProxy")]
pub struct PyLazyWrapperProxy {
data_ptr: *mut u8,
layout: Arc<ComponentLayout>,
py_type: *const pyo3::ffi::PyTypeObject,
validity: ValidityFlagWithMode,
mutable: bool,
component_id: ComponentId,
entity: Entity,
world_ptr: *mut World,
}
unsafe impl Send for PyLazyWrapperProxy {}
unsafe impl Sync for PyLazyWrapperProxy {}
impl PyLazyWrapperProxy {
pub unsafe fn new(
data_ptr: *mut u8,
layout: Arc<ComponentLayout>,
py_type: *const pyo3::ffi::PyTypeObject,
validity: ValidityFlagWithMode,
mutable: bool,
component_id: ComponentId,
entity: Entity,
world_ptr: *mut World,
) -> Self {
Self {
data_ptr,
layout,
py_type,
validity,
mutable,
component_id,
entity,
world_ptr,
}
}
#[inline]
fn check_valid(&self) -> PyResult<()> {
Ok(self.validity.check()?)
}
fn deserialize_field(&self, py: Python, field_name: &str) -> PyResult<Py<PyAny>> {
self.check_valid()?;
let field = self
.layout
.fields
.iter()
.find(|f| f.name == field_name)
.ok_or_else(|| {
let available: Vec<&str> =
self.layout.fields.iter().map(|f| f.name.as_str()).collect();
PyAttributeError::new_err(format!(
"Component has no field '{}' (available: {})",
field_name,
available.join(", ")
))
})?;
let bytes_ptr = unsafe { self.data_ptr.add(field.offset) };
let value: Py<PyAny> = match field.field_type {
PrimitiveType::F32 => {
let val = unsafe { *(bytes_ptr as *const f32) };
val.into_pyobject(py)?.unbind().into()
}
PrimitiveType::F64 => {
let val = unsafe { *(bytes_ptr as *const f64) };
val.into_pyobject(py)?.unbind().into()
}
PrimitiveType::I32 => {
let val = unsafe { *(bytes_ptr as *const i32) };
val.into_pyobject(py)?.unbind().into()
}
PrimitiveType::I64 => {
let val = unsafe { *(bytes_ptr as *const i64) };
val.into_pyobject(py)?.unbind().into()
}
PrimitiveType::U32 => {
let val = unsafe { *(bytes_ptr as *const u32) };
val.into_pyobject(py)?.unbind().into()
}
PrimitiveType::U64 => {
let val = unsafe { *(bytes_ptr as *const u64) };
val.into_pyobject(py)?.unbind().into()
}
PrimitiveType::Bool => {
let val = unsafe { *(bytes_ptr as *const bool) };
Py::from(pyo3::types::PyBool::new(py, val)).into()
}
PrimitiveType::Vec3 => {
let vec3_ptr = bytes_ptr as *mut Vec3;
let storage = unsafe { ValueStorage::borrowed(vec3_ptr, self.validity.clone()) };
let py_vec3 = PyVec3::from_borrowed(storage);
Py::new(py, py_vec3)?.into_any()
}
PrimitiveType::Vec2 => {
let vec2_ptr = bytes_ptr as *mut Vec2;
let storage = unsafe { ValueStorage::borrowed(vec2_ptr, self.validity.clone()) };
let py_vec2 = PyVec2::from_borrowed(storage);
Py::new(py, py_vec2)?.into_any()
}
};
Ok(value)
}
fn serialize_field(
&self,
_py: Python,
field_name: &str,
value: &Bound<'_, PyAny>,
) -> PyResult<()> {
self.check_valid()?;
if !self.mutable {
return Err(PyRuntimeError::new_err(
"Cannot mutate read-only component (use Mut[T] in query)",
));
}
let field = self
.layout
.fields
.iter()
.find(|f| f.name == field_name)
.ok_or_else(|| {
PyAttributeError::new_err(format!("Component has no field '{}'", field_name))
})?;
let bytes_ptr = unsafe { self.data_ptr.add(field.offset) };
match field.field_type {
PrimitiveType::F32 => {
let val: f32 = value.extract()?;
unsafe { *(bytes_ptr as *mut f32) = val };
}
PrimitiveType::F64 => {
let val: f64 = value.extract()?;
unsafe { *(bytes_ptr as *mut f64) = val };
}
PrimitiveType::I32 => {
let val: i32 = value.extract()?;
unsafe { *(bytes_ptr as *mut i32) = val };
}
PrimitiveType::I64 => {
let val: i64 = value.extract()?;
unsafe { *(bytes_ptr as *mut i64) = val };
}
PrimitiveType::U32 => {
let val: u32 = value.extract()?;
unsafe { *(bytes_ptr as *mut u32) = val };
}
PrimitiveType::U64 => {
let val: u64 = value.extract()?;
unsafe { *(bytes_ptr as *mut u64) = val };
}
PrimitiveType::Bool => {
let val: bool = value.extract()?;
unsafe { *(bytes_ptr as *mut bool) = val };
}
PrimitiveType::Vec3 => {
let py_vec3: PyRef<PyVec3> = value.extract()?;
let v: Vec3 = (&*py_vec3).into();
unsafe { *(bytes_ptr as *mut Vec3) = v };
}
PrimitiveType::Vec2 => {
let py_vec2: PyRef<PyVec2> = value.extract()?;
let v: Vec2 = (&*py_vec2).into();
unsafe { *(bytes_ptr as *mut Vec2) = v };
}
}
Ok(())
}
}
#[pymethods]
impl PyLazyWrapperProxy {
fn __getattr__(self_: &Bound<'_, Self>, py: Python, name: &str) -> PyResult<Py<PyAny>> {
let this = self_.borrow();
this.check_valid()?;
match this.deserialize_field(py, name) {
Ok(value) => return Ok(value),
Err(e) => {
if !e.is_instance_of::<pyo3::exceptions::PyAttributeError>(py) {
return Err(e);
}
}
}
let py_type =
unsafe { pyo3::Bound::from_borrowed_ptr(py, this.py_type as *mut pyo3::ffi::PyObject) };
let attr = py_type.getattr(name).map_err(|_| {
let available: Vec<&str> = this.layout.fields.iter().map(|f| f.name.as_str()).collect();
pyo3::exceptions::PyAttributeError::new_err(format!(
"Component has no field or method '{}' (fields: {})",
name,
available.join(", ")
))
})?;
if attr.is_callable() {
let types_module = py.import("types")?;
let method_type = types_module.getattr("MethodType")?;
let bound_method = method_type.call1((&attr, self_))?;
Ok(bound_method.unbind())
} else {
Ok(attr.unbind())
}
}
fn __setattr__(&self, py: Python, name: &str, value: Bound<'_, PyAny>) -> PyResult<()> {
if name.starts_with("__") && name.ends_with("__") {
return Err(PyAttributeError::new_err(format!(
"Cannot set special attribute '{}'",
name
)));
}
self.serialize_field(py, name, &value)?;
crate::ecs::change_tracking::mark_component_changed_explicit(
self.entity,
self.world_ptr,
self.component_id,
);
Ok(())
}
#[getter(__class__)]
fn get_class(&self, py: Python) -> PyResult<Py<PyType>> {
let py_type =
unsafe { pyo3::Bound::from_borrowed_ptr(py, self.py_type as *mut pyo3::ffi::PyObject) };
if let Ok(type_obj) = py_type.cast::<PyType>() {
Ok(type_obj.clone().unbind())
} else {
Err(PyRuntimeError::new_err("Invalid component type"))
}
}
fn __repr__(&self, py: Python) -> PyResult<String> {
self.check_valid()?;
let type_name = self.get_class(py)?;
let type_name_str = type_name.bind(py).name()?;
let mut field_strs = Vec::new();
for field in &self.layout.fields {
let value = self.deserialize_field(py, &field.name)?;
let value_repr = value.bind(py).repr()?;
field_strs.push(format!("{}={}", field.name, value_repr));
}
Ok(format!("{}({})", type_name_str, field_strs.join(", ")))
}
}