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 } else if !path.is_empty() && path[path.len() - 1] != path2[path2.len() - 1]
168 {
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 let mut previous_keys = FxHashMap::<&DiffKey, VecDeque<usize>>::default();
177
178 for (i, e) in e2.iter().enumerate() {
179 let (PathElement::Element { key, .. } | PathElement::Component { key, .. }) =
180 e;
181 previous_keys.entry(key).or_default().push_back(i)
182 }
183
184 for e in e1 {
185 let (PathElement::Element { key, .. } | PathElement::Component { key, .. }) =
186 e;
187 if let Some(old_i) =
188 previous_keys.get_mut(key).and_then(VecDeque::pop_front)
189 {
190 e.diff(Some(&e2[old_i]), diff);
191 } else {
192 e.diff(None, diff);
193 }
194 }
195
196 for indexes in previous_keys.values() {
197 for i in indexes {
198 let (PathElement::Element { path, .. }
199 | PathElement::Component { path, .. }) = &e2[*i];
200 diff.moved.remove(path);
202 diff.removed.push(path.clone());
203 }
205 }
206 }
207 (s, o) => {
208 let (PathElement::Element { path, .. } | PathElement::Component { path, .. }) =
210 o;
211 diff.removed.push(path.clone());
212
213 s.diff(None, diff);
215 }
216 },
217 }
218 }
219}