ferroui_base/rendering/composition/expressions/
expression_tracked_values.rs1use std::collections::HashSet;
4
5use super::expression_evaluation_context::IExpressionObject;
6
7#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
14pub struct ExpressionObjectKey(pub usize);
15
16#[derive(Clone, Debug, Default)]
19pub struct ExpressionTrackedObjects {
20 list: Vec<ExpressionObjectKey>,
21 hash_set: HashSet<ExpressionObjectKey>,
22}
23
24impl ExpressionTrackedObjects {
25 pub fn new() -> Self {
27 Self::default()
28 }
29
30 pub fn add(&mut self, obj: &dyn IExpressionObject, member: &str) {
32 self.add_key(obj.key(), member);
33 }
34
35 pub fn add_key(&mut self, obj: ExpressionObjectKey, _member: &str) {
37 if self.hash_set.insert(obj) {
38 self.list.push(obj);
39 }
40 }
41
42 pub fn clear(&mut self) {
44 self.list.clear();
45 self.hash_set.clear();
46 }
47
48 pub fn iter(&self) -> std::slice::Iter<'_, ExpressionObjectKey> {
50 self.list.iter()
51 }
52}
53
54impl<'a> IntoIterator for &'a ExpressionTrackedObjects {
55 type Item = &'a ExpressionObjectKey;
56 type IntoIter = std::slice::Iter<'a, ExpressionObjectKey>;
57
58 fn into_iter(self) -> Self::IntoIter {
59 self.list.iter()
60 }
61}
62
63#[derive(Debug, Default)]
66pub struct ExpressionTrackedObjectsPool {
67 stack: Vec<ExpressionTrackedObjects>,
68}
69
70impl ExpressionTrackedObjectsPool {
71 pub fn new() -> Self {
73 Self::default()
74 }
75
76 pub fn get(&mut self) -> ExpressionTrackedObjects {
79 self.stack.pop().unwrap_or_default()
80 }
81
82 pub fn return_(&mut self, obj: ExpressionTrackedObjects) {
85 self.stack.clear();
86 self.stack.push(obj);
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93 use crate::rendering::composition::expressions::ExpressionVariant;
94
95 struct Obj(#[allow(dead_code)] u8);
96
97 impl IExpressionObject for Obj {
98 fn get_property(&self, _name: &str) -> ExpressionVariant {
99 ExpressionVariant::Invalid
100 }
101 }
102
103 struct Keyed(usize);
104
105 impl IExpressionObject for Keyed {
106 fn get_property(&self, _name: &str) -> ExpressionVariant {
107 ExpressionVariant::Invalid
108 }
109
110 fn key(&self) -> ExpressionObjectKey {
111 ExpressionObjectKey(self.0)
112 }
113 }
114
115 #[test]
116 fn tracks_each_object_once_in_insertion_order() {
117 let (a, b) = (Obj(1), Obj(2));
118 let mut tracked = ExpressionTrackedObjects::new();
119 tracked.add(&b, "Offset");
120 tracked.add(&a, "Offset");
121 tracked.add(&b, "Size");
122 tracked.add(&a as &dyn IExpressionObject, "Opacity");
123
124 let keys: Vec<_> = tracked.iter().copied().collect();
125 assert_eq!(keys, vec![b.key(), a.key()]);
126 assert_ne!(a.key(), b.key());
127 assert_eq!((&tracked).into_iter().count(), 2);
128
129 tracked.clear();
130 assert_eq!(tracked.iter().count(), 0);
131 tracked.add(&a, "Offset");
132 assert_eq!(tracked.iter().count(), 1);
133 }
134
135 #[test]
136 fn uses_the_key_of_the_object() {
137 let mut tracked = ExpressionTrackedObjects::new();
138 tracked.add(&Keyed(7), "A");
139 tracked.add(&Keyed(7), "B");
140 tracked.add(&Keyed(9), "A");
141 tracked.add_key(ExpressionObjectKey(9), "C");
142 let keys: Vec<_> = tracked.iter().map(|k| k.0).collect();
143 assert_eq!(keys, vec![7, 9]);
144 }
145
146 #[test]
147 fn pool_keeps_only_the_last_returned_collection() {
148 let mut pool = ExpressionTrackedObjectsPool::new();
149 assert_eq!(pool.get().iter().count(), 0);
150
151 let mut first = ExpressionTrackedObjects::new();
152 first.add_key(ExpressionObjectKey(1), "A");
153 let mut second = ExpressionTrackedObjects::new();
154 second.add_key(ExpressionObjectKey(2), "A");
155 second.add_key(ExpressionObjectKey(3), "A");
156 pool.return_(first);
157 pool.return_(second);
158
159 assert_eq!(pool.get().iter().count(), 2);
161 assert_eq!(pool.get().iter().count(), 0);
162 }
163}