modern_terminal/core/
segment.rs

1#[derive(Clone, Debug)]
2pub enum SegmentPortion {
3    Style(crate::core::style::Style),
4    Text(String),
5}
6
7#[derive(Clone, Copy, Debug, PartialEq)]
8pub enum SegmentPadding {
9    Center(usize),
10    Left(usize),
11    None,
12    Right(usize),
13}
14
15#[derive(Clone, Debug)]
16pub struct Segment {
17    pub portions: Vec<SegmentPortion>,
18}
19
20pub type RenderedSegment = String;
21pub type RenderedSegments = Vec<RenderedSegment>;
22
23impl Segment {
24    pub fn new() -> Segment {
25        Segment { portions: vec![] }
26    }
27
28    pub fn add_text(
29        &mut self,
30        text: &str,
31    ) {
32        self.portions.push(SegmentPortion::Text(String::from(text)));
33    }
34
35    pub fn add_style(
36        &mut self,
37        style: crate::core::style::Style,
38    ) {
39        self.portions.push(SegmentPortion::Style(style));
40    }
41
42    pub fn render(
43        &self,
44        padding: SegmentPadding,
45        storage: Option<crate::core::color::storage::Storage>,
46    ) -> String {
47        let mut rendered = String::new();
48
49        let rendered_length: usize = self
50            .portions
51            .iter()
52            .map(|portion| match portion {
53                SegmentPortion::Text(text) => text.len(),
54                SegmentPortion::Style(_) => 0,
55            })
56            .sum();
57
58        match padding {
59            SegmentPadding::Center(desired_length) => {
60                if desired_length > rendered_length {
61                    rendered.push_str(&whitespace(
62                        (desired_length - rendered_length) / 2,
63                    ));
64                };
65            },
66            SegmentPadding::Right(desired_length) => {
67                if desired_length > rendered_length {
68                    rendered.push_str(&whitespace(
69                        desired_length - rendered_length,
70                    ));
71                }
72            },
73            _ => {},
74        };
75
76        for portion in self.portions.iter() {
77            match portion {
78                SegmentPortion::Text(text) => {
79                    rendered.push_str(&text);
80                },
81                SegmentPortion::Style(style) => match storage {
82                    Some(storage) => {
83                        rendered.push_str(&style.ansi_escape_code(storage));
84                    },
85                    None => {},
86                },
87            }
88        }
89
90        match storage {
91            Some(storage) => rendered.push_str(
92                &crate::core::style::Style::None.ansi_escape_code(storage),
93            ),
94            None => (),
95        };
96
97        match padding {
98            SegmentPadding::Left(desired_length) => {
99                if desired_length > rendered_length {
100                    rendered.push_str(&whitespace(
101                        desired_length - rendered_length,
102                    ));
103                }
104            },
105            SegmentPadding::Center(desired_length) => {
106                if desired_length > rendered_length {
107                    rendered.push_str(&whitespace(
108                        desired_length
109                            - rendered_length
110                            - (desired_length - rendered_length) / 2,
111                    ));
112                }
113            },
114            _ => {},
115        }
116
117        rendered
118    }
119}
120
121fn whitespace(length: usize) -> String {
122    std::iter::repeat(" ").take(length).collect::<String>()
123}
124
125#[cfg(test)]
126mod test_segment {
127    use super::{Segment, SegmentPadding};
128
129    #[test]
130    fn new() {
131        let mut segment = Segment::new();
132        segment.add_style(crate::core::style::Style::Bold);
133        segment.add_text("a b c d");
134
135        assert_eq!(segment.render(SegmentPadding::None, None), "a b c d");
136        assert_eq!(segment.render(SegmentPadding::Center(8), None), "a b c d ");
137        assert_eq!(segment.render(SegmentPadding::Left(8), None), "a b c d ");
138        assert_eq!(segment.render(SegmentPadding::Right(8), None), " a b c d");
139        assert_eq!(
140            segment.render(
141                SegmentPadding::Center(9),
142                Some(crate::core::color::storage::Storage::Bits24)
143            ),
144            " \u{1b}[1ma b c d\u{1b}[0m "
145        );
146    }
147}