freya_core/
path_element.rs1use std::rc::Rc;
2
3#[cfg(debug_assertions)]
4use rustc_hash::FxHashMap;
5use tiny_diff::DiffNode;
6
7use crate::{
8 diff_key::DiffKey,
9 element::{
10 ComponentProps,
11 Element,
12 ElementExt,
13 },
14 tree::DiffModifies,
15};
16
17pub enum PathElement {
18 Component {
19 key: DiffKey,
20
21 comp: Rc<dyn Fn(Rc<dyn ComponentProps>) -> Element>,
22
23 props: Rc<dyn ComponentProps>,
24
25 path: Box<[u32]>,
26 },
27 Element {
28 key: DiffKey,
29
30 element: Rc<dyn ElementExt>,
31 elements: Box<[PathElement]>,
32 path: Box<[u32]>,
33 },
34}
35
36impl std::fmt::Debug for PathElement {
37 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
38 match self {
39 PathElement::Component { key, path, .. } => f
40 .debug_struct("Component")
41 .field("key", key)
42 .field("path", path)
43 .field("comp", &"<fn>")
44 .field("props", &"<props>")
45 .finish(),
46 PathElement::Element { elements, path, .. } => f
47 .debug_struct("Element")
48 .field("path", path)
49 .field("elements", elements)
50 .field("element", &"<element>")
51 .finish(),
52 }
53 }
54}
55
56impl PathElement {
57 #[inline(always)]
58 pub fn with_element<D>(&self, target_path: &[u32], with: D)
59 where
60 D: FnOnce(&PathElement),
61 {
62 match self {
63 Self::Component { path, .. } | Self::Element { path, .. }
64 if path.as_ref() == target_path =>
65 {
66 with(self);
67 }
68 Self::Element { elements, path, .. } if target_path.starts_with(path) => {
69 let next_step = target_path[path.len()];
70 elements[next_step as usize].with_element(target_path, with);
71 }
72 _ => {}
73 }
74 }
75
76 #[cfg_attr(feature = "hotpath", hotpath::measure)]
77 pub fn from_element(path: Vec<u32>, element: Element) -> Self {
78 match element {
79 Element::Component { key, comp, props } => PathElement::Component {
80 key,
81 comp,
82 props,
83 path: path.into_boxed_slice(),
84 },
85 Element::Element {
86 elements,
87 element,
88 key,
89 } => PathElement::Element {
90 elements: elements
91 .into_iter()
92 .enumerate()
93 .map(|(i, e)| {
94 let mut child_path = Vec::with_capacity(path.len() + 1);
95 child_path.extend_from_slice(&path);
96 child_path.push(i as u32);
97 PathElement::from_element(child_path, e)
98 })
99 .collect::<Box<[PathElement]>>(),
100 path: path.into_boxed_slice(),
101 element,
102 key,
103 },
104 }
105 }
106}
107
108impl DiffNode for PathElement {
109 type Key = DiffKey;
110 type Modifications = DiffModifies;
111
112 fn key(&self) -> &DiffKey {
113 let (Self::Element { key, .. } | Self::Component { key, .. }) = self;
114 key
115 }
116
117 fn path(&self) -> &[u32] {
118 let (Self::Element { path, .. } | Self::Component { path, .. }) = self;
119 path
120 }
121
122 fn children(&self) -> &[Self] {
123 match self {
124 Self::Element { elements, .. } => elements,
125 Self::Component { .. } => &[],
126 }
127 }
128
129 fn compatible(&self, previous: &Self) -> bool {
130 matches!(
131 (self, previous),
132 (Self::Element { .. }, Self::Element { .. })
133 | (Self::Component { .. }, Self::Component { .. })
134 )
135 }
136
137 fn modifications(&self, previous: &Self) -> Option<DiffModifies> {
138 match (self, previous) {
139 (
140 Self::Element { element, .. },
141 Self::Element {
142 element: previous, ..
143 },
144 ) => {
145 let flags = element.diff(previous);
146 (!flags.is_empty()).then_some(flags)
147 }
148 _ => None,
149 }
150 }
151
152 #[cfg(debug_assertions)]
153 fn validate_children(&self) {
154 let mut seen = FxHashMap::<&DiffKey, &[u32]>::default();
155 for child in self.children() {
156 let key = child.key();
157 if matches!(key, DiffKey::None | DiffKey::DefaultU64(_)) {
158 continue;
159 }
160 if let Some(previous) = seen.insert(key, child.path()) {
161 panic!(
162 "freya diff: duplicate sibling key {:?} under parent {:?} (paths {:?} and {:?})",
163 key,
164 self.path(),
165 previous,
166 child.path(),
167 );
168 }
169 }
170 }
171}