use crate::tree::NodeId;
use astrelis_core::alloc::HashMap;
use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct WidgetId(u64);
impl WidgetId {
pub fn new(key: &str) -> Self {
Self(Self::hash_str(key))
}
pub const fn from_raw(id: u64) -> Self {
Self(id)
}
pub const fn as_u64(&self) -> u64 {
self.0
}
fn hash_str(s: &str) -> u64 {
const FNV_OFFSET_BASIS: u64 = 0xcbf29ce484222325;
const FNV_PRIME: u64 = 0x100000001b3;
let mut hash = FNV_OFFSET_BASIS;
for byte in s.as_bytes() {
hash ^= *byte as u64;
hash = hash.wrapping_mul(FNV_PRIME);
}
hash
}
}
impl fmt::Display for WidgetId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "WidgetId(0x{:016x})", self.0)
}
}
impl From<&str> for WidgetId {
fn from(s: &str) -> Self {
Self::new(s)
}
}
impl From<String> for WidgetId {
fn from(s: String) -> Self {
Self::new(&s)
}
}
pub struct WidgetIdRegistry {
id_to_node: HashMap<WidgetId, NodeId>,
node_to_id: HashMap<NodeId, WidgetId>,
}
impl WidgetIdRegistry {
pub fn new() -> Self {
Self {
id_to_node: HashMap::new(),
node_to_id: HashMap::new(),
}
}
pub fn register(&mut self, widget_id: WidgetId, node_id: NodeId) {
self.id_to_node.insert(widget_id, node_id);
self.node_to_id.insert(node_id, widget_id);
}
pub fn get_node(&self, widget_id: WidgetId) -> Option<NodeId> {
self.id_to_node.get(&widget_id).copied()
}
pub fn get_widget_id(&self, node_id: NodeId) -> Option<WidgetId> {
self.node_to_id.get(&node_id).copied()
}
pub fn contains(&self, widget_id: WidgetId) -> bool {
self.id_to_node.contains_key(&widget_id)
}
pub fn remove(&mut self, widget_id: WidgetId) {
if let Some(node_id) = self.id_to_node.remove(&widget_id) {
self.node_to_id.remove(&node_id);
}
}
pub fn clear(&mut self) {
self.id_to_node.clear();
self.node_to_id.clear();
}
pub fn len(&self) -> usize {
self.id_to_node.len()
}
pub fn is_empty(&self) -> bool {
self.id_to_node.is_empty()
}
pub fn iter(&self) -> impl Iterator<Item = (WidgetId, NodeId)> + '_ {
self.id_to_node.iter().map(|(&id, &node)| (id, node))
}
}
impl Default for WidgetIdRegistry {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_widget_id_creation() {
let id1 = WidgetId::new("button1");
let id2 = WidgetId::new("button1");
let id3 = WidgetId::new("button2");
assert_eq!(id1, id2);
assert_ne!(id1, id3);
}
#[test]
fn test_widget_id_from_str() {
let id1: WidgetId = "test".into();
let id2 = WidgetId::new("test");
assert_eq!(id1, id2);
}
#[test]
fn test_registry() {
let mut registry = WidgetIdRegistry::new();
let widget_id = WidgetId::new("test");
let node_id = NodeId(42);
registry.register(widget_id, node_id);
assert_eq!(registry.get_node(widget_id), Some(node_id));
assert_eq!(registry.get_widget_id(node_id), Some(widget_id));
assert!(registry.contains(widget_id));
registry.remove(widget_id);
assert_eq!(registry.get_node(widget_id), None);
}
#[test]
fn test_registry_clear() {
let mut registry = WidgetIdRegistry::new();
registry.register(WidgetId::new("a"), NodeId(1));
registry.register(WidgetId::new("b"), NodeId(2));
assert_eq!(registry.len(), 2);
registry.clear();
assert_eq!(registry.len(), 0);
assert!(registry.is_empty());
}
}