moirai-for-games 0.1.0

A small deterministic no_std ECS for constrained and headless games
Documentation
//! Per-world owner token used to validate entities, components, and events.
//!
//! Each [`World`](super::World) and [`WorldBuilder`](super::WorldBuilder) receives a
//! unique [`WorldOwner`] stamped onto handles so cross-world misuse is rejected.

use core::hash::{Hash, Hasher};
use core::sync::atomic::{AtomicU32, Ordering};

static NEXT_OWNER_TOKEN: AtomicU32 = AtomicU32::new(1);

#[derive(Clone)]
pub(crate) struct WorldOwner(u32);

impl WorldOwner {
    pub fn new() -> Self {
        // `fetch_update` is the Rust 1.75-compatible spelling; newer Rust
        // releases call it `try_update`.
        #[allow(deprecated)]
        let token = NEXT_OWNER_TOKEN
            .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |current| {
                current.checked_add(1)
            })
            .expect("world owner token exhausted");
        Self(token)
    }

    pub fn same(&self, other: &Self) -> bool {
        self.0 == other.0
    }

    pub(crate) fn token(&self) -> u32 {
        self.0
    }
}

impl PartialEq for WorldOwner {
    fn eq(&self, other: &Self) -> bool {
        self.same(other)
    }
}

impl Eq for WorldOwner {}

impl Hash for WorldOwner {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.0.hash(state);
    }
}

impl core::fmt::Debug for WorldOwner {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        f.debug_tuple("WorldOwner").field(&self.0).finish()
    }
}

impl Default for WorldOwner {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use core::hash::{Hash, Hasher};

    struct TokenHasher(u64);

    impl Hasher for TokenHasher {
        fn finish(&self) -> u64 {
            self.0
        }

        fn write(&mut self, _: &[u8]) {}

        fn write_u32(&mut self, i: u32) {
            self.0 = i as u64;
        }
    }

    #[test]
    fn same_owner_is_equal_and_hashes_consistently() {
        let a = WorldOwner::new();
        let b = a.clone();
        assert_eq!(a, b);
        let mut hasher_a = TokenHasher(0);
        let mut hasher_b = TokenHasher(0);
        a.hash(&mut hasher_a);
        b.hash(&mut hasher_b);
        assert_eq!(hasher_a.finish(), hasher_b.finish());
    }

    #[test]
    fn different_owners_are_not_equal() {
        let a = WorldOwner::new();
        let b = WorldOwner::new();
        assert_ne!(a, b);
        assert!(!a.same(&b));
    }

    #[test]
    fn default_constructed_owner_is_well_formed() {
        let owner = WorldOwner::default();
        assert!(owner.same(&owner));
    }

    #[test]
    fn debug_formats_as_owner_tuple() {
        let owner = WorldOwner::new();
        let text = alloc::format!("{owner:?}");
        assert!(text.starts_with("WorldOwner("));
        assert!(text.ends_with(')'));
    }

    #[test]
    fn token_hasher_records_u32_writes() {
        let owner = WorldOwner::new();
        let mut hasher = TokenHasher(0);
        owner.hash(&mut hasher);
        hasher.write(&[]);
        assert_ne!(hasher.finish(), 0);
    }
}