Skip to main content

wowlab_engine_domain/rotation/buffer/
descriptor_table.rs

1use wowlab_types::sim::FastMap;
2
3use super::{DescriptorId, FieldDescriptor};
4
5/// Indexed collection of all [`FieldDescriptor`]s registered via `inventory`.
6#[derive(Debug)]
7pub struct DescriptorTable {
8    descriptors: Vec<&'static FieldDescriptor>,
9    index: FastMap<&'static str, FastMap<&'static str, DescriptorId>>,
10}
11
12/// Iterator over descriptor identifiers and definitions.
13#[derive(Debug)]
14pub struct DescriptorIter<'a> {
15    inner: std::iter::Enumerate<std::slice::Iter<'a, &'static FieldDescriptor>>,
16}
17
18impl<'a> Iterator for DescriptorIter<'a> {
19    type Item = (DescriptorId, &'a FieldDescriptor);
20
21    fn next(&mut self) -> Option<Self::Item> {
22        let (index, descriptor) = self.inner.next()?;
23        let id = u16::try_from(index).ok()?;
24
25        Some((DescriptorId::new(id), *descriptor))
26    }
27
28    fn size_hint(&self) -> (usize, Option<usize>) {
29        self.inner.size_hint()
30    }
31}
32
33impl ExactSizeIterator for DescriptorIter<'_> {}
34impl std::iter::FusedIterator for DescriptorIter<'_> {}
35
36impl DescriptorTable {
37    /// Collect all registered descriptors and build the lookup table.
38    ///
39    /// # Panics
40    ///
41    /// Panics if more than `u16::MAX` descriptors are registered.
42    #[must_use]
43    pub fn build() -> Self {
44        let descriptors: Vec<&'static FieldDescriptor> =
45            inventory::iter::<FieldDescriptor>.into_iter().collect();
46        let mut index: FastMap<&'static str, FastMap<&'static str, DescriptorId>> =
47            FastMap::default();
48
49        for (i, d) in descriptors.iter().enumerate() {
50            let id = DescriptorId::new(
51                u16::try_from(i).expect("descriptor count exceeds u16 identifier space"),
52            );
53
54            index.entry(d.domain).or_default().insert(d.name, id);
55        }
56
57        Self { descriptors, index }
58    }
59
60    /// Returns the number of registered descriptors.
61    #[must_use]
62    pub fn len(&self) -> usize {
63        self.descriptors.len()
64    }
65
66    /// Returns whether no descriptors are registered.
67    #[must_use]
68    pub fn is_empty(&self) -> bool {
69        self.descriptors.is_empty()
70    }
71
72    /// Iterate all descriptors with their IDs.
73    #[must_use]
74    pub fn iter(&self) -> DescriptorIter<'_> {
75        DescriptorIter {
76            inner: self.descriptors.iter().enumerate(),
77        }
78    }
79
80    pub(crate) fn lookup(
81        &self,
82        domain: &str,
83        name: &str,
84    ) -> Option<(DescriptorId, &FieldDescriptor)> {
85        let &id = self.index.get(domain)?.get(name)?;
86
87        // #t(rust_unchecked_indexing) id was inserted from descriptors.enumerate() in build()
88        Some((id, self.descriptors[id.as_usize()]))
89    }
90
91    pub(crate) fn get(&self, id: DescriptorId) -> Option<&FieldDescriptor> {
92        self.descriptors.get(id.as_usize()).copied()
93    }
94
95    #[cfg(test)]
96    pub(crate) fn domain<'a>(
97        &'a self,
98        domain: &'a str,
99    ) -> impl Iterator<Item = (DescriptorId, &'a FieldDescriptor)> + 'a {
100        self.descriptors
101            .iter()
102            .enumerate()
103            .filter(move |(_, d)| d.domain == domain)
104            .filter_map(|(index, descriptor)| {
105                u16::try_from(index)
106                    .ok()
107                    .map(|id| (DescriptorId::new(id), *descriptor))
108            })
109    }
110}
111
112impl<'a> IntoIterator for &'a DescriptorTable {
113    type Item = (DescriptorId, &'a FieldDescriptor);
114    type IntoIter = DescriptorIter<'a>;
115
116    fn into_iter(self) -> Self::IntoIter {
117        self.iter()
118    }
119}