line_ui/element/
gap.rs

1/*
2 * Copyright (c) 2025 Jasmine Tai. All rights reserved.
3 */
4
5use std::marker::PhantomData;
6
7use crate::Style;
8use crate::element::Element;
9use crate::render::RenderChunk;
10
11/// An element that renders a blank space of the specified width.
12#[derive(Debug, Clone, Copy)]
13pub struct Gap(pub usize);
14
15impl Gap {
16    pub(crate) fn into_render<'s>(self) -> impl DoubleEndedIterator<Item = RenderChunk<'s>> {
17        GapIter {
18            size: self.0,
19            phantom: PhantomData,
20        }
21    }
22}
23
24impl Element for Gap {
25    fn width(&self) -> usize {
26        self.0
27    }
28
29    fn render(&self) -> impl DoubleEndedIterator<Item = RenderChunk<'_>> {
30        GapIter {
31            size: self.0,
32            phantom: PhantomData,
33        }
34    }
35}
36
37struct GapIter<'s> {
38    size: usize,
39    phantom: PhantomData<&'s ()>,
40}
41
42// I could just set Item = StyledStr<'static>, but the compiler doesn't like
43// that for some reason
44impl<'s> Iterator for GapIter<'s> {
45    type Item = RenderChunk<'s>;
46
47    fn size_hint(&self) -> (usize, Option<usize>) {
48        let value = self.size.div_ceil(GAP.len());
49        (value, Some(value))
50    }
51
52    fn next(&mut self) -> Option<Self::Item> {
53        if self.size == 0 {
54            None
55        } else {
56            let spaces = self.size.min(GAP.len());
57            self.size -= spaces;
58            Some(RenderChunk::with_known_width(
59                &GAP[..spaces],
60                spaces,
61                Style::EMPTY,
62            ))
63        }
64    }
65}
66
67impl<'s> DoubleEndedIterator for GapIter<'s> {
68    fn next_back(&mut self) -> Option<Self::Item> {
69        self.next()
70    }
71}
72
73const GAP: &str = "                "; // 16 spaces
74
75#[cfg(test)]
76mod tests {
77    use super::*;
78
79    #[test]
80    fn empty() {
81        let element = Gap(0);
82        let render: Vec<_> = element.render().collect();
83        assert_eq!(render, []);
84    }
85
86    #[test]
87    fn short() {
88        let element = Gap(7);
89        let render: Vec<_> = element.render().collect();
90        assert_eq!(render, ["       ".into()]);
91    }
92
93    #[test]
94    fn long() {
95        let element = Gap(GAP.len() + 2);
96        let render: Vec<_> = element.render().collect();
97        assert_eq!(render, [GAP.into(), "  ".into()]);
98    }
99}