line_ui/element/
impls.rs

1/*
2 * Copyright (c) 2025 Jasmine Tai. All rights reserved.
3 */
4
5use crate::element::Element;
6use crate::render::RenderChunk;
7
8impl<'s, E: Element<'s> + ?Sized> Element<'s> for &'_ E {
9    fn width(&self) -> usize {
10        (*self).width()
11    }
12
13    fn render(&self) -> impl DoubleEndedIterator<Item = RenderChunk<'s>> {
14        (*self).render()
15    }
16}
17
18impl<'s, E: Element<'s>> Element<'s> for [E] {
19    fn width(&self) -> usize {
20        self.iter().map(|e| e.width()).sum()
21    }
22
23    fn render(&self) -> impl DoubleEndedIterator<Item = RenderChunk<'s>> {
24        self.iter().flat_map(|e| e.render())
25    }
26}
27
28impl<'s, E: Element<'s>, const N: usize> Element<'s> for [E; N] {
29    fn width(&self) -> usize {
30        self.iter().map(|e| e.width()).sum()
31    }
32
33    fn render(&self) -> impl DoubleEndedIterator<Item = RenderChunk<'s>> {
34        self.iter().flat_map(|e| e.render())
35    }
36}
37
38impl<'s, E: Element<'s>> Element<'s> for Option<E> {
39    fn width(&self) -> usize {
40        match self {
41            Some(inner) => inner.width(),
42            None => 0,
43        }
44    }
45
46    fn render(&self) -> impl DoubleEndedIterator<Item = RenderChunk<'s>> {
47        self.iter().flat_map(|e| e.render())
48    }
49}
50
51impl<'s> Element<'s> for () {
52    fn width(&self) -> usize {
53        0
54    }
55
56    fn render(&self) -> impl DoubleEndedIterator<Item = RenderChunk<'s>> {
57        std::iter::empty()
58    }
59}
60
61macro_rules! impl_element_for_tuple {
62    ( A $( $t:ident )* , 0 $( $n:tt )* ) => {
63        impl<'s, A $(, $t)*> Element<'s> for (A, $($t),*)
64        where
65            A: Element<'s>,
66            $($t: Element<'s>,)*
67        {
68            fn width(&self) -> usize {
69                self.0.width()
70                $(+ self.$n.width())*
71            }
72
73            fn render(&self) -> impl DoubleEndedIterator<Item = RenderChunk<'s>> {
74                self.0.render()
75                $(.chain(self.$n.render()))*
76            }
77        }
78    };
79}
80
81impl_element_for_tuple!(A, 0);
82impl_element_for_tuple!(A B, 0 1);
83impl_element_for_tuple!(A B C, 0 1 2);
84impl_element_for_tuple!(A B C D, 0 1 2 3);
85impl_element_for_tuple!(A B C D E, 0 1 2 3 4);
86impl_element_for_tuple!(A B C D E F, 0 1 2 3 4 5);
87impl_element_for_tuple!(A B C D E F G, 0 1 2 3 4 5 6);
88impl_element_for_tuple!(A B C D E F G H, 0 1 2 3 4 5 6 7);
89impl_element_for_tuple!(A B C D E F G H I, 0 1 2 3 4 5 6 7 8);
90impl_element_for_tuple!(A B C D E F G H I J, 0 1 2 3 4 5 6 7 8 9);
91
92#[cfg(feature = "either")]
93impl<'s, L: Element<'s>, R: Element<'s>> Element<'s> for either::Either<L, R> {
94    fn width(&self) -> usize {
95        either::for_both!(self, inner => inner.width())
96    }
97
98    fn render(&self) -> impl DoubleEndedIterator<Item = RenderChunk<'s>> {
99        self.as_ref().map_either(L::render, R::render)
100    }
101}
102
103#[cfg(test)]
104mod tests {
105    use crate::element::{Gap, IntoElement};
106
107    use super::*;
108
109    fn is_element<'s, E: Element<'s> + ?Sized>() {}
110    fn value_is_element<'s, E: Element<'s> + ?Sized>(_: &E) {}
111
112    #[test]
113    fn element_impls() {
114        is_element::<[Gap]>();
115        is_element::<&[Gap]>();
116        is_element::<[Gap; 42]>();
117        is_element::<[&[Gap]; 4]>();
118        is_element::<((), (), (), (), (), (), (), Gap, (), ())>();
119
120        let not_static = "foo".to_owned();
121        let not_static = not_static[..].into_element();
122        value_is_element(&not_static);
123        value_is_element(&(not_static, Gap(1)));
124
125        #[cfg(feature = "either")]
126        is_element::<either::Either<Gap, (Gap, Gap)>>();
127    }
128
129    fn same_lifetime<'s, L: Element<'s>, R: Element<'s>>(_: &L, _: &R) {}
130
131    #[test]
132    fn element_lifetime() {
133        let not_static = "foo".to_owned();
134        let mut not_static = not_static[..].into_element();
135        let is_static = "foo".into_element();
136        same_lifetime(&is_static, &not_static);
137        same_lifetime(&not_static, &is_static);
138        not_static = is_static;
139        let _ = not_static;
140    }
141}