anathema_widgets/components/
deferred.rs

1use std::any::Any;
2use std::borrow::Cow;
3
4use anathema_value_resolver::{Attributes, ValueKind};
5
6use crate::nodes::component::Component;
7
8pub struct DeferredComponents {
9    queue: Vec<Command>,
10}
11
12impl DeferredComponents {
13    pub fn new() -> Self {
14        Self { queue: vec![] }
15    }
16
17    pub fn drain(&mut self) -> impl Iterator<Item = Command> + '_ {
18        self.queue.drain(..).rev()
19    }
20
21    pub fn by_name(&mut self, name: impl Into<Cow<'static, str>>) -> QueryBuilder<'_> {
22        QueryBuilder::new(&mut self.queue, Filter::Name(name.into()))
23    }
24
25    pub fn by_attribute(
26        &mut self,
27        key: impl Into<Cow<'static, str>>,
28        value: impl Into<ValueKind<'static>>,
29    ) -> QueryBuilder<'_> {
30        QueryBuilder::new(
31            &mut self.queue,
32            Filter::Attribute {
33                key: key.into(),
34                value: value.into(),
35            },
36        )
37    }
38
39    pub fn nth(&mut self, count: usize) -> QueryBuilder<'_> {
40        QueryBuilder::new(&mut self.queue, Filter::Nth(count))
41    }
42}
43
44pub struct QueryBuilder<'a> {
45    queue: &'a mut Vec<Command>,
46    filter: Filter,
47}
48
49impl<'a> QueryBuilder<'a> {
50    fn new(queue: &'a mut Vec<Command>, filter: Filter) -> Self {
51        Self { queue, filter }
52    }
53
54    pub fn by_name(self, name: impl Into<Cow<'static, str>>) -> Self {
55        Self {
56            queue: self.queue,
57            filter: self.filter.chain(Filter::Name(name.into())),
58        }
59    }
60
61    pub fn by_attribute(self, key: impl Into<Cow<'static, str>>, value: impl Into<ValueKind<'static>>) -> Self {
62        Self {
63            queue: self.queue,
64            filter: self.filter.chain(Filter::Attribute {
65                key: key.into(),
66                value: value.into(),
67            }),
68        }
69    }
70
71    pub fn nth(self, count: usize) -> Self {
72        Self {
73            queue: self.queue,
74            filter: self.filter.chain(Filter::Nth(count)),
75        }
76    }
77
78    pub fn send(self, message: impl Any + Send + Sync) {
79        let command = Command {
80            kind: CommandKind::SendMessage(Box::new(message)),
81            filter: self.filter,
82        };
83        self.queue.push(command);
84    }
85
86    pub fn focus(self) {
87        let command = Command {
88            kind: CommandKind::Focus,
89            filter: self.filter,
90        };
91        self.queue.push(command);
92    }
93}
94
95enum Filter {
96    Name(Cow<'static, str>),
97    Attribute {
98        key: Cow<'static, str>,
99        value: ValueKind<'static>,
100    },
101    Nth(usize),
102    Chain(Box<Self>, Box<Self>),
103}
104
105impl Filter {
106    fn chain(self, other: Self) -> Self {
107        Self::Chain(Box::new(self), Box::new(other))
108    }
109
110    // The filter only works with primitives, this excludes maps, lists and composite
111    // values
112    fn filter(&mut self, component: &Component<'_>, attributes: &Attributes<'_>) -> bool {
113        match self {
114            Filter::Name(cow) => component.name == cow,
115            Filter::Attribute { key, value: rhs } => match attributes.get(key) {
116                Some(lhs) => match (lhs, rhs) {
117                    (ValueKind::Int(lhs), ValueKind::Int(rhs)) => lhs == rhs,
118                    (ValueKind::Float(lhs), ValueKind::Float(rhs)) => lhs == rhs,
119                    (ValueKind::Bool(lhs), ValueKind::Bool(rhs)) => lhs == rhs,
120                    (ValueKind::Char(lhs), ValueKind::Char(rhs)) => lhs == rhs,
121                    (ValueKind::Hex(lhs), ValueKind::Hex(rhs)) => lhs == rhs,
122                    (ValueKind::Str(lhs), ValueKind::Str(rhs)) => lhs == rhs,
123                    (ValueKind::Null, ValueKind::Null) => true,
124                    _ => false,
125                },
126                None => false,
127            },
128            Filter::Nth(0) => true,
129            Filter::Nth(nth) => {
130                *nth -= 1;
131                false
132            }
133            Filter::Chain(first, second) => match first.filter(component, attributes) {
134                true => second.filter(component, attributes),
135                false => false,
136            },
137        }
138    }
139}
140
141pub struct Command {
142    filter: Filter,
143    pub kind: CommandKind,
144}
145
146impl Command {
147    pub fn filter_component(&mut self, component: &Component<'_>, attributes: &Attributes<'_>) -> bool {
148        self.filter.filter(component, attributes)
149    }
150}
151
152pub enum CommandKind {
153    SendMessage(Box<dyn Any + Send + Sync>),
154    Focus,
155}