Skip to main content

steel_registry/
feature.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
86impl crate::RegistryEntry for ConfiguredFeature {
87    fn key(&self) -> &Identifier {
88        &self.key
89    }
90
91    fn try_id(&self) -> Option<usize> {
92        self.id.get().copied()
93    }
94}
95
96/// A placed feature registry entry.
97#[derive(Debug)]
98pub struct PlacedFeature {
99    /// Registry key.
100    pub key: Identifier,
101    /// Configured feature plus placement modifier chain.
102    pub data: PlacedFeatureData,
103    /// Cached registry ID.
104    pub id: OnceLock<usize>,
105}
106
107/// Read-only placed feature reference.
108pub type PlacedFeatureEntryRef = &'static PlacedFeature;
109
110/// Registry of placed features.
111pub struct PlacedFeatureRegistry {
112    features_by_id: Vec<PlacedFeatureEntryRef>,
113    features_by_key: FxHashMap<Identifier, usize>,
114    allows_registering: bool,
115}
116
117impl PlacedFeatureRegistry {
118    /// Creates an empty registry.
119    #[must_use]
120    pub fn new() -> Self {
121        Self {
122            features_by_id: Vec::new(),
123            features_by_key: FxHashMap::default(),
124            allows_registering: true,
125        }
126    }
127
128    /// Registers a placed feature and returns its numeric ID.
129    pub fn register(&mut self, entry: PlacedFeatureEntryRef) -> usize {
130        assert!(
131            self.allows_registering,
132            "Cannot register PlacedFeature after registry has been frozen"
133        );
134        let id = self.features_by_id.len();
135        let cached = entry.id.get_or_init(|| id);
136        assert_eq!(*cached, id, "placed feature registered with conflicting id");
137        self.features_by_id.push(entry);
138        self.features_by_key.insert(entry.key.clone(), id);
139        id
140    }
141
142    /// Iterates over all placed features.
143    pub fn iter(&self) -> impl Iterator<Item = (usize, PlacedFeatureEntryRef)> + '_ {
144        self.features_by_id
145            .iter()
146            .enumerate()
147            .map(|(id, &entry)| (id, entry))
148    }
149}
150
151impl Default for PlacedFeatureRegistry {
152    fn default() -> Self {
153        Self::new()
154    }
155}
156
157crate::impl_registry_ext!(
158    PlacedFeatureRegistry,
159    PlacedFeature,
160    features_by_id,
161    features_by_key
162);
163
164impl crate::RegistryEntry for PlacedFeature {
165    fn key(&self) -> &Identifier {
166        &self.key
167    }
168
169    fn try_id(&self) -> Option<usize> {
170        self.id.get().copied()
171    }
172}