//! ContentEntityRegistry resource (D-079). //! //! Maps NPC canonical_id strings (e.g., "kael-davan") to their runtime StableIds. //! Populated at NPC spawn time; queried at KnowledgeGrant processing time to //! resolve `entity_ref` strings in `KnowledgeGrant::Entity` variants. //! //! BTreeMap for deterministic iteration (D-010 principle 4). use bevy_ecs::prelude::*; use std::collections::BTreeMap; use super::types::StableId; /// Content ID → StableId registry. /// /// Populated by `spawn_npc` for every authored NPC. Read by the /// `KnowledgeGranted` event handler to resolve `entity_ref` strings at /// grant processing time (D-079). #[derive(Resource, Debug, Default)] pub struct ContentEntityRegistry { entries: BTreeMap, } impl ContentEntityRegistry { /// Register a content_id → StableId mapping. /// /// Idempotent for the same (content_id, stable_id) pair. /// If the same content_id is registered twice with different StableIds, /// the latest call wins (last-write semantics; warn in caller if this is unexpected). pub fn register(&mut self, content_id: impl Into, stable_id: StableId) { self.entries.insert(content_id.into(), stable_id); } /// Resolve a content_id string to a StableId. /// /// Returns `None` if the entity_ref is not registered. Callers should /// emit `tracing::warn!` and drop the grant when `None` is returned. pub fn resolve(&self, entity_ref: &str) -> Option { self.entries.get(entity_ref).copied() } /// Number of registered entries. pub fn len(&self) -> usize { self.entries.len() } /// Whether the registry is empty. pub fn is_empty(&self) -> bool { self.entries.is_empty() } } #[cfg(test)] mod tests { use super::*; #[test] fn register_and_resolve() { let mut registry = ContentEntityRegistry::default(); let sid = StableId(42); registry.register("kael-davan", sid); assert_eq!(registry.resolve("kael-davan"), Some(sid)); assert_eq!(registry.resolve("unknown-npc"), None); assert_eq!(registry.len(), 1); } #[test] fn register_overwrites() { let mut registry = ContentEntityRegistry::default(); let sid_a = StableId(1); let sid_b = StableId(2); registry.register("npc-x", sid_a); registry.register("npc-x", sid_b); assert_eq!(registry.resolve("npc-x"), Some(sid_b)); assert_eq!(registry.len(), 1); } #[test] fn empty_registry() { let registry = ContentEntityRegistry::default(); assert!(registry.is_empty()); assert_eq!(registry.resolve("any"), None); } }