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