1use std::ops::ControlFlow;
2
3use anathema_value_resolver::{AttributeStorage, Attributes};
4
5use super::{Chain, Filter, Nodes, Query, QueryValue};
6use crate::nodes::component::Component;
7use crate::{WidgetId, WidgetKind};
8
9pub struct Components<'children, 'tree, 'bp> {
10 pub(super) elements: &'children mut Nodes<'tree, 'bp>,
11}
12
13impl<'children, 'tree, 'bp> Components<'children, 'tree, 'bp> {
14 pub fn by_name<'a>(&mut self, name: &'a str) -> ComponentQuery<'_, 'tree, 'bp, Kind<'a>> {
15 self.make_query(Kind::ByName(name))
16 }
17
18 pub fn by_id<F, U>(&mut self, id: WidgetId, mut f: F) -> Option<U>
19 where
20 F: FnMut(&mut Component<'_>, &mut Attributes<'_>) -> U,
21 {
22 let widget = self.elements.children.get_mut(id)?;
23 let WidgetKind::Component(component) = &mut widget.kind else { return None };
24 let attributes = self.elements.attributes.get_mut(id);
25 Some(f(component, attributes))
26 }
27
28 pub fn by_attribute<'a>(
29 &mut self,
30 key: &'a str,
31 value: impl Into<QueryValue<'a>>,
32 ) -> ComponentQuery<'_, 'tree, 'bp, Kind<'a>> {
33 self.make_query(Kind::ByAttribute(key, value.into()))
34 }
35
36 fn make_query<'a>(&mut self, kind: Kind<'a>) -> ComponentQuery<'_, 'tree, 'bp, Kind<'a>> {
37 ComponentQuery {
38 query: Query {
39 filter: kind,
40 elements: self.elements,
41 },
42 }
43 }
44}
45
46pub struct ComponentQuery<'el, 'tree, 'bp, T>
47where
48 T: Filter<'bp, Kind = Component<'bp>> + Copy,
49{
50 query: Query<'el, 'tree, 'bp, T, Component<'bp>>,
51}
52
53impl<'el, 'tree, 'bp, T> ComponentQuery<'el, 'tree, 'bp, T>
54where
55 T: Filter<'bp, Kind = Component<'bp>> + Copy,
56{
57 pub fn by_name(self, name: &str) -> ComponentQuery<'el, 'tree, 'bp, Chain<T, Kind<'_>>> {
58 ComponentQuery {
59 query: Query {
60 filter: Chain::new(self.query.filter, Kind::ByName(name)),
61 elements: self.query.elements,
62 },
63 }
64 }
65
66 pub fn by_attribute<'a>(
67 self,
68 key: &'a str,
69 value: impl Into<QueryValue<'a>>,
70 ) -> ComponentQuery<'el, 'tree, 'bp, Chain<T, Kind<'a>>> {
71 ComponentQuery {
72 query: Query {
73 filter: Chain::new(self.query.filter, Kind::ByAttribute(key, value.into())),
74 elements: self.query.elements,
75 },
76 }
77 }
78
79 pub fn first<F, U>(self, mut f: F) -> Option<U>
80 where
81 F: FnMut(WidgetId, &mut Component<'_>, &mut Attributes<'_>) -> U,
82 {
83 match self.query(&mut f, false) {
84 ControlFlow::Continue(_) => None,
85 ControlFlow::Break(val) => Some(val),
86 }
87 }
88
89 pub fn each<F>(self, mut f: F)
90 where
91 F: FnMut(WidgetId, &mut Component<'_>, &mut Attributes<'_>),
92 {
93 _ = self.query(&mut f, true);
94 }
95
96 fn query<F, U>(self, f: &mut F, continuous: bool) -> ControlFlow<U>
97 where
98 F: FnMut(WidgetId, &mut Component<'_>, &mut Attributes<'_>) -> U,
99 {
100 let ret_val = self.query.elements.children.for_each(|_path, container, children| {
101 if let WidgetKind::Component(ref mut component) = container.kind {
102 if self.query.filter.filter(component, self.query.elements.attributes) {
103 let attributes = self.query.elements.attributes.get_mut(component.widget_id);
104 let ret_val = f(component.widget_id, component, attributes);
105
106 if !continuous {
107 return ControlFlow::Break(ret_val);
108 }
109 }
110 }
111
112 let mut elements = Nodes::new(
113 children,
114 self.query.elements.attributes,
115 self.query.elements.needs_layout,
116 );
117
118 let query = ComponentQuery {
119 query: Query {
120 elements: &mut elements,
121 filter: self.query.filter,
122 },
123 };
124
125 query.query(f, continuous)
126 });
127
128 match ret_val {
129 Some(val) => ControlFlow::Break(val),
130 None => ControlFlow::Continue(()),
131 }
132 }
133}
134
135#[derive(Debug, Copy, Clone)]
139pub enum Kind<'a> {
140 ByName(&'a str),
141 ByAttribute(&'a str, QueryValue<'a>),
142}
143
144impl<'bp> Filter<'bp> for Kind<'_> {
145 type Kind = Component<'bp>;
146
147 fn filter(&self, el: &Self::Kind, attributes: &mut AttributeStorage<'bp>) -> bool {
148 match self {
149 Kind::ByName(name) => el.name == *name,
150 Kind::ByAttribute(key, value) => {
151 let attribs = attributes.get(el.widget_id);
152 attribs.get(key).map(|attribute| value.eq(attribute)).unwrap_or(false)
153 }
154 }
155 }
156}