Skip to main content

freya_core/
path_element.rs

1use 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}