shiv 0.1.0-alpha.10

A simple modern Entity Component System
Documentation
use crate::world::World;

use super::{IntoSystem, System, SystemMeta};

#[derive(Debug)]
pub struct PipeSystem<A, B> {
    system_a: A,
    system_b: B,
    meta: SystemMeta,
}

impl<A, B> System for PipeSystem<A, B>
where
    A: System,
    B: System<In = A::Out>,
{
    type In = A::In;
    type Out = B::Out;

    fn meta(&self) -> &SystemMeta {
        &self.meta
    }

    unsafe fn meta_mut(&mut self) -> &mut SystemMeta {
        &mut self.meta
    }

    fn init(&mut self, world: &mut World) {
        self.system_a.init(world);
        self.system_b.init(world);
    }

    unsafe fn run_unchecked(&mut self, input: Self::In, world: &World) -> Self::Out {
        let payload = unsafe { self.system_a.run_unchecked(input, world) };
        let out = unsafe { self.system_b.run_unchecked(payload, world) };

        self.meta.last_change_tick = self.system_a.meta().last_change_tick;

        out
    }

    fn run(&mut self, input: Self::In, world: &mut World) -> Self::Out {
        let payload = self.system_a.run(input, world);
        let out = self.system_b.run(payload, world);

        self.meta.last_change_tick = self.system_a.meta().last_change_tick;

        out
    }

    fn apply(&mut self, world: &mut World) {
        self.system_a.apply(world);
        self.system_b.apply(world);
    }

    fn check_change_tick(&mut self, change_tick: u32) {
        self.system_a.check_change_tick(change_tick);
        self.system_b.check_change_tick(change_tick);
    }

    fn set_last_change_tick(&mut self, last_change_tick: u32) {
        self.system_a.set_last_change_tick(last_change_tick);
        self.system_b.set_last_change_tick(last_change_tick);
    }
}

pub trait IntoPipeSystem<In, Payload, Out, ParamsA, ParamsB, System>:
    IntoSystem<In, Payload, ParamsA>
where
    System: IntoSystem<Payload, Out, ParamsB>,
{
    fn pipe(self, system: System) -> PipeSystem<Self::System, System::System>;
}

impl<In, Payload, Out, ParamsA, ParamsB, SystemA, SystemB>
    IntoPipeSystem<In, Payload, Out, ParamsA, ParamsB, SystemB> for SystemA
where
    SystemA: IntoSystem<In, Payload, ParamsA>,
    SystemB: IntoSystem<Payload, Out, ParamsB>,
{
    fn pipe(self, system: SystemB) -> PipeSystem<Self::System, SystemB::System> {
        let system_a = self.into_system();
        let system_b = system.into_system();

        let mut access = system_a.meta().access.clone();
        access.extend(&system_b.meta().access);

        let meta = SystemMeta {
            name: format!("{} | {}", system_a.meta().name, system_b.meta().name).into(),
            access,
            last_change_tick: 0,
        };

        PipeSystem {
            system_a,
            system_b,
            meta,
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::{
        system::{In, IntoPipeSystem, System},
        world::World,
    };

    #[test]
    fn pipe_system() {
        fn a() -> u32 {
            3
        }

        fn b(input: In<u32>) -> f32 {
            (*input * 3) as f32
        }

        let mut world = World::new();
        let mut system = a.pipe(b);

        system.init(&mut world);
        let out = unsafe { system.run_unchecked((), &world) };
        system.apply(&mut world);

        assert_eq!(out, 9.0);
    }
}