Skip to main content

steel_registry/
macros.rs

1/// Implements `RegistryExt` for a registry type.
2///
3/// Expects `$id_field` to be `Vec<&'static $Entry>`.
4#[macro_export]
5macro_rules! impl_registry_ext {
6    ($Registry:ty, $Entry:ty, $id_field:ident, $key_field:ident) => {
7        impl $crate::RegistryExt for $Registry {
8            type Entry = $Entry;
9
10            fn freeze(&mut self) {
11                self.allows_registering = false;
12            }
13
14            fn by_id(&self, id: usize) -> Option<&'static $Entry> {
15                self.$id_field.get(id).copied()
16            }
17
18            fn by_key(&self, key: &steel_utils::Identifier) -> Option<&'static $Entry> {
19                self.$key_field
20                    .get(key)
21                    .and_then(|&id| self.$id_field.get(id).copied())
22            }
23
24            fn id_from_key(&self, key: &steel_utils::Identifier) -> Option<usize> {
25                self.$key_field.get(key).copied()
26            }
27
28            fn len(&self) -> usize {
29                self.$id_field.len()
30            }
31
32            fn is_empty(&self) -> bool {
33                self.$id_field.is_empty()
34            }
35        }
36    };
37}
38
39/// Implements `RegistryEntry` for an entry type via hash map lookup.
40#[macro_export]
41macro_rules! impl_registry_entry {
42    ($Entry:ty, $global_field:ident) => {
43        impl $crate::RegistryEntry for $Entry {
44            fn key(&self) -> &steel_utils::Identifier {
45                &self.key
46            }
47
48            fn try_id(&self) -> Option<usize> {
49                use $crate::RegistryExt;
50                $crate::REGISTRY.$global_field.id_from_key(&self.key)
51            }
52        }
53    };
54}
55
56/// Implements the default register, replace, and iter methods in the registries
57#[macro_export]
58macro_rules! impl_standard_methods {
59    ($Registry:ty, $Entry:ty, $id_field:ident, $key_field:ident, $allow_registering:ident) => {
60        impl $Registry {
61            pub fn register(&mut self, entry: $Entry) -> usize {
62                assert!(
63                    self.$allow_registering,
64                    concat!(
65                        "Cannot register ",
66                        stringify!($Entry),
67                        " after registry has been frozen"
68                    )
69                );
70                let id = self.$id_field.len();
71                self.$id_field.push(entry);
72                self.$key_field.insert(entry.key.clone(), id);
73                id
74            }
75
76            pub fn iter(&self) -> impl Iterator<Item = (usize, $Entry)> + '_ {
77                self.$id_field
78                    .iter()
79                    .enumerate()
80                    .map(|(id, &entry)| (id, entry))
81            }
82        }
83
84        impl Default for $Registry {
85            fn default() -> Self {
86                Self::new()
87            }
88        }
89    };
90}
91
92/// Implements both `RegistryExt` and `RegistryEntry` for a standard registry.
93#[macro_export]
94macro_rules! impl_registry {
95    ($Registry:ty, $Entry:ty, $id_field:ident, $key_field:ident, $global_field:ident) => {
96        $crate::impl_registry_ext!($Registry, $Entry, $id_field, $key_field);
97        $crate::impl_registry_entry!($Entry, $global_field);
98    };
99}
100
101/// Implements `TaggedRegistryExt` for a registry with tag support.
102#[macro_export]
103macro_rules! impl_tagged_registry {
104    ($Registry:ty, $key_field:ident, $entity_name:literal) => {
105        impl $crate::TaggedRegistryExt for $Registry {
106            fn register_tag(&mut self, tag: steel_utils::Identifier, keys: &[&'static str]) {
107                assert!(
108                    self.allows_registering,
109                    "Cannot register tags after registry has been frozen"
110                );
111
112                let identifiers: Vec<steel_utils::Identifier> = keys
113                    .iter()
114                    .filter_map(|key| {
115                        let ident = steel_utils::registry::registry_vanilla_or_custom_tag(key);
116                        if self.$key_field.contains_key(&ident) {
117                            Some(ident)
118                        } else {
119                            None
120                        }
121                    })
122                    .collect();
123
124                self.tags.insert(tag, identifiers);
125            }
126
127            fn modify_tag(
128                &mut self,
129                tag: &steel_utils::Identifier,
130                f: impl FnOnce(Vec<steel_utils::Identifier>) -> Vec<steel_utils::Identifier>,
131            ) {
132                let existing = self.tags.remove(tag).unwrap_or_default();
133                let entries = f(existing)
134                    .into_iter()
135                    .filter(|key| {
136                        let exists = self.$key_field.contains_key(key);
137                        if !exists {
138                            tracing::error!(
139                                "{} {} not found in registry, skipping from tag {}",
140                                $entity_name,
141                                key,
142                                tag,
143                            );
144                        }
145                        exists
146                    })
147                    .collect();
148                self.tags.insert(tag.clone(), entries);
149            }
150
151            fn is_in_tag(&self, entry: &Self::Entry, tag: &steel_utils::Identifier) -> bool {
152                self.tags
153                    .get(tag)
154                    .is_some_and(|entries| entries.contains(&entry.key))
155            }
156
157            fn get_tag(&self, tag: &steel_utils::Identifier) -> Option<Vec<&'static Self::Entry>> {
158                use $crate::RegistryExt;
159                self.tags.get(tag).map(|idents| {
160                    idents
161                        .iter()
162                        .filter_map(|ident| self.by_key(ident))
163                        .collect()
164                })
165            }
166
167            fn iter_tag(
168                &self,
169                tag: &steel_utils::Identifier,
170            ) -> impl Iterator<Item = &'static Self::Entry> + '_ {
171                use $crate::RegistryExt;
172                self.tags
173                    .get(tag)
174                    .into_iter()
175                    .flat_map(|v| v.iter().filter_map(|ident| self.by_key(ident)))
176            }
177
178            fn tag_keys(&self) -> impl Iterator<Item = &steel_utils::Identifier> + '_ {
179                self.tags.keys()
180            }
181        }
182    };
183}