1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
use crate::vdom::TreePath;

/// specifies how attributes will be skipped
#[derive(Debug, PartialEq, Clone)]
pub enum SkipAttrs {
    /// all attributes are skipped
    All,
    /// skip only the listed indices
    Indices(Vec<usize>),
}

impl SkipAttrs {
    /// dont skip anything
    pub fn none() -> Self {
        Self::Indices(vec![])
    }
}

/// if the expression evaluates to true,
/// diffing at this node will be skipped entirely
#[derive(Debug, PartialEq, Clone)]
pub struct SkipDiff {
    /// shall skip or not
    pub skip_attrs: SkipAttrs,
    /// children skip diff
    pub children: Vec<SkipDiff>,
}

impl SkipDiff {
    /// the skip diff is a block
    pub fn block() -> Self {
        Self {
            skip_attrs: SkipAttrs::none(),
            children: vec![],
        }
    }

    /// return SkipDiff in this path location
    pub fn in_path(&self, path: &TreePath) -> Option<&Self> {
        let mut path = path.clone();
        if path.is_empty() {
            Some(self)
        } else {
            let idx = path.remove_first();
            if let Some(child) = self.children.get(idx) {
                child.in_path(&path)
            } else {
                None
            }
        }
    }

    /// get the skip diff at this child index
    pub fn traverse(&self, idx: usize) -> Option<&Self> {
        self.children.get(idx)
    }

    /// check if shall skip diffing attributes at this path
    /// if the path does not coincide in this skip diff, then by default it is skipped
    pub fn shall_skip_attributes(&self) -> bool {
        self.skip_attrs == SkipAttrs::All
    }

    /// return true if this skip diff and its children can be skipped
    pub fn is_skippable_recursive(&self) -> bool {
        self.shall_skip_attributes() && self.children.iter().all(Self::is_skippable_recursive)
    }

    ///
    pub fn shall_skip_node(&self) -> bool {
        self.shall_skip_attributes() && self.children.is_empty()
    }

    /// collapse into 1 skip_if if all the children is skippable
    pub fn collapse_children(self) -> Self {
        let Self {
            skip_attrs,
            children,
        } = self;
        let can_skip_children = children.iter().all(Self::is_skippable_recursive);
        Self {
            skip_attrs,
            children: if can_skip_children {
                vec![]
            } else {
                children.into_iter().map(Self::collapse_children).collect()
            },
        }
    }
}

/// skip diffing the node is the val is true
pub fn skip_if(shall: bool, children: impl IntoIterator<Item = SkipDiff>) -> SkipDiff {
    SkipDiff {
        skip_attrs: if shall {
            SkipAttrs::All
        } else {
            SkipAttrs::none()
        },
        children: children.into_iter().collect(),
    }
}

/// combination of TreePath and SkipDiff
#[derive(Debug)]
pub struct SkipPath {
    pub(crate) path: TreePath,
    pub(crate) skip_diff: Option<SkipDiff>,
}

impl SkipPath {
    pub(crate) fn new(path: TreePath, skip_diff: SkipDiff) -> Self {
        Self {
            path,
            skip_diff: Some(skip_diff),
        }
    }

    pub(crate) fn traverse(&self, idx: usize) -> Self {
        Self {
            path: self.path.traverse(idx),
            skip_diff: if let Some(skip_diff) = self.skip_diff.as_ref() {
                skip_diff.traverse(idx).cloned()
            } else {
                None
            },
        }
    }

    pub(crate) fn backtrack(&self) -> Self {
        Self {
            path: self.path.backtrack(),
            //TODO: here the skip_diff can not back track as we lose that info already
            skip_diff: None,
        }
    }
}