1use crate::accessor::NodeRef;
20use crate::dom::Dom;
21use crate::node_id::NodeId;
22
23pub struct NodeList<'a, Ext: 'static> {
28 nodes: Vec<NodeId>,
29 dom: &'a Dom<Ext>,
30}
31
32impl<'a, Ext: 'static> NodeList<'a, Ext> {
33 pub fn from_ids(dom: &'a Dom<Ext>, nodes: impl IntoIterator<Item = NodeId>) -> Self {
36 Self {
37 nodes: nodes.into_iter().collect(),
38 dom,
39 }
40 }
41
42 pub fn len(&self) -> usize {
44 self.nodes.len()
45 }
46
47 pub fn is_empty(&self) -> bool {
49 self.nodes.is_empty()
50 }
51
52 pub fn item(&self, index: usize) -> Option<NodeRef<'a, Ext>> {
56 let id = *self.nodes.get(index)?;
57 if self.dom.contains(id) {
58 Some(self.dom.node(id))
59 } else {
60 None
61 }
62 }
63
64 pub fn iter(&self) -> impl Iterator<Item = NodeRef<'a, Ext>> + '_ {
68 self.nodes
69 .iter()
70 .copied()
71 .filter(|id| self.dom.contains(*id))
72 .map(move |id| self.dom.node(id))
73 }
74
75 pub fn ids(&self) -> &[NodeId] {
79 &self.nodes
80 }
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86
87 #[test]
88 fn snapshot_len_item_ids() {
89 let mut dom: Dom = Dom::new();
90 let a = dom.create_element("a");
91 let b = dom.create_element("b");
92 let c = dom.create_element("c");
93 let list = NodeList::from_ids(&dom, [a, b, c]);
94
95 assert_eq!(list.len(), 3);
96 assert!(!list.is_empty());
97 assert_eq!(list.ids(), &[a, b, c][..]);
98
99 assert_eq!(list.item(0).map(|n| n.id()), Some(a));
100 assert_eq!(list.item(1).map(|n| n.id()), Some(b));
101 assert_eq!(list.item(2).map(|n| n.id()), Some(c));
102 assert_eq!(list.item(3).map(|n| n.id()), None);
103 }
104
105 #[test]
106 fn empty_snapshot_is_empty() {
107 let dom: Dom = Dom::new();
108 let list = NodeList::from_ids(&dom, Vec::<NodeId>::new());
109 assert!(list.is_empty());
110 assert_eq!(list.len(), 0);
111 assert_eq!(list.item(0).map(|n| n.id()), None);
112 }
113
114 #[test]
115 fn iter_yields_snapshot_order() {
116 let mut dom: Dom = Dom::new();
117 let a = dom.create_element("a");
118 let b = dom.create_element("b");
119 let list = NodeList::from_ids(&dom, [a, b]);
120 let ids: Vec<NodeId> = list.iter().map(|n| n.id()).collect();
121 assert_eq!(ids, vec![a, b]);
122 }
123
124 #[test]
125 fn removed_node_returns_none_from_item() {
126 let mut dom: Dom = Dom::new();
130 let root = dom.root();
131 let a = dom.create_element("a");
132 let b = dom.create_element("b");
133 dom.append_child(root, a).unwrap();
134 dom.append_child(root, b).unwrap();
135
136 let snapshot_ids = vec![a, b];
137 dom.remove_child(root, a).unwrap();
138 dom.drop_subtree(a).unwrap();
139
140 let list = NodeList::from_ids(&dom, snapshot_ids);
141 assert!(list.item(0).is_none());
143 assert_eq!(list.item(1).map(|n| n.id()), Some(b));
145 }
146
147 #[test]
148 fn iter_skips_removed_nodes() {
149 let mut dom: Dom = Dom::new();
150 let root = dom.root();
151 let a = dom.create_element("a");
152 let b = dom.create_element("b");
153 let c = dom.create_element("c");
154 dom.append_child(root, a).unwrap();
155 dom.append_child(root, b).unwrap();
156 dom.append_child(root, c).unwrap();
157
158 let snapshot_ids = vec![a, b, c];
159 dom.remove_child(root, b).unwrap();
160 dom.drop_subtree(b).unwrap();
161
162 let list = NodeList::from_ids(&dom, snapshot_ids);
163 let live: Vec<NodeId> = list.iter().map(|n| n.id()).collect();
164 assert_eq!(live, vec![a, c]);
165 }
166}