flax 0.7.1

An ergonomic archetypical ECS
Documentation
use flax::{components::name, *};
use itertools::Itertools;
use pretty_assertions::assert_eq;

#[test]
fn no_changes() {
    let mut world = World::new();

    component! {
        a: i32,
        b: i32,
    }

    let mut query = Query::new(entity_ids()).filter(a().modified() | b().modified());

    let id = Entity::builder().set(a(), 5).spawn(&mut world);

    assert_eq!(query.collect_vec(&world), [id]);
}

#[test]
#[cfg(feature = "flume")]
fn change_detection() {
    use flax::{components::name, *};
    use glam::{Quat, Vec3};
    use itertools::Itertools;
    use pretty_assertions::assert_eq;
    use rand::{rngs::StdRng, Rng, SeedableRng};

    use flax::events::{EventKind, EventSubscriber};

    let (removed_tx, removed_rx) = flume::unbounded();

    component! {
        position: Vec3,
        rotation: Quat,
    }

    let mut world = World::new();

    world.subscribe(
        removed_tx
            .filter_components([rotation().key()])
            .filter(|kind, _| kind == EventKind::Removed),
    );

    let mut rng = StdRng::seed_from_u64(83);
    let mut ids = (0..10)
        .map(|i| {
            Entity::builder()
                .set(name(), format!("a.{i}"))
                .set(position(), rng.gen())
                .spawn(&mut world)
        })
        .collect_vec();

    ids.extend((0..30).map(|i| {
        Entity::builder()
            .set(name(), format!("b.{i}"))
            .set(position(), rng.gen())
            .set(rotation(), Quat::from_scaled_axis(rng.gen()))
            .spawn(&mut world)
    }));

    let mut query = Query::new((entity_ids(), position().modified()));

    assert_eq!(
        query
            .borrow(&world)
            .iter()
            .map(|v| v.0)
            .sorted()
            .collect_vec(),
        ids
    );

    for &id in &ids[20..40] {
        world.set(id, position(), Vec3::ZERO).unwrap();
    }

    assert_eq!(
        query
            .borrow(&world)
            .iter()
            .map(|v| v.0)
            .sorted()
            .collect_vec(),
        ids[20..40]
    );

    world.remove(ids[11], rotation()).unwrap();
    world.remove(ids[12], rotation()).unwrap();
    world.remove(ids[30], rotation()).unwrap();

    let removed = removed_rx
        .drain()
        .inspect(|v| eprintln!("removed: {v:?}"))
        .map(|v| v.id)
        .collect_vec();

    assert_eq!(removed, [ids[11], ids[12], ids[30]]);

    world.despawn(ids[35]).unwrap();

    let removed = removed_rx.drain().map(|v| v.id).collect_vec();
    assert_eq!(removed, [ids[35]]);

    dbg!(removed);
}

#[test]
#[cfg(feature = "flume")]
fn clear_events() {
    use flax::{
        components::{component_info, name},
        events::{Event, EventSubscriber},
    };

    let mut world = World::new();

    let (tx, rx) = flume::unbounded();
    world.subscribe(
        tx.filter_components([name().key()])
            .filter_arch(component_info().without()),
    );

    let id = Entity::builder().set(name(), "id".into()).spawn(&mut world);

    world.clear(id).unwrap();

    assert_eq!(
        rx.drain().collect_vec(),
        &[
            Event {
                id,
                key: name().key(),
                kind: events::EventKind::Added,
            },
            Event {
                id,
                key: name().key(),
                kind: events::EventKind::Removed,
            }
        ]
    );
}

#[test]
#[cfg(feature = "flume")]
fn despawn() {
    use flax::{
        components::{component_info, name},
        events::{Event, EventSubscriber},
    };

    let mut world = World::new();

    let (tx, rx) = flume::unbounded();
    world.subscribe(
        tx.filter_components([name().key()])
            .filter_arch(component_info().without()),
    );

    let id = Entity::builder().set(name(), "id".into()).spawn(&mut world);

    world.clear(id).unwrap();

    assert_eq!(
        rx.drain().collect_vec(),
        &[
            Event {
                id,
                key: name().key(),
                kind: events::EventKind::Added,
            },
            Event {
                id,
                key: name().key(),
                kind: events::EventKind::Removed,
            }
        ]
    );
}
#[test]
fn query_changes() {
    component! {
        a: i32,
        b: i32,
        c: f32,
    };

    let mut world = World::new();

    let ids = (0..10)
        .map(|i| Entity::builder().set(a(), i).into())
        .chain((0..30).map(|i| Entity::builder().set(a(), i).set(b(), -i).into()))
        .chain((0..80).map(|i| {
            Entity::builder()
                .set(a(), i)
                .set(b(), -i)
                .set(c(), i as f32)
                .into()
        }))
        .enumerate()
        .map(|(i, mut v): (usize, EntityBuilder)| v.set(name(), format!("{i}")).spawn(&mut world))
        .collect_vec();

    let mut changed = Query::new((entity_ids(), a().modified().copied()));

    let mut changed = |w| changed.borrow(w).iter().map(|v| v.0).collect_vec();

    assert_eq!(changed(&world), ids);

    Query::new(a().as_mut())
        .with(b())
        .without(c())
        .borrow(&world)
        .for_each(|v| *v *= 2);

    assert_eq!(changed(&world), &ids[10..40]);

    Query::new(a().as_mut())
        .with(b())
        .filter(!c().with() | c().gt(40.0))
        .borrow(&world)
        .for_each(|v| *v *= 2);

    assert_eq!(
        changed(&world),
        [&ids[10..40], &ids[(40 + 41)..(40 + 80)]].concat()
    );
}

#[test]
fn added_opt_union() {
    component! {
        a: i32,
        b: i32,
    }

    let mut world = World::new();

    // let mut query = Query::new((a(), b().modified().opt_or_default()));
    let mut query = Query::new((a(), b().opt_or_default()).modified());

    let id = Entity::builder().set(a(), 5).spawn(&mut world);

    assert_eq!(query.borrow(&world).iter().collect_vec(), [(&5, &0)]);
    assert_eq!(query.borrow(&world).iter().collect_vec(), []);

    world.set(id, b(), 2).unwrap();

    assert_eq!(query.borrow(&world).iter().collect_vec(), [(&5, &2)]);
    assert_eq!(query.borrow(&world).iter().collect_vec(), []);
}

#[test]
fn added_opt_and() {
    component! {
        a: i32,
        b: i32,
    }

    let mut world = World::new();

    // let mut query = Query::new((a(), b().modified().opt_or_default()));
    let mut query = Query::new((a(), b().opt_or_default().modified()));

    let id = Entity::builder().set(a(), 5).spawn(&mut world);

    assert_eq!(query.borrow(&world).iter().collect_vec(), []);

    world.set(id, b(), 2).unwrap();

    assert_eq!(query.borrow(&world).iter().collect_vec(), [(&5, &2)]);
    assert_eq!(query.borrow(&world).iter().collect_vec(), []);
}