sauron_core/dom/application/
skip_diff.rs

1use crate::vdom::TreePath;
2
3/// specifies how attributes will be skipped
4#[derive(Debug, PartialEq, Clone)]
5pub enum SkipAttrs {
6    /// all attributes are skipped
7    All,
8    /// skip only the listed indices
9    Indices(Vec<usize>),
10}
11
12impl SkipAttrs {
13    /// dont skip anything
14    pub fn none() -> Self {
15        Self::Indices(vec![])
16    }
17}
18
19/// if the expression evaluates to true,
20/// diffing at this node will be skipped entirely
21#[derive(Debug, PartialEq, Clone)]
22pub struct SkipDiff {
23    /// shall skip or not
24    pub skip_attrs: SkipAttrs,
25    /// children skip diff
26    pub children: Vec<SkipDiff>,
27}
28
29impl SkipDiff {
30    /// the skip diff is a block
31    pub fn block() -> Self {
32        Self {
33            skip_attrs: SkipAttrs::none(),
34            children: vec![],
35        }
36    }
37
38    /// return SkipDiff in this path location
39    pub fn in_path(&self, path: &TreePath) -> Option<&Self> {
40        let mut path = path.clone();
41        if path.is_empty() {
42            Some(self)
43        } else {
44            let idx = path.remove_first();
45            if let Some(child) = self.children.get(idx) {
46                child.in_path(&path)
47            } else {
48                None
49            }
50        }
51    }
52
53    /// get the skip diff at this child index
54    pub fn traverse(&self, idx: usize) -> Option<&Self> {
55        self.children.get(idx)
56    }
57
58    /// check if shall skip diffing attributes at this path
59    /// if the path does not coincide in this skip diff, then by default it is skipped
60    pub fn shall_skip_attributes(&self) -> bool {
61        self.skip_attrs == SkipAttrs::All
62    }
63
64    /// return true if this skip diff and its children can be skipped
65    pub fn is_skippable_recursive(&self) -> bool {
66        self.shall_skip_attributes() && self.children.iter().all(Self::is_skippable_recursive)
67    }
68
69    ///
70    pub fn shall_skip_node(&self) -> bool {
71        self.shall_skip_attributes() && self.children.is_empty()
72    }
73
74    /// collapse into 1 skip_if if all the children is skippable
75    pub fn collapse_children(self) -> Self {
76        let Self {
77            skip_attrs,
78            children,
79        } = self;
80        let can_skip_children = children.iter().all(Self::is_skippable_recursive);
81        Self {
82            skip_attrs,
83            children: if can_skip_children {
84                vec![]
85            } else {
86                children.into_iter().map(Self::collapse_children).collect()
87            },
88        }
89    }
90}
91
92/// skip diffing the node is the val is true
93pub fn skip_if(shall: bool, children: impl IntoIterator<Item = SkipDiff>) -> SkipDiff {
94    SkipDiff {
95        skip_attrs: if shall {
96            SkipAttrs::All
97        } else {
98            SkipAttrs::none()
99        },
100        children: children.into_iter().collect(),
101    }
102}
103
104/// combination of TreePath and SkipDiff
105#[derive(Debug)]
106pub struct SkipPath {
107    pub(crate) path: TreePath,
108    pub(crate) skip_diff: Option<SkipDiff>,
109}
110
111impl SkipPath {
112    pub(crate) fn new(path: TreePath, skip_diff: SkipDiff) -> Self {
113        Self {
114            path,
115            skip_diff: Some(skip_diff),
116        }
117    }
118
119    pub(crate) fn traverse(&self, idx: usize) -> Self {
120        Self {
121            path: self.path.traverse(idx),
122            skip_diff: if let Some(skip_diff) = self.skip_diff.as_ref() {
123                skip_diff.traverse(idx).cloned()
124            } else {
125                None
126            },
127        }
128    }
129
130    pub(crate) fn backtrack(&self) -> Self {
131        Self {
132            path: self.path.backtrack(),
133            //TODO: here the skip_diff can not back track as we lose that info already
134            skip_diff: None,
135        }
136    }
137}