use bevy::{
ecs::{
component::ComponentId,
world::{Command, DeferredWorld},
},
prelude::*,
};
#[derive(Component)]
pub(crate) struct MutableCell<T>(pub(crate) T);
#[derive(PartialEq, Debug)]
pub struct Mutable<T> {
pub(crate) cell: Entity,
pub(crate) component: ComponentId,
pub(crate) marker: std::marker::PhantomData<T>,
}
impl<T> Mutable<T> {
pub fn id(&self) -> Entity {
self.cell
}
}
impl<T> Copy for Mutable<T> {}
impl<T> Clone for Mutable<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> Mutable<T>
where
T: PartialEq + Send + Sync + 'static,
{
pub fn update<F: FnOnce(Mut<T>), W: WriteMutable>(&self, cx: &mut W, updater: F) {
cx.update_mutable(self.cell, updater);
}
}
impl<T> Mutable<T>
where
T: PartialEq + Send + Sync + 'static,
{
pub fn as_ref<'a, 'b: 'a, R: ReadMutable>(&'a self, cx: &'b mut R) -> &'a T {
cx.read_mutable_as_ref(self)
}
}
impl<T> Mutable<T>
where
T: PartialEq + Copy + Send + Sync + 'static,
{
pub fn get<R: ReadMutable>(&self, cx: &R) -> T {
cx.read_mutable(self)
}
pub fn set<R: WriteMutable>(&self, cx: &mut R, value: T) {
cx.write_mutable(self.cell, value);
}
}
impl<T> Mutable<T>
where
T: PartialEq + Clone + Send + Sync + 'static,
{
pub fn get_clone<R: ReadMutable>(&self, cx: &R) -> T {
cx.read_mutable_clone(self)
}
pub fn set_clone<R: WriteMutable>(&self, cx: &mut R, value: T) {
cx.write_mutable_clone(self.cell, value);
}
}
pub trait ReadMutable {
fn read_mutable<T>(&self, mutable: &Mutable<T>) -> T
where
T: Send + Sync + Copy + 'static;
fn read_mutable_clone<T>(&self, mutable: &Mutable<T>) -> T
where
T: Send + Sync + Clone + 'static;
fn read_mutable_as_ref<T>(&self, mutable: &Mutable<T>) -> &T
where
T: Send + Sync + 'static;
fn read_mutable_map<T, U, F: Fn(&T) -> U>(&self, mutable: &Mutable<T>, f: F) -> U
where
T: Send + Sync + 'static;
}
pub trait WriteMutable {
fn write_mutable<T>(&mut self, mutable: Entity, value: T)
where
T: Send + Sync + Copy + PartialEq + 'static;
fn write_mutable_clone<T>(&mut self, mutable: Entity, value: T)
where
T: Send + Sync + Clone + PartialEq + 'static;
fn update_mutable<T, F: FnOnce(Mut<T>)>(&mut self, mutable: Entity, updater: F)
where
T: Send + Sync + 'static;
}
pub(crate) struct UpdateMutableCell<T> {
pub(crate) mutable: Entity,
pub(crate) value: T,
}
impl<T: Send + Sync + 'static + PartialEq> Command for UpdateMutableCell<T> {
fn apply(self, world: &mut World) {
let mut mutable_ent = world.entity_mut(self.mutable);
let mut mutable = mutable_ent.get_mut::<MutableCell<T>>().unwrap();
if mutable.0 != self.value {
mutable.0 = self.value;
}
}
}
impl ReadMutable for World {
fn read_mutable<T>(&self, mutable: &Mutable<T>) -> T
where
T: Send + Sync + Copy + 'static,
{
let mutable_entity = self.entity(mutable.cell);
mutable_entity.get::<MutableCell<T>>().unwrap().0
}
fn read_mutable_clone<T>(&self, mutable: &Mutable<T>) -> T
where
T: Send + Sync + Clone + 'static,
{
let mutable_entity = self.entity(mutable.cell);
mutable_entity.get::<MutableCell<T>>().unwrap().0.clone()
}
fn read_mutable_as_ref<T>(&self, mutable: &Mutable<T>) -> &T
where
T: Send + Sync + 'static,
{
let mutable_entity = self.entity(mutable.cell);
&mutable_entity.get::<MutableCell<T>>().unwrap().0
}
fn read_mutable_map<T, U, F: Fn(&T) -> U>(&self, mutable: &Mutable<T>, f: F) -> U
where
T: Send + Sync + 'static,
{
let mutable_entity = self.entity(mutable.cell);
f(&mutable_entity.get::<MutableCell<T>>().unwrap().0)
}
}
impl WriteMutable for World {
fn write_mutable<T>(&mut self, mutable: Entity, value: T)
where
T: Send + Sync + PartialEq + 'static,
{
self.commands().add(UpdateMutableCell { mutable, value });
}
fn write_mutable_clone<T>(&mut self, mutable: Entity, value: T)
where
T: Send + Sync + Clone + PartialEq + 'static,
{
self.commands().add(UpdateMutableCell { mutable, value });
}
fn update_mutable<T, F: FnOnce(Mut<T>)>(&mut self, mutable: Entity, updater: F)
where
T: Send + Sync + 'static,
{
let value = self.get_mut::<MutableCell<T>>(mutable).unwrap();
let inner = value.map_unchanged(|v| &mut v.0);
(updater)(inner);
}
}
impl<'w> ReadMutable for DeferredWorld<'w> {
fn read_mutable<T>(&self, mutable: &Mutable<T>) -> T
where
T: Send + Sync + Copy + 'static,
{
let mutable_entity = self.entity(mutable.cell);
mutable_entity.get::<MutableCell<T>>().unwrap().0
}
fn read_mutable_clone<T>(&self, mutable: &Mutable<T>) -> T
where
T: Send + Sync + Clone + 'static,
{
let mutable_entity = self.entity(mutable.cell);
mutable_entity.get::<MutableCell<T>>().unwrap().0.clone()
}
fn read_mutable_as_ref<T>(&self, mutable: &Mutable<T>) -> &T
where
T: Send + Sync + 'static,
{
let mutable_entity = self.entity(mutable.cell);
&mutable_entity.get::<MutableCell<T>>().unwrap().0
}
fn read_mutable_map<T, U, F: Fn(&T) -> U>(&self, mutable: &Mutable<T>, f: F) -> U
where
T: Send + Sync + 'static,
{
let mutable_entity = self.entity(mutable.cell);
f(&mutable_entity.get::<MutableCell<T>>().unwrap().0)
}
}
impl<'w> WriteMutable for DeferredWorld<'w> {
fn write_mutable<T>(&mut self, mutable: Entity, value: T)
where
T: Send + Sync + PartialEq + 'static,
{
self.commands().add(UpdateMutableCell { mutable, value });
}
fn write_mutable_clone<T>(&mut self, mutable: Entity, value: T)
where
T: Send + Sync + Clone + PartialEq + 'static,
{
self.commands().add(UpdateMutableCell { mutable, value });
}
fn update_mutable<T, F: FnOnce(Mut<T>)>(&mut self, mutable: Entity, updater: F)
where
T: Send + Sync + 'static,
{
let value = self.get_mut::<MutableCell<T>>(mutable).unwrap();
let inner = value.map_unchanged(|v| &mut v.0);
(updater)(inner);
}
}
#[cfg(test)]
mod tests {
use crate::{cx::Cx, TrackingScope};
use super::*;
#[test]
fn test_mutable_copy() {
let mut world = World::default();
let mut scope = TrackingScope::new(world.change_tick());
let owner = world.spawn_empty().id();
let mut cx = Cx::new(&mut world, owner, &mut scope);
let mutable = cx.create_mutable::<i32>(0);
let reader = mutable;
let reader2 = cx.create_mutable::<i32>(0);
assert_eq!(reader.get(&cx), 0);
assert_eq!(reader2.get(&cx), 0);
mutable.set(&mut cx, 1);
assert_eq!(reader.get(&cx), 0);
assert_eq!(reader2.get(&cx), 0);
world.flush_commands();
let cx = Cx::new(&mut world, owner, &mut scope);
assert_eq!(mutable.get(&cx), 1);
assert_eq!(reader2.get(&cx), 0);
}
#[test]
fn test_mutable_clone() {
let mut world = World::default();
let mut scope = TrackingScope::new(world.change_tick());
let owner = world.spawn_empty().id();
let mut cx = Cx::new(&mut world, owner, &mut scope);
let mutable = cx.create_mutable("Hello".to_string());
let reader = mutable;
let reader2 = cx.create_mutable::<i32>(0);
assert_eq!(reader.get_clone(&cx), "Hello".to_string());
assert_eq!(reader2.get(&cx), 0);
mutable.set_clone(&mut cx, "Goodbye".to_string());
assert_eq!(reader.get_clone(&cx), "Hello".to_string());
assert_eq!(reader2.get(&cx), 0);
world.flush_commands();
let cx = Cx::new(&mut world, owner, &mut scope);
assert_eq!(reader.get_clone(&cx), "Goodbye".to_string());
assert_eq!(reader2.get(&cx), 0);
}
}