//! [`LayerRegistry`]: human-readable names for the 32 layers. use serde::{Deserialize, Serialize}; use super::{LayerMask, MAX_LAYERS}; /// Maps layer indices (`0..32`) to project-defined names. /// /// A [`LayerMask`] is just bits; the registry is what lets a project, the /// editor, and scripts talk about layer **3** as `"Enemy"` instead of a magic /// number. It is project-level data (serialized with the project, later stages) /// and changing a name never moves an entity between layers — only the label /// changes. /// /// Index `0` is seeded with the name `"Default"`, the layer every entity starts /// on (see [`Layer`](super::Layer)). The remaining slots are unnamed until a /// project assigns them. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct LayerRegistry { /// One slot per layer; `None` means the layer has no assigned name. names: Vec>, } impl LayerRegistry { /// A registry with only layer 0 named (`"Default"`). pub fn new() -> Self { let mut names = vec![None; MAX_LAYERS as usize]; names[0] = Some("Default".to_string()); Self { names } } /// Assigns `name` to layer `index`. /// /// # Panics /// Panics if `index >= 32`. pub fn set(&mut self, index: u32, name: impl Into) { assert!( index < MAX_LAYERS, "layer index out of range (must be 0..32)" ); self.names[index as usize] = Some(name.into()); } /// Clears the name of layer `index`, leaving it unnamed. /// /// # Panics /// Panics if `index >= 32`. pub fn clear(&mut self, index: u32) { assert!( index < MAX_LAYERS, "layer index out of range (must be 0..32)" ); self.names[index as usize] = None; } /// The name of layer `index`, or `None` if it is out of range or unnamed. pub fn name(&self, index: u32) -> Option<&str> { self.names.get(index as usize).and_then(|n| n.as_deref()) } /// The index of the layer named `name`, or `None` if no layer has it. /// /// Names are not required to be unique; the lowest matching index wins. pub fn index_of(&self, name: &str) -> Option { self.names .iter() .position(|n| n.as_deref() == Some(name)) .map(|i| i as u32) } /// A [`LayerMask`] built from layer names, skipping any that are unknown. /// /// Convenient for authoring filters by name, e.g. /// `registry.mask_of(["Player", "NPC"])`. pub fn mask_of<'a, I>(&self, names: I) -> LayerMask where I: IntoIterator, { names.into_iter().filter_map(|n| self.index_of(n)).collect() } /// Iterates `(index, name)` for every *named* layer, ascending by index. pub fn iter(&self) -> impl Iterator { self.names .iter() .enumerate() .filter_map(|(i, n)| n.as_deref().map(|name| (i as u32, name))) } /// The number of named layers. pub fn named_count(&self) -> usize { self.names.iter().filter(|n| n.is_some()).count() } } impl Default for LayerRegistry { fn default() -> Self { Self::new() } } #[cfg(test)] mod tests { use super::*; #[test] fn default_names_only_layer_zero() { let reg = LayerRegistry::new(); assert_eq!(reg.name(0), Some("Default")); assert_eq!(reg.name(1), None); assert_eq!(reg.named_count(), 1); } #[test] fn set_and_look_up_by_name() { let mut reg = LayerRegistry::new(); reg.set(1, "Player"); reg.set(2, "NPC"); reg.set(5, "Water"); assert_eq!(reg.name(2), Some("NPC")); assert_eq!(reg.index_of("Water"), Some(5)); assert_eq!(reg.index_of("Missing"), None); assert_eq!(reg.named_count(), 4); } #[test] fn mask_of_names_builds_a_filter() { let mut reg = LayerRegistry::new(); reg.set(1, "Player"); reg.set(2, "NPC"); let mask = reg.mask_of(["Player", "NPC", "Unknown"]); assert_eq!(mask, LayerMask::NONE.with(1).with(2)); } #[test] fn clear_unsets_a_name() { let mut reg = LayerRegistry::new(); reg.set(3, "Trigger"); assert_eq!(reg.index_of("Trigger"), Some(3)); reg.clear(3); assert_eq!(reg.name(3), None); assert_eq!(reg.index_of("Trigger"), None); } #[test] fn iter_visits_named_layers_in_order() { let mut reg = LayerRegistry::new(); reg.set(4, "B"); reg.set(2, "A"); let pairs: Vec<_> = reg.iter().collect(); assert_eq!(pairs, vec![(0, "Default"), (2, "A"), (4, "B")]); } #[test] fn round_trips_through_ron() { let mut reg = LayerRegistry::new(); reg.set(1, "Player"); reg.set(7, "Foliage"); let ron = ron::to_string(®).unwrap(); let back: LayerRegistry = ron::from_str(&ron).unwrap(); assert_eq!(reg, back); } }