Skip to main content

steel_registry/feature/
mod.rs

1//! Configured and placed feature registries.
2//!
3//! Configured features describe *what* to place. Placed features pair a
4//! configured feature with vanilla's ordered placement modifier chain.
5
6use std::sync::OnceLock;
7
8use rustc_hash::FxHashMap;
9use steel_utils::Identifier;
10
11pub mod data;
12
13pub use data::*;
14
15/// A fully-configured feature registry entry.
16#[derive(Debug)]
17pub struct ConfiguredFeature {
18    /// Registry key.
19    pub key: Identifier,
20    /// Typed feature configuration.
21    pub kind: ConfiguredFeatureKind,
22    /// Cached registry ID.
23    pub id: OnceLock<usize>,
24}
25
26/// Read-only configured feature reference.
27pub type ConfiguredFeatureEntryRef = &'static ConfiguredFeature;
28
29/// Registry of configured features.
30pub struct ConfiguredFeatureRegistry {
31    features_by_id: Vec<ConfiguredFeatureEntryRef>,
32    features_by_key: FxHashMap<Identifier, usize>,
33    allows_registering: bool,
34}
35
36impl ConfiguredFeatureRegistry {
37    /// Creates an empty registry.
38    #[must_use]
39    pub fn new() -> Self {
40        Self {
41            features_by_id: Vec::new(),
42            features_by_key: FxHashMap::default(),
43            allows_registering: true,
44        }
45    }
46
47    /// Registers a configured feature and returns its numeric ID.
48    pub fn register(&mut self, entry: ConfiguredFeatureEntryRef) -> usize {
49        assert!(
50            self.allows_registering,
51            "Cannot register ConfiguredFeature after registry has been frozen"
52        );
53        let id = self.features_by_id.len();
54        let cached = entry.id.get_or_init(|| id);
55        assert_eq!(
56            *cached, id,
57            "configured feature registered with conflicting id"
58        );
59        self.features_by_id.push(entry);
60        self.features_by_key.insert(entry.key.clone(), id);
61        id
62    }
63
64    /// Iterates over all configured features.
65    pub fn iter(&self) -> impl Iterator<Item = (usize, ConfiguredFeatureEntryRef)> + '_ {
66        self.features_by_id
67            .iter()
68            .enumerate()
69            .map(|(id, &entry)| (id, entry))
70    }
71}
72
73impl Default for ConfiguredFeatureRegistry {
74    fn default() -> Self {
75        Self::new()
76    }
77}
78
79crate::impl_registry_ext!(
80    ConfiguredFeatureRegistry,
81    ConfiguredFeature,
82    features_by_id,
83    features_by_key
84);
85
86crate::impl_registry_entry_eq!(ConfiguredFeature);
87
88impl crate::RegistryEntry for ConfiguredFeature {
89    fn key(&self) -> &Identifier {
90        &self.key
91    }
92
93    fn try_id(&self) -> Option<usize> {
94        self.id.get().copied()
95    }
96}
97
98/// A placed feature registry entry.
99#[derive(Debug)]
100pub struct PlacedFeature {
101    /// Registry key.
102    pub key: Identifier,
103    /// Configured feature plus placement modifier chain.
104    pub data: PlacedFeatureData,
105    /// Cached registry ID.
106    pub id: OnceLock<usize>,
107}
108
109/// Read-only placed feature reference.
110pub type PlacedFeatureEntryRef = &'static PlacedFeature;
111
112/// Registry of placed features.
113pub struct PlacedFeatureRegistry {
114    features_by_id: Vec<PlacedFeatureEntryRef>,
115    features_by_key: FxHashMap<Identifier, usize>,
116    allows_registering: bool,
117}
118
119impl PlacedFeatureRegistry {
120    /// Creates an empty registry.
121    #[must_use]
122    pub fn new() -> Self {
123        Self {
124            features_by_id: Vec::new(),
125            features_by_key: FxHashMap::default(),
126            allows_registering: true,
127        }
128    }
129
130    /// Registers a placed feature and returns its numeric ID.
131    pub fn register(&mut self, entry: PlacedFeatureEntryRef) -> usize {
132        assert!(
133            self.allows_registering,
134            "Cannot register PlacedFeature after registry has been frozen"
135        );
136        let id = self.features_by_id.len();
137        let cached = entry.id.get_or_init(|| id);
138        assert_eq!(*cached, id, "placed feature registered with conflicting id");
139        self.features_by_id.push(entry);
140        self.features_by_key.insert(entry.key.clone(), id);
141        id
142    }
143
144    /// Iterates over all placed features.
145    pub fn iter(&self) -> impl Iterator<Item = (usize, PlacedFeatureEntryRef)> + '_ {
146        self.features_by_id
147            .iter()
148            .enumerate()
149            .map(|(id, &entry)| (id, entry))
150    }
151}
152
153impl Default for PlacedFeatureRegistry {
154    fn default() -> Self {
155        Self::new()
156    }
157}
158
159crate::impl_registry_ext!(
160    PlacedFeatureRegistry,
161    PlacedFeature,
162    features_by_id,
163    features_by_key
164);
165
166crate::impl_registry_entry_eq!(PlacedFeature);
167
168impl crate::RegistryEntry for PlacedFeature {
169    fn key(&self) -> &Identifier {
170        &self.key
171    }
172
173    fn try_id(&self) -> Option<usize> {
174        self.id.get().copied()
175    }
176}