use crate::accessor::NodeRef;
use crate::dom::Dom;
use crate::node_id::NodeId;
pub struct NodeList<'a, Ext: 'static> {
nodes: Vec<NodeId>,
dom: &'a Dom<Ext>,
}
impl<'a, Ext: 'static> NodeList<'a, Ext> {
pub fn from_ids(dom: &'a Dom<Ext>, nodes: impl IntoIterator<Item = NodeId>) -> Self {
Self {
nodes: nodes.into_iter().collect(),
dom,
}
}
pub fn len(&self) -> usize {
self.nodes.len()
}
pub fn is_empty(&self) -> bool {
self.nodes.is_empty()
}
pub fn item(&self, index: usize) -> Option<NodeRef<'a, Ext>> {
let id = *self.nodes.get(index)?;
if self.dom.contains(id) {
Some(self.dom.node(id))
} else {
None
}
}
pub fn iter(&self) -> impl Iterator<Item = NodeRef<'a, Ext>> + '_ {
self.nodes
.iter()
.copied()
.filter(|id| self.dom.contains(*id))
.map(move |id| self.dom.node(id))
}
pub fn ids(&self) -> &[NodeId] {
&self.nodes
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn snapshot_len_item_ids() {
let mut dom: Dom = Dom::new();
let a = dom.create_element("a");
let b = dom.create_element("b");
let c = dom.create_element("c");
let list = NodeList::from_ids(&dom, [a, b, c]);
assert_eq!(list.len(), 3);
assert!(!list.is_empty());
assert_eq!(list.ids(), &[a, b, c][..]);
assert_eq!(list.item(0).map(|n| n.id()), Some(a));
assert_eq!(list.item(1).map(|n| n.id()), Some(b));
assert_eq!(list.item(2).map(|n| n.id()), Some(c));
assert_eq!(list.item(3).map(|n| n.id()), None);
}
#[test]
fn empty_snapshot_is_empty() {
let dom: Dom = Dom::new();
let list = NodeList::from_ids(&dom, Vec::<NodeId>::new());
assert!(list.is_empty());
assert_eq!(list.len(), 0);
assert_eq!(list.item(0).map(|n| n.id()), None);
}
#[test]
fn iter_yields_snapshot_order() {
let mut dom: Dom = Dom::new();
let a = dom.create_element("a");
let b = dom.create_element("b");
let list = NodeList::from_ids(&dom, [a, b]);
let ids: Vec<NodeId> = list.iter().map(|n| n.id()).collect();
assert_eq!(ids, vec![a, b]);
}
#[test]
fn removed_node_returns_none_from_item() {
let mut dom: Dom = Dom::new();
let root = dom.root();
let a = dom.create_element("a");
let b = dom.create_element("b");
dom.append_child(root, a).unwrap();
dom.append_child(root, b).unwrap();
let snapshot_ids = vec![a, b];
dom.remove_child(root, a).unwrap();
dom.drop_subtree(a).unwrap();
let list = NodeList::from_ids(&dom, snapshot_ids);
assert!(list.item(0).is_none());
assert_eq!(list.item(1).map(|n| n.id()), Some(b));
}
#[test]
fn iter_skips_removed_nodes() {
let mut dom: Dom = Dom::new();
let root = dom.root();
let a = dom.create_element("a");
let b = dom.create_element("b");
let c = dom.create_element("c");
dom.append_child(root, a).unwrap();
dom.append_child(root, b).unwrap();
dom.append_child(root, c).unwrap();
let snapshot_ids = vec![a, b, c];
dom.remove_child(root, b).unwrap();
dom.drop_subtree(b).unwrap();
let list = NodeList::from_ids(&dom, snapshot_ids);
let live: Vec<NodeId> = list.iter().map(|n| n.id()).collect();
assert_eq!(live, vec![a, c]);
}
}