1use std::{
2 collections::VecDeque,
3 rc::Rc,
4};
5
6use rustc_hash::FxHashMap;
7
8use crate::{
9 diff_key::DiffKey,
10 element::{
11 ComponentProps,
12 Element,
13 ElementExt,
14 },
15 runner::Diff,
16};
17
18pub enum PathElement {
19 Component {
20 key: DiffKey,
21
22 comp: Rc<dyn Fn(Rc<dyn ComponentProps>) -> Element>,
23
24 props: Rc<dyn ComponentProps>,
25
26 path: Box<[u32]>,
27 },
28 Element {
29 key: DiffKey,
30
31 element: Rc<dyn ElementExt>,
32 elements: Box<[PathElement]>,
33 path: Box<[u32]>,
34 },
35}
36
37impl std::fmt::Debug for PathElement {
38 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39 match self {
40 PathElement::Component { key, path, .. } => f
41 .debug_struct("Component")
42 .field("key", key)
43 .field("path", path)
44 .field("comp", &"<fn>")
45 .field("props", &"<props>")
46 .finish(),
47 PathElement::Element { elements, path, .. } => f
48 .debug_struct("Element")
49 .field("path", path)
50 .field("elements", elements)
51 .field("element", &"<element>")
52 .finish(),
53 }
54 }
55}
56
57impl PathElement {
58 #[inline(always)]
59 pub fn with_element<D>(&self, target_path: &[u32], with: D)
60 where
61 D: FnOnce(&PathElement),
62 {
63 match self {
64 Self::Component { path, .. } | Self::Element { path, .. }
65 if path.as_ref() == target_path =>
66 {
67 with(self);
68 }
69 Self::Element { elements, path, .. } if target_path.starts_with(path) => {
70 let next_step = target_path[path.len()];
71 elements[next_step as usize].with_element(target_path, with);
72 }
73 _ => {}
74 }
75 }
76
77 #[cfg_attr(feature = "hotpath", hotpath::measure)]
78 pub fn from_element(path: Vec<u32>, element: Element) -> Self {
79 match element {
80 Element::Component { key, comp, props } => PathElement::Component {
81 key,
82 comp,
83 props,
84 path: path.into_boxed_slice(),
85 },
86 Element::Element {
87 elements,
88 element,
89 key,
90 } => PathElement::Element {
91 elements: elements
92 .into_iter()
93 .enumerate()
94 .map(|(i, e)| {
95 let mut path = path.clone();
96 path.push(i as u32);
97 PathElement::from_element(path, e)
98 })
99 .collect::<Box<[PathElement]>>(),
100 path: path.into_boxed_slice(),
101 element,
102 key,
103 },
104 }
105 }
106
107 #[cfg_attr(feature = "hotpath", hotpath::measure)]
108 pub fn diff(&self, previous: Option<&Self>, diff: &mut Diff) {
109 match previous {
110 None => {
111 match self {
112 PathElement::Component { path, .. } => {
113 diff.added.push(path.clone());
114 }
115 PathElement::Element { path, elements, .. } => {
116 diff.added.push(path.clone());
117
118 for element in elements {
120 element.diff(None, diff);
121 }
122 }
123 }
124 }
125 Some(previous) => match (self, previous) {
126 (
127 PathElement::Component { key: k1, path, .. },
128 PathElement::Component {
129 key: k2,
130 path: path2,
131 ..
132 },
133 ) => {
134 if k1 != k2 || diff.removed.iter().any(|p| **p == path2[..path2.len() - 1]) {
135 diff.added.push(path.clone());
136 diff.removed.push(path2.clone());
137 } else if !path.is_empty() && path[path.len() - 1] != path2[path2.len() - 1] {
138 diff.moved
139 .entry(Box::from(path[..path.len() - 1].to_vec()))
140 .or_default()
141 .push((*path2.last().unwrap(), *path.last().unwrap()));
142 }
143 }
144 (
145 PathElement::Element {
146 elements: e1,
147 element: element1,
148 path,
149 key: k1,
150 ..
151 },
152 PathElement::Element {
153 elements: e2,
154 element: element2,
155 path: path2,
156 key: k2,
157 ..
158 },
159 ) => {
160 if k1 != k2 || diff.removed.iter().any(|p| **p == path2[..path2.len() - 1]) {
161 diff.added.push(path.clone());
162 diff.removed.push(path2.clone());
163 } else {
164 let diff_flags = element1.diff(element2);
165 if !diff_flags.is_empty() {
166 diff.modified.push((path.clone(), diff_flags));
167 }
168 if !path.is_empty() && path[path.len() - 1] != path2[path2.len() - 1] {
169 diff.moved
170 .entry(Box::from(path[..path.len() - 1].to_vec()))
171 .or_default()
172 .push((*path2.last().unwrap(), *path.last().unwrap()));
173 }
174 }
175
176 #[cfg(debug_assertions)]
177 {
178 let mut seen = FxHashMap::<&DiffKey, &[u32]>::default();
179 for child in e1 {
180 let (PathElement::Element {
181 key,
182 path: child_path,
183 ..
184 }
185 | PathElement::Component {
186 key,
187 path: child_path,
188 ..
189 }) = child;
190 if matches!(key, DiffKey::None | DiffKey::DefaultU64(_)) {
191 continue;
192 }
193 if let Some(prev) = seen.insert(key, child_path) {
194 panic!(
195 "freya diff: duplicate sibling key {:?} under parent {:?} \
196 (paths {:?} and {:?})",
197 key, path, prev, child_path,
198 );
199 }
200 }
201 }
202
203 let mut previous_keys = FxHashMap::<&DiffKey, VecDeque<usize>>::default();
204
205 for (i, e) in e2.iter().enumerate() {
206 let (PathElement::Element { key, .. } | PathElement::Component { key, .. }) =
207 e;
208 previous_keys.entry(key).or_default().push_back(i)
209 }
210
211 for e in e1 {
212 let (PathElement::Element { key, .. } | PathElement::Component { key, .. }) =
213 e;
214 if let Some(old_i) =
215 previous_keys.get_mut(key).and_then(VecDeque::pop_front)
216 {
217 e.diff(Some(&e2[old_i]), diff);
218 } else {
219 e.diff(None, diff);
220 }
221 }
222
223 for indexes in previous_keys.values() {
224 for i in indexes {
225 let (PathElement::Element { path, .. }
226 | PathElement::Component { path, .. }) = &e2[*i];
227 diff.moved.remove(path);
229 diff.removed.push(path.clone());
230 }
232 }
233 }
234 (s, o) => {
235 let (PathElement::Element { path, .. } | PathElement::Component { path, .. }) =
237 o;
238 diff.removed.push(path.clone());
239
240 s.diff(None, diff);
242 }
243 },
244 }
245 }
246}