1use std::borrow::Borrow;
2
3use anathema_store::slab::{Gen, SecondaryMap};
4use anathema_store::smallmap::SmallIndex;
5
6use crate::ValueKind;
7use crate::expression::ValueExpr;
8use crate::value::{Value, Values};
9
10type WidgetId = anathema_store::slab::Key;
11
12#[derive(Debug, Copy, Clone, PartialEq, Default)]
13pub enum ValueKey<'bp> {
14 #[default]
15 Value,
16 Attribute(&'bp str),
17}
18
19impl ValueKey<'_> {
20 pub fn as_str(&self) -> &str {
21 match self {
22 ValueKey::Value => "[value]",
23 ValueKey::Attribute(name) => name,
24 }
25 }
26}
27
28impl Borrow<str> for ValueKey<'_> {
29 fn borrow(&self) -> &str {
30 self.as_str()
31 }
32}
33
34#[derive(Debug)]
38pub struct AttributeStorage<'bp>(SecondaryMap<WidgetId, (Gen, Attributes<'bp>)>);
39
40impl<'bp> AttributeStorage<'bp> {
41 pub fn empty() -> Self {
42 Self(SecondaryMap::empty())
43 }
44
45 pub fn get(&self, id: WidgetId) -> &Attributes<'bp> {
47 self.0.get(id).map(|(_, a)| a).expect("every element has attributes")
48 }
49
50 pub fn try_get(&self, id: WidgetId) -> Option<&Attributes<'bp>> {
52 self.0.get(id).map(|(_, a)| a)
53 }
54
55 pub fn get_mut(&mut self, id: WidgetId) -> &mut Attributes<'bp> {
57 self.0
58 .get_mut(id)
59 .map(|(_, a)| a)
60 .expect("every element has attributes")
61 }
62
63 pub fn with_mut<F, O>(&mut self, widget_id: WidgetId, f: F) -> Option<O>
64 where
65 F: FnOnce(&mut Attributes<'bp>, &mut Self) -> O,
66 {
67 let mut value = self.try_remove(widget_id)?;
68 let output = f(&mut value, self);
69 self.insert(widget_id, value);
70 Some(output)
71 }
72
73 pub fn insert(&mut self, widget_id: WidgetId, attribs: Attributes<'bp>) {
77 self.0.insert(widget_id, (widget_id.generation(), attribs))
78 }
79
80 pub fn try_remove(&mut self, id: WidgetId) -> Option<Attributes<'bp>> {
82 self.0
83 .remove_if(id, |(current_gen, _)| *current_gen == id.generation())
84 .map(|(_, value)| value)
85 }
86}
87
88#[derive(Debug)]
93pub struct Attributes<'bp> {
94 pub(crate) attribs: Values<'bp>,
95 pub value: Option<SmallIndex>,
96}
97
98impl<'bp> Attributes<'bp> {
105 pub fn empty() -> Self {
107 Self {
108 attribs: Values::empty(),
109 value: None,
110 }
111 }
112
113 pub fn set(&mut self, key: &'bp str, value: impl Into<ValueKind<'bp>>) {
122 let key = ValueKey::Attribute(key);
123 let value = value.into();
124 let value = Value {
125 expr: ValueExpr::Null,
126 kind: value,
127 sub: anathema_state::Subscriber::MAX,
128 sub_to: anathema_state::SubTo::Zero,
129 };
130
131 self.attribs.set(key, value);
132 }
133
134 #[doc(hidden)]
135 pub fn insert_with<F>(&mut self, key: ValueKey<'bp>, f: F) -> SmallIndex
136 where
137 F: FnMut(SmallIndex) -> Value<'bp>,
138 {
139 self.attribs.insert_with(key, f)
140 }
141
142 pub fn remove(&mut self, key: &'bp str) -> Option<Value<'bp>> {
144 let key = ValueKey::Attribute(key);
145 self.attribs.remove(&key)
146 }
147
148 pub fn value(&self) -> Option<&ValueKind<'bp>> {
151 let idx = self.value?;
152 self.attribs.get_with_index(idx).map(|val| &val.kind)
153 }
154
155 pub fn get(&self, key: &str) -> Option<&ValueKind<'bp>> {
156 self.attribs.get(key).map(|val| &val.kind)
157 }
158
159 pub fn get_as<'a, T>(&'a self, key: &str) -> Option<T>
172 where
173 T: TryFrom<&'a ValueKind<'bp>>,
174 {
175 self.attribs.get(key).and_then(|val| (&val.kind).try_into().ok())
176 }
177
178 pub fn iter_as<'a, T>(&'a self, key: &str) -> impl Iterator<Item = T>
191 where
192 T: TryFrom<&'a ValueKind<'bp>>,
193 {
194 self.attribs
195 .get(key)
196 .and_then(|val| match &val.kind {
197 ValueKind::List(value_kinds) => {
198 let list = value_kinds.iter().filter_map(|v| T::try_from(v).ok());
199 Some(list)
200 }
201
202 _ => None,
203 })
204 .into_iter()
205 .flatten()
206 }
207
208 #[doc(hidden)]
209 pub fn get_mut_with_index(&mut self, index: SmallIndex) -> Option<&mut Value<'bp>> {
212 self.attribs.get_mut_with_index(index)
213 }
214
215 pub fn iter(&self) -> impl Iterator<Item = (&ValueKey<'_>, &ValueKind<'bp>)> {
218 self.attribs.iter().filter_map(|(key, val)| match key {
219 ValueKey::Value => None,
220 ValueKey::Attribute(_) => Some((key, &val.kind)),
221 })
222 }
223
224 pub(super) fn get_value_expr(&self, key: &str) -> Option<ValueExpr<'bp>> {
225 let value = self.attribs.get(key)?;
226 Some(value.expr.clone())
227 }
228}
229
230#[cfg(test)]
231mod test {
232 use anathema_state::{Color, Hex};
233
234 use super::*;
235
236 fn attribs() -> Attributes<'static> {
237 let mut attributes = Attributes::empty();
238
239 let values = ValueKind::List([ValueKind::Int(1), ValueKind::Bool(true), ValueKind::Int(2)].into());
240 attributes.set("mixed_list", values);
241 attributes.set("num", 123);
242 attributes.set("static_str", "static");
243 attributes.set("string", String::from("string"));
244 attributes.set("hex", Hex::from((1, 2, 3)));
245 attributes.set("red", Color::Red);
246 attributes.set("float", 1.23);
247 attributes.set("bool", true);
248 attributes.set("char", 'a');
249
250 attributes
251 }
252
253 #[test]
254 fn iter_as_type() {
255 let attributes = attribs();
256
257 let values = attributes.iter_as::<u8>("mixed_list").collect::<Vec<_>>();
258 assert_eq!(vec![1, 2], values);
259
260 let values = attributes.iter_as::<bool>("mixed_list").collect::<Vec<_>>();
261 assert_eq!(vec![true], values);
262 }
263
264 #[test]
265 fn get_as_int() {
266 assert_eq!(123, attribs().get_as::<u8>("num").unwrap());
267 assert_eq!(123, attribs().get_as::<i8>("num").unwrap());
268 assert_eq!(123, attribs().get_as::<u16>("num").unwrap());
269 assert_eq!(123, attribs().get_as::<i16>("num").unwrap());
270 assert_eq!(123, attribs().get_as::<u32>("num").unwrap());
271 assert_eq!(123, attribs().get_as::<i32>("num").unwrap());
272 assert_eq!(123, attribs().get_as::<u64>("num").unwrap());
273 assert_eq!(123, attribs().get_as::<i64>("num").unwrap());
274 assert_eq!(123, attribs().get_as::<usize>("num").unwrap());
275 assert_eq!(123, attribs().get_as::<isize>("num").unwrap());
276 }
277
278 #[test]
279 fn get_as_strings() {
280 let attributes = attribs();
281 assert_eq!("static", attributes.get_as::<&str>("static_str").unwrap());
282 assert_eq!("string", attributes.get_as::<&str>("string").unwrap());
283 }
284
285 #[test]
286 fn get_as_hex() {
287 let attributes = attribs();
288 assert_eq!(Hex::from((1, 2, 3)), attributes.get_as::<Hex>("hex").unwrap());
289 }
290
291 #[test]
292 fn get_as_color() {
293 let attributes = attribs();
294 assert_eq!(Color::Red, attributes.get_as::<Color>("red").unwrap());
295 }
296
297 #[test]
298 fn get_as_float() {
299 let attributes = attribs();
300 assert_eq!(1.23, attributes.get_as::<f32>("float").unwrap());
301 assert_eq!(1.23, attributes.get_as::<f64>("float").unwrap());
302 }
303
304 #[test]
305 fn get_as_bool() {
306 let attributes = attribs();
307 assert!(attributes.get_as::<bool>("bool").unwrap());
308 }
309
310 #[test]
311 fn get_as_char() {
312 let attributes = attribs();
313 assert_eq!('a', attributes.get_as::<char>("char").unwrap());
314 }
315}