1use crate::dom::Dom;
10use crate::node::{Node, NodeData};
11use crate::node_id::NodeId;
12
13impl<Ext: Clone> Dom<Ext> {
14 pub fn clone_node(&mut self, id: NodeId, deep: bool) -> NodeId {
20 let new_id = self.clone_one(id);
21 if !deep {
22 return new_id;
23 }
24 let first = |dom: &Self, n: NodeId| dom.get_node(n).and_then(|n| n.first_child);
30 let mut stack = vec![(new_id, first(self, id))];
31 while let Some(top) = stack.last_mut() {
32 let dst = top.0;
33 if let Some(src) = top.1 {
34 top.1 = self.get_node(src).and_then(|n| n.next_sibling);
35 let clone = self.clone_one(src);
36 stack.push((clone, first(self, src)));
37 continue;
38 }
39 stack.pop();
40 if let Some(&(parent, _)) = stack.last() {
41 self.append_child(parent, dst)
42 .expect("clone_node deep: append failed");
43 }
44 }
45 new_id
46 }
47
48 fn clone_one(&mut self, id: NodeId) -> NodeId {
51 let data = match &self.get_node(id).expect("clone_node: invalid id").data {
52 NodeData::Element {
53 tag,
54 attrs,
55 classes,
56 ext,
57 } => NodeData::Element {
58 tag: tag.clone(),
59 attrs: attrs.clone(),
60 classes: classes.clone(),
61 ext: ext.clone(),
62 },
63 NodeData::Text { data } => NodeData::Text { data: data.clone() },
64 NodeData::Comment { data } => NodeData::Comment { data: data.clone() },
65 NodeData::Fragment => NodeData::Fragment,
66 };
67 self.alloc(Node::new(data))
68 }
69}
70
71#[cfg(test)]
72mod tests {
73 use crate::Dom;
74
75 #[test]
76 fn shallow_clone_copies_tag_and_attrs() {
77 let mut dom: Dom = Dom::new();
78 let el = dom.create_element("div");
79 dom.set_attribute(el, "role", "banner").unwrap();
80 dom.add_class(el, "active").unwrap();
81
82 let c = dom.clone_node(el, false);
83 assert_eq!(dom.node(c).tag_name(), Some("div"));
84 assert_eq!(dom.node(c).get_attribute("role"), Some("banner"));
85 assert!(dom.node(c).has_class("active"));
86 assert!(dom.node(c).parent_node().is_none());
87 }
88
89 #[test]
90 fn shallow_clone_has_no_children() {
91 let mut dom: Dom = Dom::new();
92 let parent = dom.create_element("div");
93 let child = dom.create_element("span");
94 dom.append_child(parent, child).unwrap();
95
96 let c = dom.clone_node(parent, false);
97 assert!(!dom.node(c).has_child_nodes());
98 }
99
100 #[test]
101 fn deep_clone_recursively_copies_children() {
102 let mut dom: Dom = Dom::new();
103 let parent = dom.create_element("div");
104 let child = dom.create_element("span");
105 let text = dom.create_text_node("hello");
106 dom.append_child(child, text).unwrap();
107 dom.append_child(parent, child).unwrap();
108
109 let c = dom.clone_node(parent, true);
110 assert_eq!(dom.node(c).child_element_count(), 1);
111 let first = dom.node(c).first_element_child().unwrap();
112 assert_eq!(first.tag_name(), Some("span"));
113 let first_text = first.first_child().unwrap();
114 assert_eq!(first_text.node_value(), Some("hello"));
115 assert!(dom.is_equal_node(parent, c));
116 }
117
118 #[test]
119 fn clone_text_node() {
120 let mut dom: Dom = Dom::new();
121 let t = dom.create_text_node("hello");
122 let c = dom.clone_node(t, true);
123 assert_eq!(dom.node(c).node_value(), Some("hello"));
124 }
125
126 #[test]
127 fn clone_is_independent() {
128 let mut dom: Dom = Dom::new();
129 let el = dom.create_element("div");
130 dom.set_attribute(el, "class", "original").unwrap();
131
132 let c = dom.clone_node(el, false);
133 dom.set_attribute(c, "class", "modified").unwrap();
134
135 assert_eq!(dom.node(el).get_attribute("class"), Some("original"));
136 assert_eq!(dom.node(c).get_attribute("class"), Some("modified"));
137 }
138
139 #[test]
140 fn cloned_fragment_unwraps_normally_on_append() {
141 let mut dom: Dom = Dom::new();
142 let frag = dom.create_document_fragment();
143 let a = dom.create_element("a");
144 let b = dom.create_element("b");
145 dom.append_child(frag, a).unwrap();
146 dom.append_child(frag, b).unwrap();
147
148 let cloned_frag = dom.clone_node(frag, true);
149 let root = dom.root();
150 dom.append_child(root, cloned_frag).unwrap();
151 assert_eq!(dom.node(root).child_element_count(), 2);
153 }
154}