Skip to main content

nexus_rt/
resource.rs

1//! Shared and mutable resource references for system parameters.
2
3use std::cell::Cell;
4use std::ops::{Deref, DerefMut};
5
6use crate::world::Sequence;
7
8/// Shared reference to a resource in [`World`](crate::World).
9///
10/// Appears in system function signatures to declare a read dependency.
11/// Derefs to the inner value transparently. Carries change-detection
12/// metadata — call [`is_changed`](Self::is_changed) to check.
13///
14/// Construction is `pub(crate)` — only the dispatch layer creates these.
15pub struct Res<'w, T: 'static> {
16    value: &'w T,
17    changed_at: Sequence,
18    current_sequence: Sequence,
19}
20
21impl<'w, T: 'static> Res<'w, T> {
22    pub(crate) fn new(value: &'w T, changed_at: Sequence, current_sequence: Sequence) -> Self {
23        Self {
24            value,
25            changed_at,
26            current_sequence,
27        }
28    }
29
30    /// Returns `true` if the resource was modified during the current sequence.
31    pub fn is_changed(&self) -> bool {
32        self.changed_at == self.current_sequence
33    }
34}
35
36impl<T: 'static> Deref for Res<'_, T> {
37    type Target = T;
38
39    #[inline(always)]
40    fn deref(&self) -> &T {
41        self.value
42    }
43}
44
45/// Mutable reference to a resource in [`World`](crate::World).
46///
47/// Appears in system function signatures to declare a write dependency.
48/// Derefs to the inner value transparently. Stamps the resource's
49/// `changed_at` sequence on [`DerefMut`] — the act of writing is the
50/// change signal.
51///
52/// Construction is `pub(crate)` — only the dispatch layer creates these.
53pub struct ResMut<'w, T: 'static> {
54    value: &'w mut T,
55    changed_at: &'w Cell<Sequence>,
56    current_sequence: Sequence,
57}
58
59impl<'w, T: 'static> ResMut<'w, T> {
60    pub(crate) fn new(
61        value: &'w mut T,
62        changed_at: &'w Cell<Sequence>,
63        current_sequence: Sequence,
64    ) -> Self {
65        Self {
66            value,
67            changed_at,
68            current_sequence,
69        }
70    }
71
72    /// Returns `true` if the resource was modified during the current sequence.
73    pub fn is_changed(&self) -> bool {
74        self.changed_at.get() == self.current_sequence
75    }
76}
77
78impl<T: 'static> Deref for ResMut<'_, T> {
79    type Target = T;
80
81    #[inline(always)]
82    fn deref(&self) -> &T {
83        self.value
84    }
85}
86
87impl<T: 'static> DerefMut for ResMut<'_, T> {
88    #[inline(always)]
89    fn deref_mut(&mut self) -> &mut T {
90        self.changed_at.set(self.current_sequence);
91        self.value
92    }
93}
94
95#[cfg(test)]
96mod tests {
97    use super::*;
98
99    #[test]
100    fn res_deref() {
101        let val = 42u64;
102        let res = Res::new(&val, Sequence::default(), Sequence::default());
103        assert_eq!(*res, 42);
104    }
105
106    #[test]
107    fn res_is_changed() {
108        let val = 42u64;
109        let tick = Sequence::default();
110        let res = Res::new(&val, tick, tick);
111        assert!(res.is_changed());
112    }
113
114    #[test]
115    fn res_not_changed() {
116        let val = 42u64;
117        // changed_at=0, current_sequence=1 → not changed
118        let res = Res::new(&val, Sequence::default(), Sequence(1));
119        assert!(!res.is_changed());
120    }
121
122    #[test]
123    fn res_mut_deref_mut() {
124        let mut val = 1u64;
125        let changed_at = Cell::new(Sequence::default());
126        let mut res = ResMut::new(&mut val, &changed_at, Sequence::default());
127        *res = 99;
128        assert_eq!(*res, 99);
129        drop(res);
130        assert_eq!(val, 99);
131    }
132
133    #[test]
134    fn res_mut_deref_mut_stamps() {
135        let mut val = 1u64;
136        let changed_at = Cell::new(Sequence(0));
137        let current = Sequence(5);
138        let mut res = ResMut::new(&mut val, &changed_at, current);
139
140        // Before DerefMut — changed_at is still 0
141        assert_eq!(changed_at.get(), Sequence(0));
142
143        *res = 99;
144
145        // After DerefMut — changed_at stamped to current_sequence
146        assert_eq!(changed_at.get(), Sequence(5));
147    }
148
149    #[test]
150    fn res_mut_deref_does_not_stamp() {
151        let mut val = 42u64;
152        let changed_at = Cell::new(Sequence(0));
153        let current = Sequence(5);
154        let res = ResMut::new(&mut val, &changed_at, current);
155
156        // Deref (shared) — read only, should not stamp
157        let _ = *res;
158        assert_eq!(changed_at.get(), Sequence(0));
159    }
160
161    #[test]
162    fn res_mut_is_changed() {
163        let mut val = 1u64;
164        let changed_at = Cell::new(Sequence(3));
165        let res = ResMut::new(&mut val, &changed_at, Sequence(3));
166        assert!(res.is_changed());
167
168        let res2 = ResMut::new(&mut val, &changed_at, Sequence(4));
169        assert!(!res2.is_changed());
170    }
171}