kozan_core/events/
path.rs1use crate::dom::document_cell::DocumentCell;
10use crate::id::{INVALID, RawId};
11
12pub struct EventPath {
20 entries: Vec<(u32, u32)>,
22}
23
24impl EventPath {
25 pub(crate) fn build(cell: DocumentCell, target: RawId) -> Self {
30 let mut entries = Vec::new();
31
32 if !cell.read(|doc| doc.is_alive_id(target)) {
33 return Self { entries };
34 }
35
36 entries.push((target.index(), target.generation()));
37
38 let mut current = target.index();
40 loop {
41 let Some(cur_gen) = cell.read(|doc| doc.generation(current)) else {
42 break;
43 };
44 let Some(tree) = cell.read(|doc| doc.tree_data(RawId::new(current, cur_gen))) else {
45 break;
46 };
47
48 if tree.parent == INVALID {
49 break;
50 }
51
52 let Some(parent_gen) = cell.read(|doc| doc.generation(tree.parent)) else {
53 break;
54 };
55
56 entries.push((tree.parent, parent_gen));
57 current = tree.parent;
58 }
59
60 Self { entries }
61 }
62
63 #[must_use]
65 pub fn len(&self) -> usize {
66 self.entries.len()
67 }
68
69 #[must_use]
71 pub fn is_empty(&self) -> bool {
72 self.entries.is_empty()
73 }
74
75 #[must_use]
77 pub fn target(&self) -> Option<(u32, u32)> {
78 self.entries.first().copied()
79 }
80
81 #[must_use]
83 pub fn target_index(&self) -> usize {
84 0
85 }
86
87 pub fn capture_order(&self) -> impl Iterator<Item = (usize, u32, u32)> + '_ {
89 self.entries
90 .iter()
91 .enumerate()
92 .rev()
93 .map(|(i, &(idx, generation))| (i, idx, generation))
94 }
95
96 pub fn bubble_order(&self) -> impl Iterator<Item = (usize, u32, u32)> + '_ {
98 self.entries
99 .iter()
100 .enumerate()
101 .map(|(i, &(idx, generation))| (i, idx, generation))
102 }
103
104 #[must_use]
106 pub fn get(&self, pos: usize) -> Option<(u32, u32)> {
107 self.entries.get(pos).copied()
108 }
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114 use crate::dom::document::Document;
115 use crate::dom::traits::{ContainerNode, HasHandle};
116 use crate::html::{HtmlButtonElement, HtmlDivElement};
117
118 #[test]
119 fn path_target_to_root_ordering() {
120 let doc = Document::new();
121 let div = doc.create::<HtmlDivElement>();
122 let btn = doc.create::<HtmlButtonElement>();
123 doc.root().append(div);
124 div.append(btn);
125
126 let cell = doc.cell();
127 let path = EventPath::build(cell, btn.handle().raw());
128
129 assert_eq!(path.len(), 3);
131 assert_eq!(
132 path.target().expect("non-empty path").0,
133 btn.handle().raw().index()
134 );
135 let (root_idx, _) = path.get(2).expect("root entry");
137 assert_eq!(root_idx, doc.root().raw().index());
138 }
139
140 #[test]
141 fn path_single_node_at_root() {
142 let doc = Document::new();
143 let cell = doc.cell();
144 let root_id = doc.root().raw();
145 let path = EventPath::build(cell, root_id);
146
147 assert_eq!(path.len(), 1);
149 assert_eq!(path.target().expect("non-empty").0, root_id.index());
150 }
151
152 #[test]
153 fn path_dead_target_produces_empty_path() {
154 let doc = Document::new();
155 let btn = doc.create::<HtmlButtonElement>();
156 let raw = btn.handle().raw();
157 btn.handle().destroy();
158
159 let cell = doc.cell();
160 let path = EventPath::build(cell, raw);
161 assert!(path.is_empty());
162 }
163
164 #[test]
165 fn capture_order_is_root_to_target() {
166 let doc = Document::new();
167 let div = doc.create::<HtmlDivElement>();
168 let btn = doc.create::<HtmlButtonElement>();
169 doc.root().append(div);
170 div.append(btn);
171
172 let cell = doc.cell();
173 let path = EventPath::build(cell, btn.handle().raw());
174
175 let capture: Vec<u32> = path.capture_order().map(|(_, idx, _)| idx).collect();
176 assert_eq!(capture[0], doc.root().raw().index());
178 assert_eq!(capture[1], div.handle().raw().index());
179 assert_eq!(capture[2], btn.handle().raw().index());
180 }
181
182 #[test]
183 fn bubble_order_is_target_to_root() {
184 let doc = Document::new();
185 let div = doc.create::<HtmlDivElement>();
186 let btn = doc.create::<HtmlButtonElement>();
187 doc.root().append(div);
188 div.append(btn);
189
190 let cell = doc.cell();
191 let path = EventPath::build(cell, btn.handle().raw());
192
193 let bubble: Vec<u32> = path.bubble_order().map(|(_, idx, _)| idx).collect();
194 assert_eq!(bubble[0], btn.handle().raw().index());
196 assert_eq!(bubble[1], div.handle().raw().index());
197 assert_eq!(bubble[2], doc.root().raw().index());
198 }
199
200 #[test]
201 fn detached_node_path_contains_only_target() {
202 let doc = Document::new();
203 let btn = doc.create::<HtmlButtonElement>();
204 let cell = doc.cell();
206 let path = EventPath::build(cell, btn.handle().raw());
207
208 assert_eq!(path.len(), 1);
209 assert_eq!(
210 path.target().expect("has target").0,
211 btn.handle().raw().index()
212 );
213 }
214}