amaze/
room4_list.rs

1use crate::room4::Room4;
2use std::fmt::{Debug, Display, Formatter};
3use std::num::NonZeroUsize;
4use std::ops::{Deref, Index, IndexMut};
5
6/// An index for addressing the room in the room list.
7#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
8pub struct RoomIndex(NonZeroUsize);
9
10#[derive(Debug, Default)]
11pub struct Room4List<Tag = ()> {
12    rooms: Vec<Room4<Tag>>,
13}
14
15pub(crate) trait EnsureIndexConsistency<Tag> {
16    /// Verifies and ensures that neighbor indexes are correctly set in the list.
17    fn propagate_index_to_neighbors(&self, list: &mut Room4List<Tag>);
18}
19
20impl<Tag> Room4List<Tag> {
21    /// Adds a new, empty room to the list.
22    ///
23    /// ## Arguments
24    /// * `tag` - The tag to pass to the room.
25    ///
26    /// ## Returns
27    /// The index of the newly created room.
28    pub fn push_default(&mut self, tag: Tag) -> RoomIndex {
29        self.push_new(tag, |_| {})
30    }
31
32    /// Adds a new room to the list and configures it.
33    ///
34    /// ## Remarks
35    /// This method ensures that all neighboring indexes are correctly configured.
36    ///
37    /// ## Arguments
38    /// * `tag` - The tag to pass to the room.
39    /// * `f` - A function to configure the room after creation.
40    ///
41    /// ## Returns
42    /// The index of the newly created room.
43    pub fn push_new<F>(&mut self, tag: Tag, f: F) -> RoomIndex
44    where
45        F: FnOnce(&mut Room4<Tag>),
46    {
47        // SAFETY:
48        // By increasing the current size of the index by 1,
49        // the resulting value is always nonzero.
50        let index = RoomIndex(unsafe { NonZeroUsize::new_unchecked(self.rooms.len() + 1) });
51
52        let mut room = Room4::new_empty(index, tag);
53        f(&mut room);
54
55        // Update indexes in all neighbors.
56        if room.has_neighbors() {
57            room.propagate_index_to_neighbors(self);
58        }
59
60        self.rooms.push(room);
61        index
62    }
63
64    /// Returns the number of rooms in this list.
65    pub fn len(&self) -> usize {
66        self.rooms.len()
67    }
68
69    /// Indicates whether this list is empty.
70    pub fn is_empty(&self) -> bool {
71        self.rooms.is_empty()
72    }
73
74    pub fn get(&self, index: RoomIndex) -> Option<&Room4<Tag>> {
75        let index = (*index).get() - 1;
76        self.rooms.get(index)
77    }
78
79    pub fn get_mut(&mut self, index: RoomIndex) -> Option<&mut Room4<Tag>> {
80        let index = (*index).get() - 1;
81        self.rooms.get_mut(index)
82    }
83}
84
85impl<Tag> Index<RoomIndex> for Room4List<Tag> {
86    type Output = Room4<Tag>;
87
88    fn index(&self, index: RoomIndex) -> &Self::Output {
89        let index = (*index).get() - 1;
90        &self.rooms[index]
91    }
92}
93
94impl<Tag> Index<usize> for Room4List<Tag> {
95    type Output = Room4<Tag>;
96
97    fn index(&self, index: usize) -> &Self::Output {
98        &self.rooms[index]
99    }
100}
101
102impl<Tag> IndexMut<RoomIndex> for Room4List<Tag> {
103    fn index_mut(&mut self, index: RoomIndex) -> &mut Self::Output {
104        let index = (*index).get() - 1;
105        &mut self.rooms[index]
106    }
107}
108
109impl<Tag> IndexMut<usize> for Room4List<Tag> {
110    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
111        &mut self.rooms[index]
112    }
113}
114
115impl RoomIndex {
116    pub fn from(value: usize) -> Option<RoomIndex> {
117        if value == usize::MAX {
118            return None;
119        }
120        NonZeroUsize::new(value + 1).map(RoomIndex)
121    }
122}
123
124impl Deref for RoomIndex {
125    type Target = NonZeroUsize;
126
127    fn deref(&self) -> &Self::Target {
128        &self.0
129    }
130}
131
132impl Debug for RoomIndex {
133    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
134        write!(f, "{}", self.0.get() - 1)
135    }
136}
137
138impl Display for RoomIndex {
139    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
140        write!(f, "{:?}", self)
141    }
142}
143
144pub struct Room4ListIntoIterator<Tag> {
145    list: Vec<Room4<Tag>>,
146}
147
148impl<Tag> Iterator for Room4ListIntoIterator<Tag> {
149    type Item = Room4<Tag>;
150
151    fn next(&mut self) -> Option<Self::Item> {
152        self.list.pop()
153    }
154}
155
156impl<Tag> IntoIterator for Room4List<Tag> {
157    type Item = Room4<Tag>;
158    type IntoIter = Room4ListIntoIterator<Tag>;
159
160    fn into_iter(self) -> Self::IntoIter {
161        Self::IntoIter { list: self.rooms }
162    }
163}
164
165#[cfg(test)]
166mod tests {
167    use super::*;
168
169    #[test]
170    fn get_returns_some() {
171        let mut list = Room4List::default();
172        let idx_a = list.push_default("a");
173        let idx_b = list.push_default("b");
174        assert_eq!(list.get(idx_a).unwrap().tag, "a");
175        assert_eq!(list.get(idx_b).unwrap().tag, "b");
176    }
177
178    #[test]
179    fn index_returns_some() {
180        let mut list = Room4List::default();
181        let idx_a = list.push_default("a");
182        let idx_b = list.push_default("b");
183        assert_ne!(idx_a, idx_b);
184
185        // index via RoomIndex
186        assert_eq!(list[idx_a].tag, "a");
187        assert_eq!(list[idx_b].tag, "b");
188
189        // index via usize
190        assert_eq!(list[0].tag, "a");
191        assert_eq!(list[1].tag, "b");
192    }
193
194    #[test]
195    fn get_mut_returns_some() {
196        let mut list = Room4List::default();
197        let idx_a = list.push_default("a");
198        let idx_b = list.push_default("b");
199        assert_eq!(list.get_mut(idx_a).unwrap().tag, "a");
200        assert_eq!(list.get_mut(idx_b).unwrap().tag, "b");
201    }
202
203    #[test]
204    fn index_mut_returns_some() {
205        let mut list = Room4List::default();
206        let idx_a = list.push_default("a");
207        let idx_b = list.push_default("b");
208
209        // index via RoomIndex
210        let room_a = &mut list[idx_a];
211        assert_eq!(room_a.tag, "a");
212        let room_b = &mut list[idx_b];
213        assert_eq!(room_b.tag, "b");
214
215        // index via usize
216        let room_a = &mut list[0];
217        assert_eq!(room_a.tag, "a");
218        let room_b = &mut list[1];
219        assert_eq!(room_b.tag, "b");
220    }
221
222    #[test]
223    fn get_with_invalid_index_returns_none() {
224        let mut list = Room4List::default();
225        let _ = list.push_default("a");
226
227        let invalid_idx = RoomIndex(unsafe { NonZeroUsize::new_unchecked(2) });
228        assert!(list.get(invalid_idx).is_none());
229    }
230}