use std::{ptr::NonNull, marker::PhantomData}; use crate::{world::World, View, Query, Access}; pub mod graph; /// A system that does not mutate the world pub trait System { fn world_access(&self) -> Access; fn execute(&mut self, world: NonNull) -> anyhow::Result<()>; } /// A trait for converting something into a system. pub trait IntoSystem { type System: System; fn into_system(self) -> Self::System; } pub trait FnArgFetcher { type Arg<'a>: FnArg; fn new() -> Self; /// Return the appropriate world access if this fetcher gets the world directly. /// Return [`Access::None`] if you're only fetching components, or resources. fn world_access(&self) -> Access { Access::None } /// Get the arg from the world unsafe fn get<'a>(&mut self, world: NonNull) -> Self::Arg<'a>; } pub trait FnArg { type Fetcher: FnArgFetcher; } pub struct FnSystem { inner: F, args: Args, } impl System for FnSystem where A: FnArgFetcher, F: for<'a> FnMut(A::Arg<'a>) -> anyhow::Result<()>, { fn world_access(&self) -> Access { todo!() } fn execute(&mut self, world: NonNull) -> anyhow::Result<()> { let mut a = A::new(); let a = unsafe { a.get(world) }; (self.inner)(a)?; Ok(()) } } impl IntoSystem for F where A: FnArg, F: FnMut(A) -> anyhow::Result<()>, F: for<'a> FnMut(::Arg<'a>) -> anyhow::Result<()>, { type System = FnSystem; fn into_system(self) -> Self::System { FnSystem { args: A::Fetcher::new(), inner: self } } } /// An ArgFetcher implementation for query [`View`]s pub struct ViewArgFetcher { query: Q } impl<'a, Q: Query> FnArg for View<'a, Q> { type Fetcher = ViewArgFetcher; } impl FnArgFetcher for ViewArgFetcher { type Arg<'a> = View<'a, Q>; fn new() -> Self { ViewArgFetcher { query: Q::new(), } } unsafe fn get<'a>(&mut self, world: NonNull) -> Self::Arg<'a> { let world = &*world.as_ptr(); let arch = world.archetypes.values().collect(); let v = View::new(world, self.query, arch); v } } /// An ArgFetcher implementation for borrowing the [`World`]. pub struct WorldArgFetcher; impl<'a> FnArg for &'a World { type Fetcher = WorldArgFetcher; } impl FnArgFetcher for WorldArgFetcher { type Arg<'a> = &'a World; fn new() -> Self { WorldArgFetcher } fn world_access(&self) -> Access { Access::Read } unsafe fn get<'a>(&mut self, world: NonNull) -> Self::Arg<'a> { &*world.as_ptr() } } /// An ArgFetcher implementation for mutably borrowing the [`World`]. pub struct WorldMutArgFetcher; impl<'a> FnArg for &'a mut World { type Fetcher = WorldMutArgFetcher; } impl FnArgFetcher for WorldMutArgFetcher { type Arg<'a> = &'a mut World; fn new() -> Self { WorldMutArgFetcher } fn world_access(&self) -> Access { Access::Write } unsafe fn get<'a>(&mut self, world: NonNull) -> Self::Arg<'a> { &mut *world.as_ptr() } } #[cfg(test)] mod tests { use std::ptr::NonNull; use crate::{tests::{Vec2, Vec3}, View, QueryBorrow, world::World}; use super::{System, IntoSystem}; struct SomeCounter(u32); #[test] fn simple_system() { let mut world = World::new(); let vecs = &[Vec2::rand(), Vec2::rand(), Vec2::rand(), Vec2::rand()]; world.spawn((vecs[0],)); world.spawn((vecs[1],)); world.spawn((vecs[2],)); world.spawn((vecs[3],)); let mut count = 0; let test_system = |view: View>| -> anyhow::Result<()> { let mut vecs = vecs.to_vec(); for v in view.into_iter() { let pos = vecs.iter().position(|vec| *vec == *v) .expect("Failure to find vec inside list"); vecs.remove(pos); println!("Got v at: {:?}", v); count += 1; } Ok(()) }; test_system.into_system().execute(NonNull::from(&world)).unwrap(); assert_eq!(count, 4); } #[test] fn multi_system() { let mut world = World::new(); world.spawn((Vec2::rand(), Vec3::rand())); world.spawn((Vec2::rand(), Vec3::rand())); world.spawn((Vec2::rand(),)); world.spawn((Vec2::rand(),)); let mut count = 0; let test_system = |view: View<(QueryBorrow, QueryBorrow)>| -> anyhow::Result<()> { for (v2, v3) in view.into_iter() { println!("Got v2 at '{:?}' and v3 at: '{:?}'", v2, v3); count += 1; } Ok(()) }; test_system.into_system().execute(NonNull::from(&world)).unwrap(); assert_eq!(count, 2); } #[test] fn world_system() { let mut world = World::new(); world.spawn((Vec2::rand(), Vec3::rand())); world.spawn((Vec2::rand(), Vec3::rand())); world.add_resource(SomeCounter(0)); let test_system = |world: &World| -> anyhow::Result<()> { let mut counter = world.get_resource_mut::(); counter.0 += 10; Ok(()) }; test_system.into_system().execute(NonNull::from(&world)).unwrap(); let counter = world.get_resource::(); assert_eq!(counter.0, 10); } }