Skip to main content

blitz_dom/
tree.rs

1//! Versioned storage for the nodes of the DOM tree.
2
3use std::ops::{Index, IndexMut};
4
5use blitz_traits::node_id::NodeId;
6use slotmap::{Key as _, KeyData, SlotMap};
7
8use crate::Node;
9
10slotmap::new_key_type! {
11    /// The internal [`slotmap`] key for node storage. Only used at the
12    /// storage boundary: all public APIs use [`NodeId`].
13    struct NodeKey;
14}
15
16#[inline(always)]
17fn to_key(id: NodeId) -> NodeKey {
18    NodeKey::from(KeyData::from_ffi(id.as_u64()))
19}
20
21#[inline(always)]
22fn to_id(key: NodeKey) -> NodeId {
23    NodeId::from_u64(key.data().as_ffi())
24}
25
26/// The versioned map in which the nodes of the DOM tree are stored, backed by
27/// a [`slotmap::SlotMap`].
28///
29/// Nodes are addressed by [`NodeId`], which carries the slot's version in
30/// addition to its index: when a node is dropped and its slot reused, ids
31/// referring to the dropped node no longer resolve ([`NodeTree::get`] returns
32/// `None`, and indexing panics) instead of aliasing the new occupant.
33pub struct NodeTree(SlotMap<NodeKey, Node>);
34
35impl NodeTree {
36    pub(crate) fn new() -> Self {
37        Self(SlotMap::with_key())
38    }
39
40    /// The number of live nodes in the map.
41    pub fn len(&self) -> usize {
42        self.0.len()
43    }
44
45    pub fn is_empty(&self) -> bool {
46        self.0.is_empty()
47    }
48
49    /// Whether `id` resolves to a live node.
50    pub fn contains_key(&self, id: NodeId) -> bool {
51        self.0.contains_key(to_key(id))
52    }
53
54    /// Get a reference to the node with the given id, if it is still live.
55    pub fn get(&self, id: NodeId) -> Option<&Node> {
56        self.0.get(to_key(id))
57    }
58
59    /// Get a mutable reference to the node with the given id, if it is still live.
60    pub fn get_mut(&mut self, id: NodeId) -> Option<&mut Node> {
61        self.0.get_mut(to_key(id))
62    }
63
64    /// Insert a node constructed with knowledge of its own id.
65    pub(crate) fn insert_with_key(&mut self, f: impl FnOnce(NodeId) -> Node) -> NodeId {
66        to_id(self.0.insert_with_key(|key| f(to_id(key))))
67    }
68
69    /// Remove the node with the given id, returning it if it was still live.
70    pub(crate) fn remove(&mut self, id: NodeId) -> Option<Node> {
71        self.0.remove(to_key(id))
72    }
73
74    /// Iterate over all live `(NodeId, &Node)` pairs.
75    pub fn iter(&self) -> impl Iterator<Item = (NodeId, &Node)> {
76        self.0.iter().map(|(key, node)| (to_id(key), node))
77    }
78
79    /// Iterate over all live `(NodeId, &mut Node)` pairs.
80    pub fn iter_mut(&mut self) -> impl Iterator<Item = (NodeId, &mut Node)> {
81        self.0.iter_mut().map(|(key, node)| (to_id(key), node))
82    }
83}
84
85impl Index<NodeId> for NodeTree {
86    type Output = Node;
87
88    #[track_caller]
89    #[inline]
90    fn index(&self, id: NodeId) -> &Node {
91        &self.0[to_key(id)]
92    }
93}
94
95impl IndexMut<NodeId> for NodeTree {
96    #[track_caller]
97    #[inline]
98    fn index_mut(&mut self, id: NodeId) -> &mut Node {
99        &mut self.0[to_key(id)]
100    }
101}