steel_registry/stat/
registry.rs1use crate::stat::Stat;
2use crate::{RegistryEntry, RegistryExt};
3use rustc_hash::FxHashMap;
4use std::fmt::{Debug, Formatter};
5use std::marker::PhantomData;
6use std::sync::{LazyLock, OnceLock};
7use steel_utils::{Downcast, DowncastType, DowncastTypeKey, ErasedType, Identifier};
8
9#[expect(clippy::len_without_is_empty)]
12pub trait StatValueRegistry: ErasedType + Send + Sync + 'static {
13 fn len(&self) -> usize;
14 fn value_from_id(&self, id: usize) -> Option<&'static dyn StatValueRegistryEntry>;
15 fn value_from_key(&self, key: &Identifier) -> Option<&'static dyn StatValueRegistryEntry>;
16
17 fn key_from_id(&self, id: usize) -> Option<&'static Identifier> {
18 self.value_from_id(id)
19 .map(StatValueRegistryEntry::stat_value_key)
20 }
21 fn id_from_key(&self, key: &Identifier) -> Option<usize> {
22 self.value_from_key(key)
23 .map(StatValueRegistryEntry::stat_value_id)
24 }
25}
26
27impl<R> StatValueRegistry for R
28where
29 R: RegistryExt + ErasedType + Send + Sync + 'static,
30 R::Entry: StatValueRegistryEntry,
31{
32 fn len(&self) -> usize {
33 self.len()
34 }
35
36 fn value_from_id(&self, id: usize) -> Option<&'static dyn StatValueRegistryEntry> {
37 self.by_id(id)
38 .map(|value| value as &dyn StatValueRegistryEntry)
39 }
40
41 fn value_from_key(&self, key: &Identifier) -> Option<&'static dyn StatValueRegistryEntry> {
42 self.by_key(key)
43 .map(|value| value as &dyn StatValueRegistryEntry)
44 }
45}
46
47pub trait StatValueRegistryEntry: Send + Sync + 'static {
49 fn stat_value_key(&self) -> &Identifier;
52 fn stat_value_id(&self) -> usize;
53}
54
55impl<E> StatValueRegistryEntry for E
56where
57 E: RegistryEntry + Send + Sync,
58{
59 fn stat_value_key(&self) -> &Identifier {
60 self.key()
61 }
62
63 fn stat_value_id(&self) -> usize {
64 self.id()
65 }
66}
67
68pub struct StatType<R: RegistryExt> {
71 pub key: Identifier,
73
74 stat_type_entry_ref: OnceLock<StatTypeEntryRef>,
75 _phantom: PhantomData<R>,
76}
77
78impl<R: RegistryExt> StatType<R>
79where
80 R::Entry: StatValueRegistryEntry,
81{
82 pub(crate) const fn new(key: Identifier) -> Self {
84 Self {
85 key,
86 stat_type_entry_ref: OnceLock::new(),
87 _phantom: PhantomData,
88 }
89 }
90
91 #[must_use]
93 pub const fn key(&self) -> &Identifier {
94 &self.key
95 }
96
97 pub fn stat_type_entry_ref(&self) -> StatTypeEntryRef {
103 self.stat_type_entry_ref
104 .get()
105 .expect("attempted to get the entry reference of an unregistered stat type")
106 }
107
108 pub fn get(&'static self, value: &'static R::Entry) -> Stat {
114 Stat::new(self, value)
115 }
116}
117
118pub type StatTypeRef<R> = &'static StatType<R>;
119
120#[derive(Copy, Clone)]
124pub struct StatValueRegistryData {
125 value: &'static dyn StatValueRegistry,
126}
127
128impl StatValueRegistryData {
129 #[must_use]
131 pub fn new(value: &'static dyn StatValueRegistry) -> Self {
132 Self { value }
133 }
134
135 #[must_use]
137 pub fn downcast_ref<R: StatValueRegistry + DowncastType>(&self) -> Option<&'static R> {
138 (*self.value).downcast_ref::<R>()
139 }
140
141 #[must_use]
143 pub fn type_key(&self) -> DowncastTypeKey {
144 self.value.downcast_type_key()
145 }
146}
147
148pub struct StatTypeEntry {
160 pub key: Identifier,
162
163 registry:
165 LazyLock<StatValueRegistryData, Box<dyn FnOnce() -> StatValueRegistryData + Send + Sync>>,
166}
167
168impl StatTypeEntry {
169 pub fn registry_len(&self) -> usize {
171 self.registry.value.len()
172 }
173
174 pub fn key_from_id(&self, id: usize) -> Option<&Identifier> {
176 self.registry.value.key_from_id(id)
177 }
178
179 pub fn id_from_key(&self, key: &Identifier) -> Option<usize> {
181 self.registry.value.id_from_key(key)
182 }
183
184 pub fn value_from_id(&self, id: usize) -> Option<&'static dyn StatValueRegistryEntry> {
186 self.registry.value.value_from_id(id)
187 }
188
189 pub fn value_from_key(&self, key: &Identifier) -> Option<&'static dyn StatValueRegistryEntry> {
191 self.registry.value.value_from_key(key)
192 }
193}
194
195impl Debug for StatTypeEntry {
196 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
197 f.debug_tuple("StatTypeEntry").field(&self.key).finish()
198 }
199}
200
201pub type StatTypeEntryRef = &'static StatTypeEntry;
202
203pub struct StatTypeRegistry {
209 stat_types_by_id: Vec<StatTypeEntryRef>,
211 stat_types_by_key: FxHashMap<Identifier, usize>,
213 allows_registering: bool,
215}
216
217impl StatTypeRegistry {
218 #[must_use]
220 pub fn new() -> Self {
221 Self {
222 stat_types_by_id: Vec::new(),
223 stat_types_by_key: FxHashMap::default(),
224 allows_registering: true,
225 }
226 }
227
228 pub fn register<R, F>(&mut self, stat_type: StatTypeRef<R>, registry_supplier: F)
233 where
234 R: RegistryExt + StatValueRegistry,
235 F: (FnOnce() -> &'static R) + Send + Sync + 'static,
236 {
237 assert!(
238 self.allows_registering,
239 "Cannot register stat types after the registry has been frozen"
240 );
241
242 let key = &stat_type.key;
243 assert!(
244 !self.stat_types_by_key.contains_key(key),
245 "Cannot register duplicate stat type key {key}"
246 );
247
248 let entry = StatTypeEntry {
249 key: stat_type.key.clone(),
250 registry: LazyLock::new(Box::new(|| StatValueRegistryData::new(registry_supplier()))),
251 };
252
253 let entry_ref = Box::leak(Box::new(entry));
254 let id = self.stat_types_by_id.len();
255
256 let locked_ref = stat_type.stat_type_entry_ref.get_or_init(|| entry_ref);
257 assert_eq!(entry_ref, *locked_ref);
258
259 self.stat_types_by_id.push(entry_ref);
260 self.stat_types_by_key.insert(stat_type.key.clone(), id);
261 }
262
263 pub fn iter(&self) -> impl Iterator<Item = (usize, StatTypeEntryRef)> + '_ {
265 self.stat_types_by_id
266 .iter()
267 .enumerate()
268 .map(|(id, &entry)| (id, entry))
269 }
270}
271
272impl Default for StatTypeRegistry {
273 fn default() -> Self {
274 Self::new()
275 }
276}
277
278crate::impl_registry!(
279 StatTypeRegistry,
280 StatTypeEntry,
281 stat_types_by_id,
282 stat_types_by_key,
283 stat_types
284);