1use crate::{Atom, AtomKind, Image, WidgetText};
2use smallvec::SmallVec;
3use std::borrow::Cow;
4use std::ops::{Deref, DerefMut};
5
6pub(crate) const ATOMS_SMALL_VEC_SIZE: usize = 2;
9
10#[derive(Clone, Debug, Default)]
12pub struct Atoms<'a>(SmallVec<[Atom<'a>; ATOMS_SMALL_VEC_SIZE]>);
13
14impl<'a> Atoms<'a> {
15 pub fn new(atoms: impl IntoAtoms<'a>) -> Self {
16 atoms.into_atoms()
17 }
18
19 pub fn push_right(&mut self, atom: impl Into<Atom<'a>>) {
21 self.0.push(atom.into());
22 }
23
24 pub fn push_left(&mut self, atom: impl Into<Atom<'a>>) {
26 self.0.insert(0, atom.into());
27 }
28
29 pub fn text(&self) -> Option<Cow<'_, str>> {
33 let mut string: Option<Cow<'_, str>> = None;
34 for atom in &self.0 {
35 if let AtomKind::Text(text) = &atom.kind {
36 if let Some(string) = &mut string {
37 let string = string.to_mut();
38 string.push(' ');
39 string.push_str(text.text());
40 } else {
41 string = Some(Cow::Borrowed(text.text()));
42 }
43 }
44 }
45
46 if string.is_none() {
48 string = self.iter().find_map(|a| match &a.kind {
49 AtomKind::Image(image) => image.alt_text.as_deref().map(Cow::Borrowed),
50 _ => None,
51 });
52 }
53
54 string
55 }
56
57 pub fn iter_kinds(&self) -> impl Iterator<Item = &AtomKind<'a>> {
58 self.0.iter().map(|atom| &atom.kind)
59 }
60
61 pub fn iter_kinds_mut(&mut self) -> impl Iterator<Item = &mut AtomKind<'a>> {
62 self.0.iter_mut().map(|atom| &mut atom.kind)
63 }
64
65 pub fn iter_images(&self) -> impl Iterator<Item = &Image<'a>> {
66 self.iter_kinds().filter_map(|kind| {
67 if let AtomKind::Image(image) = kind {
68 Some(image)
69 } else {
70 None
71 }
72 })
73 }
74
75 pub fn iter_images_mut(&mut self) -> impl Iterator<Item = &mut Image<'a>> {
76 self.iter_kinds_mut().filter_map(|kind| {
77 if let AtomKind::Image(image) = kind {
78 Some(image)
79 } else {
80 None
81 }
82 })
83 }
84
85 pub fn iter_texts(&self) -> impl Iterator<Item = &WidgetText> + use<'_, 'a> {
86 self.iter_kinds().filter_map(|kind| {
87 if let AtomKind::Text(text) = kind {
88 Some(text)
89 } else {
90 None
91 }
92 })
93 }
94
95 pub fn iter_texts_mut(&mut self) -> impl Iterator<Item = &mut WidgetText> + use<'a, '_> {
96 self.iter_kinds_mut().filter_map(|kind| {
97 if let AtomKind::Text(text) = kind {
98 Some(text)
99 } else {
100 None
101 }
102 })
103 }
104
105 pub fn map_atoms(&mut self, mut f: impl FnMut(Atom<'a>) -> Atom<'a>) {
106 self.iter_mut()
107 .for_each(|atom| *atom = f(std::mem::take(atom)));
108 }
109
110 pub fn map_kind<F>(&mut self, mut f: F)
111 where
112 F: FnMut(AtomKind<'a>) -> AtomKind<'a>,
113 {
114 for kind in self.iter_kinds_mut() {
115 *kind = f(std::mem::take(kind));
116 }
117 }
118
119 pub fn map_images<F>(&mut self, mut f: F)
120 where
121 F: FnMut(Image<'a>) -> Image<'a>,
122 {
123 self.map_kind(|kind| {
124 if let AtomKind::Image(image) = kind {
125 AtomKind::Image(f(image))
126 } else {
127 kind
128 }
129 });
130 }
131
132 pub fn map_texts<F>(&mut self, mut f: F)
133 where
134 F: FnMut(WidgetText) -> WidgetText,
135 {
136 self.map_kind(|kind| {
137 if let AtomKind::Text(text) = kind {
138 AtomKind::Text(f(text))
139 } else {
140 kind
141 }
142 });
143 }
144}
145
146impl<'a> IntoIterator for Atoms<'a> {
147 type Item = Atom<'a>;
148 type IntoIter = smallvec::IntoIter<[Atom<'a>; ATOMS_SMALL_VEC_SIZE]>;
149
150 fn into_iter(self) -> Self::IntoIter {
151 self.0.into_iter()
152 }
153}
154
155impl<'a, T> IntoAtoms<'a> for T
166where
167 T: Into<Atom<'a>>,
168{
169 fn collect(self, atoms: &mut Atoms<'a>) {
170 atoms.push_right(self);
171 }
172}
173
174pub trait IntoAtoms<'a> {
176 fn collect(self, atoms: &mut Atoms<'a>);
177
178 fn into_atoms(self) -> Atoms<'a>
179 where
180 Self: Sized,
181 {
182 let mut atoms = Atoms::default();
183 self.collect(&mut atoms);
184 atoms
185 }
186}
187
188impl<'a> IntoAtoms<'a> for Atoms<'a> {
189 fn collect(self, atoms: &mut Self) {
190 atoms.0.extend(self.0);
191 }
192}
193
194macro_rules! all_the_atoms {
195 ($($T:ident),*) => {
196 impl<'a, $($T),*> IntoAtoms<'a> for ($($T),*)
197 where
198 $($T: IntoAtoms<'a>),*
199 {
200 fn collect(self, _atoms: &mut Atoms<'a>) {
201 #[allow(clippy::allow_attributes)]
202 #[allow(non_snake_case)]
203 let ($($T),*) = self;
204 $($T.collect(_atoms);)*
205 }
206 }
207 };
208}
209
210all_the_atoms!();
211all_the_atoms!(T0, T1);
212all_the_atoms!(T0, T1, T2);
213all_the_atoms!(T0, T1, T2, T3);
214all_the_atoms!(T0, T1, T2, T3, T4);
215all_the_atoms!(T0, T1, T2, T3, T4, T5);
216
217impl<'a> Deref for Atoms<'a> {
218 type Target = [Atom<'a>];
219
220 fn deref(&self) -> &Self::Target {
221 &self.0
222 }
223}
224
225impl DerefMut for Atoms<'_> {
226 fn deref_mut(&mut self) -> &mut Self::Target {
227 &mut self.0
228 }
229}
230
231impl<'a, T: Into<Atom<'a>>> From<Vec<T>> for Atoms<'a> {
232 fn from(vec: Vec<T>) -> Self {
233 Atoms(vec.into_iter().map(Into::into).collect())
234 }
235}
236
237impl<'a, T: Into<Atom<'a>> + Clone> From<&[T]> for Atoms<'a> {
238 fn from(slice: &[T]) -> Self {
239 Atoms(slice.iter().cloned().map(Into::into).collect())
240 }
241}
242
243impl<'a, Item: Into<Atom<'a>>> FromIterator<Item> for Atoms<'a> {
244 fn from_iter<T: IntoIterator<Item = Item>>(iter: T) -> Self {
245 Atoms(iter.into_iter().map(Into::into).collect())
246 }
247}
248
249#[cfg(test)]
250mod tests {
251 use crate::Atoms;
252
253 #[test]
254 fn collect_atoms() {
255 let _: Atoms<'_> = ["Hello", "World"].into_iter().collect();
256 let _ = Atoms::from(vec!["Hi"]);
257 let _ = Atoms::from(["Hi"].as_slice());
258 }
259}