1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
use crate::*;

/// Contains data indexed by type.
/// World allows to dynamically enforce the rust rules of borrowing and ownership
/// at runtime:
/// - The same type cannot be borrowed immutably and mutably at the same time.
/// - The same type cannot be borrowed mutably more than once at the same time.
#[derive(Default)]
pub struct World {
    pub(crate) res:
        HashMap<TypeId, AtomicRefCell<Box<dyn Resource>>, BuildHasherDefault<TypeIdHasher>>,
}

impl World {
    /// Initializes a resource to its default value.
    /// This is the only way to "insert" a resource.
    ///
    /// It is suggested to use a macro to collect all
    /// the resources and initialize all of them.
    pub fn initialize<T: Default + Send + Sync + 'static>(&mut self) {
        if !self.res.contains_key(&TypeId::of::<T>()) {
            self.res.insert(
                TypeId::of::<T>(),
                AtomicRefCell::new(Box::new(T::default())),
            );
        }
    }
    /// Get an immutable reference to a resource by type.
    /// Will return an error if the type is:
    /// - Non initialized
    /// - Already borrowed mutably
    pub fn get<T: Send + Sync + 'static>(&self) -> Result<AtomicRef<T>, EcsError> {
        self.res
            .get(&TypeId::of::<T>())
            .ok_or(EcsError::NotInitialized)
            .and_then(|i| i.try_borrow().map_err(|_| EcsError::AlreadyBorrowed))
            .and_then(|i| Ok(AtomicRef::map(i, |j| j.downcast_ref::<T>().unwrap())))
    }
    /// Get a mutable reference to a resource by type.
    /// Will return an error if the type is:
    /// - Non initialized
    /// - Already borrowed immutably
    /// - Already borrowed mutably
    pub fn get_mut<T: Send + Sync + 'static>(&self) -> Result<AtomicRefMut<T>, EcsError> {
        self.res
            .get(&TypeId::of::<T>())
            .ok_or(EcsError::NotInitialized)
            .and_then(|i| i.try_borrow_mut().map_err(|_| EcsError::AlreadyBorrowed))
            .and_then(|i| Ok(AtomicRefMut::map(i, |j| j.downcast_mut::<T>().unwrap())))
    }

    /// Get a mutable reference to a resource by type, default-initializing it if not already
    /// initialized.
    pub fn get_mut_or_default<T: Default + Send + Sync + 'static>(&mut self) -> AtomicRefMut<T> {
        self.initialize::<T>();
        self.get_mut().unwrap()
    }

    /// Get a mutable reference to a resource by its type id. Useful if using
    /// dynamic dispatching.
    /// Will return an error if the type is:
    /// - Non initialized
    /// - Already borrowed immutably
    /// - Already borrowed mutably
    #[doc(hidden)]
    pub fn get_by_typeid(
        &self,
        typeid: &TypeId,
    ) -> Result<AtomicRefMut<Box<dyn Resource>>, EcsError> {
        self.res
            .get(typeid)
            .ok_or(EcsError::NotInitialized)
            .and_then(|i| i.try_borrow_mut().map_err(|_| EcsError::AlreadyBorrowed))
    }
}

#[cfg(test)]
mod tests {
    use crate::*;
    #[test]
    fn init_borrow() {
        let mut world = World::default();
        world.initialize::<u32>();
        *world.get_mut::<u32>().unwrap() = 5;
        *world.get_mut::<u32>().unwrap() = 6;
        {
            let _long_borrow = world.get::<u32>().unwrap();
            let _long_borrow2 = world.get::<u32>().unwrap();
            let failing_borrow = world.get_mut::<u32>();
            if let EcsError::AlreadyBorrowed = failing_borrow.err().unwrap() {
                // good
            } else {
                unreachable!();
            }
        }
        {
            let _long_borrow = world.get_mut::<u32>().unwrap();
            let failing_borrow = world.get::<u32>();
            if let EcsError::AlreadyBorrowed = failing_borrow.err().unwrap() {
                // good
            } else {
                unreachable!();
            }
        }
        assert_eq!(*world.get_mut::<u32>().unwrap(), 6);
    }

    #[test]
    fn init_or_default() {
        let mut world = World::default();

        let mut data = world.get_mut_or_default::<u32>();
        *data += 1;
    }
}