use bevy::prelude::*;
use parking_lot::RwLock;
use std::fmt::Debug;
use std::{borrow::Cow, sync::Weak};
use bevy_mod_scripting_core::world::WorldPointer;
use crate::{
error::ReflectionError,
sub_reflect::{ReflectBase, ReflectPath, ReflectPathElem},
};
pub enum ScriptRefBase {}
#[derive(Clone, Debug)]
pub struct ScriptRef {
pub(crate) path: ReflectPath,
pub(crate) world_ptr: WorldPointer,
}
impl ScriptRef {
pub fn new_component_ref(
comp: ReflectComponent,
entity: Entity,
world_ptr: WorldPointer,
) -> Self {
Self {
path: ReflectPath::new(ReflectBase::Component { comp, entity }),
world_ptr,
}
}
pub fn new_resource_ref(res: ReflectResource, world_ptr: WorldPointer) -> Self {
Self {
path: ReflectPath::new(ReflectBase::Resource { res }),
world_ptr,
}
}
pub unsafe fn new_script_ref(
ptr: ReflectPtr,
valid: Weak<RwLock<()>>,
world_ptr: WorldPointer,
) -> Self {
Self {
path: ReflectPath::new(ReflectBase::ScriptOwned { ptr, valid }),
world_ptr,
}
}
pub fn sub_ref(&self, elem: ReflectPathElem) -> ScriptRef {
let path = self.path.new_sub(elem);
Self {
path,
..self.clone()
}
}
#[inline(always)]
pub fn get<O, F>(&self, f: F) -> Result<O, ReflectionError>
where
F: FnOnce(&dyn Reflect) -> O,
{
self.path.get(self.world_ptr.clone(), f)
}
pub fn get_typed<T, O, F>(&self, f: F) -> Result<O, ReflectionError>
where
F: FnOnce(&T) -> O,
T: Reflect,
{
self.path.get(self.world_ptr.clone(), |reflect| {
(f)(reflect.downcast_ref::<T>().unwrap_or_else(|| {
panic!(
"Expected `{}` found `{}`",
::std::any::type_name::<T>(),
reflect.type_name()
)
}))
})
}
#[inline(always)]
pub fn get_mut<O, F>(&mut self, f: F) -> Result<O, ReflectionError>
where
F: FnOnce(&mut dyn Reflect) -> O,
{
self.path.get_mut(self.world_ptr.clone(), f)
}
pub fn get_mut_typed<T, O, F>(&mut self, f: F) -> Result<O, ReflectionError>
where
F: FnOnce(&mut T) -> O,
T: Reflect,
{
self.path.get_mut(self.world_ptr.clone(), |reflect| {
(f)(reflect.downcast_mut().unwrap())
})
}
pub fn apply(&mut self, other: &ScriptRef) -> Result<(), ReflectionError> {
let cloned = other.get(|s| s.clone_value())?;
self.get_mut(|s| s.apply(&*cloned))
}
pub fn set<T>(&mut self, other: &Self) -> Result<(), ReflectionError>
where
T: Reflect + Clone,
{
let other: T = other.get_typed(|s: &T| s.clone())?;
self.get_mut_typed(|s| *s = other)
}
pub fn set_val<T>(&mut self, other: T) -> Result<(), ReflectionError>
where
T: Reflect,
{
self.get_mut_typed(|s| *s = other)
}
}
pub trait ValueIndex<Idx> {
type Output;
fn index(&self, index: Idx) -> Self::Output;
}
impl ValueIndex<usize> for ScriptRef {
type Output = Self;
fn index(&self, index: usize) -> Self::Output {
self.sub_ref(ReflectPathElem::IndexAccess(index))
}
}
impl ValueIndex<Cow<'static, str>> for ScriptRef {
type Output = Self;
fn index(&self, index: Cow<'static, str>) -> Self::Output {
self.sub_ref(ReflectPathElem::FieldAccess(index))
}
}
#[derive(Clone, Copy, Debug)]
pub struct ReflectPtr {
ptr: *const dyn Reflect,
is_mut: bool,
}
impl From<*const dyn Reflect> for ReflectPtr {
fn from(ptr: *const dyn Reflect) -> Self {
Self { ptr, is_mut: false }
}
}
impl From<*mut dyn Reflect> for ReflectPtr {
fn from(ptr: *mut dyn Reflect) -> Self {
Self { ptr, is_mut: true }
}
}
impl ReflectPtr {
#[inline(always)]
pub unsafe fn const_ref<'a>(self) -> &'a dyn Reflect {
&*self.ptr
}
pub unsafe fn mut_ref<'a>(self) -> Option<&'a mut dyn Reflect> {
if self.is_mut {
Some(&mut *(self.ptr as *mut dyn Reflect))
} else {
None
}
}
pub unsafe fn map(
self,
get: fn(&dyn Reflect) -> &dyn Reflect,
get_mut: fn(&mut dyn Reflect) -> &mut dyn Reflect,
) -> Self {
if self.is_mut {
(get_mut(self.mut_ref().unwrap()) as *const dyn Reflect).into()
} else {
(get(self.const_ref()) as *const dyn Reflect).into()
}
}
}
unsafe impl Send for ReflectPtr {}
unsafe impl Sync for ReflectPtr {}
#[derive(Clone, Debug)]
pub struct ReflectedValue {
pub(crate) ref_: ScriptRef,
}
impl From<ReflectedValue> for ScriptRef {
fn from(ref_: ReflectedValue) -> Self {
ref_.ref_
}
}