1use crate::{Atom, AtomKind, Image, WidgetText};
2use core::ops::{Deref, DerefMut};
3use std::borrow::Cow;
4
5#[derive(Clone, Debug, Default)]
16pub struct Atoms<'a>(Vec<Atom<'a>>);
17
18impl<'a> Atoms<'a> {
19 pub fn new(atoms: impl IntoAtoms<'a>) -> Self {
20 atoms.into_atoms()
21 }
22
23 pub fn push_right(&mut self, atom: impl Into<Atom<'a>>) {
25 self.0.push(atom.into());
26 }
27
28 pub fn extend_right(&mut self, atoms: Self) {
32 self.0.extend(atoms.0);
33 }
34
35 pub fn push_left(&mut self, atom: impl Into<Atom<'a>>) {
37 self.0.insert(0, atom.into());
38 }
39
40 pub fn extend_left(&mut self, mut atoms: Self) {
44 core::mem::swap(&mut atoms.0, &mut self.0);
45 self.0.extend(atoms.0);
46 }
47
48 pub fn text(&self) -> Option<Cow<'_, str>> {
52 let mut string: Option<Cow<'_, str>> = None;
53 for atom in &self.0 {
54 if let AtomKind::Text(text) = &atom.kind {
55 if let Some(string) = &mut string {
56 let string = string.to_mut();
57 string.push(' ');
58 string.push_str(text.text());
59 } else {
60 string = Some(Cow::Borrowed(text.text()));
61 }
62 }
63 }
64
65 if string.is_none() {
67 string = self.iter().find_map(|a| match &a.kind {
68 AtomKind::Image(image) => image.alt_text.as_deref().map(Cow::Borrowed),
69 _ => None,
70 });
71 }
72
73 string
74 }
75
76 pub fn any_shrink(&self) -> bool {
78 self.iter().any(|a| a.shrink)
79 }
80
81 pub fn iter_kinds(&self) -> impl Iterator<Item = &AtomKind<'a>> {
82 self.0.iter().map(|atom| &atom.kind)
83 }
84
85 pub fn iter_kinds_mut(&mut self) -> impl Iterator<Item = &mut AtomKind<'a>> {
86 self.0.iter_mut().map(|atom| &mut atom.kind)
87 }
88
89 pub fn iter_images(&self) -> impl Iterator<Item = &Image<'a>> {
90 self.iter_kinds().filter_map(|kind| {
91 if let AtomKind::Image(image) = kind {
92 Some(image)
93 } else {
94 None
95 }
96 })
97 }
98
99 pub fn iter_images_mut(&mut self) -> impl Iterator<Item = &mut Image<'a>> {
100 self.iter_kinds_mut().filter_map(|kind| {
101 if let AtomKind::Image(image) = kind {
102 Some(image)
103 } else {
104 None
105 }
106 })
107 }
108
109 pub fn iter_texts(&self) -> impl Iterator<Item = &WidgetText> + use<'_, 'a> {
110 self.iter_kinds().filter_map(|kind| {
111 if let AtomKind::Text(text) = kind {
112 Some(text)
113 } else {
114 None
115 }
116 })
117 }
118
119 pub fn iter_texts_mut(&mut self) -> impl Iterator<Item = &mut WidgetText> + use<'a, '_> {
120 self.iter_kinds_mut().filter_map(|kind| {
121 if let AtomKind::Text(text) = kind {
122 Some(text)
123 } else {
124 None
125 }
126 })
127 }
128
129 pub fn map_atoms(&mut self, mut f: impl FnMut(Atom<'a>) -> Atom<'a>) {
130 self.iter_mut()
131 .for_each(|atom| *atom = f(core::mem::take(atom)));
132 }
133
134 pub fn map_kind<F>(&mut self, mut f: F)
135 where
136 F: FnMut(AtomKind<'a>) -> AtomKind<'a>,
137 {
138 for kind in self.iter_kinds_mut() {
139 *kind = f(core::mem::take(kind));
140 }
141 }
142
143 pub fn map_images<F>(&mut self, mut f: F)
144 where
145 F: FnMut(Image<'a>) -> Image<'a>,
146 {
147 self.map_kind(|kind| {
148 if let AtomKind::Image(image) = kind {
149 AtomKind::Image(f(image))
150 } else {
151 kind
152 }
153 });
154 }
155
156 pub fn map_texts<F>(&mut self, mut f: F)
157 where
158 F: FnMut(WidgetText) -> WidgetText,
159 {
160 self.map_kind(|kind| {
161 if let AtomKind::Text(text) = kind {
162 AtomKind::Text(f(text))
163 } else {
164 kind
165 }
166 });
167 }
168}
169
170impl<'a> IntoIterator for Atoms<'a> {
171 type Item = Atom<'a>;
172 type IntoIter = std::vec::IntoIter<Atom<'a>>;
173
174 fn into_iter(self) -> Self::IntoIter {
175 self.0.into_iter()
176 }
177}
178
179impl<'a, T> IntoAtoms<'a> for T
190where
191 T: Into<Atom<'a>>,
192{
193 fn collect(self, atoms: &mut Atoms<'a>) {
194 atoms.push_right(self);
195 }
196}
197
198pub trait IntoAtoms<'a> {
210 fn collect(self, atoms: &mut Atoms<'a>);
211
212 fn into_atoms(self) -> Atoms<'a>
213 where
214 Self: Sized,
215 {
216 let mut atoms = Atoms::default();
217 self.collect(&mut atoms);
218 atoms
219 }
220}
221
222impl<'a> IntoAtoms<'a> for Atoms<'a> {
223 fn collect(self, atoms: &mut Self) {
224 atoms.0.extend(self.0);
225 }
226}
227
228macro_rules! all_the_atoms {
229 ($($T:ident),*) => {
230 impl<'a, $($T),*> IntoAtoms<'a> for ($($T),*)
231 where
232 $($T: IntoAtoms<'a>),*
233 {
234 fn collect(self, _atoms: &mut Atoms<'a>) {
235 #[allow(clippy::allow_attributes, non_snake_case)]
236 let ($($T),*) = self;
237 $($T.collect(_atoms);)*
238 }
239 }
240 };
241}
242
243all_the_atoms!();
244all_the_atoms!(T0, T1);
245all_the_atoms!(T0, T1, T2);
246all_the_atoms!(T0, T1, T2, T3);
247all_the_atoms!(T0, T1, T2, T3, T4);
248all_the_atoms!(T0, T1, T2, T3, T4, T5);
249
250impl<'a> Deref for Atoms<'a> {
251 type Target = [Atom<'a>];
252
253 fn deref(&self) -> &Self::Target {
254 &self.0
255 }
256}
257
258impl DerefMut for Atoms<'_> {
259 fn deref_mut(&mut self) -> &mut Self::Target {
260 &mut self.0
261 }
262}
263
264impl<'a, T: Into<Atom<'a>>> From<Vec<T>> for Atoms<'a> {
265 fn from(vec: Vec<T>) -> Self {
266 Atoms(vec.into_iter().map(Into::into).collect())
267 }
268}
269
270impl<'a, T: Into<Atom<'a>> + Clone> From<&[T]> for Atoms<'a> {
271 fn from(slice: &[T]) -> Self {
272 Atoms(slice.iter().cloned().map(Into::into).collect())
273 }
274}
275
276impl<'a, Item: Into<Atom<'a>>> FromIterator<Item> for Atoms<'a> {
277 fn from_iter<T: IntoIterator<Item = Item>>(iter: T) -> Self {
278 Atoms(iter.into_iter().map(Into::into).collect())
279 }
280}
281
282#[cfg(test)]
283mod tests {
284 use crate::Atoms;
285
286 #[test]
287 fn collect_atoms() {
288 let _: Atoms<'_> = ["Hello", "World"].into_iter().collect();
289 let _ = Atoms::from(vec!["Hi"]);
290 let _ = Atoms::from(["Hi"].as_slice());
291 }
292}