use core::{cell::Cell, marker::PhantomData, ptr::NonNull};
use crate::{
entity::{EntityId, Location},
query::{Query, QueryItem},
world::WorldLocal,
};
pub struct FlowQuery<'a, Q> {
world_location: &'a Cell<(NonNull<WorldLocal>, Location)>,
id: EntityId,
query: Q,
marker: PhantomData<&'a ()>,
}
unsafe impl Send for FlowQuery<'_> {}
impl<Q> FlowQuery<'_, Q>
where
Q: Query,
{
pub fn fetch(&mut self) -> QueryItem<'_, Q> {
let (mut world, loc) = self.world_location.get();
let world = unsafe { world.as_mut() };
unsafe { EntityRef::from_parts(self.id, loc, world.local()) }
}
#[doc(hidden)]
pub fn reborrow(&mut self) -> FlowEntity<'_> {
FlowEntity {
world_location: self.world_location,
id: self.id,
marker: PhantomData,
}
}
}
#[doc(hidden)]
pub trait FlowEntityFnG<'a> {}
impl<'a, F, Fut> FlowEntityFnG<'a> for F
where
F: FnOnce(FlowEntity<'a>) -> Fut,
Fut: Future<Output = ()> + Send + 'a,
{
}
#[doc(hidden)]
pub fn insert_entity_flow<F, Fut>(id: EntityId, world: &mut World, init: F)
where
F: FnOnce(FlowEntity<'static>) -> Fut,
Fut: Future<Output = ()> + Send + 'static,
{
let Some(loc) = world.entities().get_location(id) else {
return;
};
let mut new_flow_task: NewFlowTask<FutureFlow<Fut>> = Arc::new(MaybeUninit::uninit());
let new_flow_task_mut = Arc::get_mut(&mut new_flow_task).unwrap();
unsafe {
let flow_ptr =
addr_of_mut!((*new_flow_task_mut.as_mut_ptr()).flow).cast::<FutureFlow<Fut>>();
let id_ptr = addr_of_mut!((*flow_ptr).id);
id_ptr.write(id);
let world_location_ptr = addr_of_mut!((*flow_ptr).world_location);
world_location_ptr.write(Cell::new((NonNull::from(world.local()), loc)));
let flow_world = FlowEntity {
world_location: &*world_location_ptr,
id,
marker: PhantomData,
};
let fut = init(flow_world);
let fut_ptr = addr_of_mut!((*flow_ptr).fut);
fut_ptr.write(fut);
}
world
.with_default_resource::<NewFlows>()
.typed_new_flows()
.array
.push(new_flow_task);
}
impl<F, Fut> FlowEntityFn for F
where
F: for<'a> FlowEntityFnG<'a>,
F: FnOnce(FlowEntity<'static>) -> Fut,
Fut: Future<Output = ()> + Send + 'static,
{
fn insert_into(self, id: EntityId, world: &mut World) {
insert_entity_flow(id, world, |world| (self)(world));
}
}
pub struct EntityClosure<F>(pub F);
impl<F> FlowEntityFn for EntityClosure<F>
where
F: FnOnce(EntityId, &mut World),
{
fn insert_into(self, id: EntityId, world: &mut World) {
(self.0)(id, world);
}
}
#[macro_export]
macro_rules! flow_closure_on {
(|mut $entity:ident $(: $FlowEntity:ty)?| -> $ret:ty $code:block) => {
$crate::private::EntityClosure(|id: $crate::entity::EntityId, world: &mut $crate::world::World| {
$crate::private::insert_entity_flow(
id,
world,
|mut world: $crate::flow::FlowEntity<'static>| async move {
let mut $entity $(: $FlowEntity)? = world.reborrow();
let res: $ret = { $code };
res
},
)
})
};
(|mut $entity:ident $(: $FlowEntity:ty)?| $code:expr) => {
$crate::private::EntityClosure(|id: $crate::entity::EntityId, world: &mut $crate::world::World| {
$crate::private::insert_entity_flow(
id,
world,
|mut world: $crate::flow::FlowEntity<'static>| async move {
let mut $entity $(: $FlowEntity)? = world.reborrow();
$code
},
)
})
};
}