tv_cli/table/
cell.rs

1use unicode_width::UnicodeWidthStr;
2
3#[derive(Debug, Clone, Copy)]
4pub enum Align {
5    None,
6    Left,
7    Center,
8    Right,
9}
10
11impl Align {
12    pub fn new(s: String) -> Self {
13        match s.to_lowercase().as_str() {
14            "l" | "left" => Self::Left,
15            "c" | "center" => Self::Center,
16            "r" | "right" => Self::Right,
17            _ => Self::None,
18        }
19    }
20}
21
22#[derive(Debug, Clone)]
23pub struct Cell<T>
24where
25    T: std::fmt::Display,
26{
27    value: T,
28    width: usize,
29    align: Align,
30}
31
32impl<T> Cell<T>
33where
34    T: std::fmt::Display + Clone,
35{
36    pub fn new(v: T) -> Self {
37        Self {
38            value: v.clone(),
39            width: format!("{}", v.clone()).width(),
40            align: Align::None,
41        }
42    }
43
44    pub fn set_width(&mut self, w: usize) -> &mut Self {
45        self.width = w;
46        self
47    }
48
49    pub fn set_align(&mut self, a: Align) -> &mut Self {
50        self.align = a;
51        self
52    }
53
54    pub fn width(&self) -> usize {
55        self.width
56    }
57}
58
59impl<T> std::fmt::Display for Cell<T>
60where
61    T: std::fmt::Display,
62{
63    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64        let s = match self.align {
65            Align::Right => {
66                format!(
67                    "{}{}",
68                    " ".repeat(self.width - format!("{}", self.value).width()),
69                    self.value
70                )
71            }
72            Align::None | Align::Left => format!(
73                "{}{}",
74                self.value,
75                " ".repeat(self.width - format!("{}", self.value).width())
76            ),
77            Align::Center => {
78                let pad = (self.width - format!("{}", self.value).width()) as f32 / 2.0;
79                let left_pad = pad.ceil() as usize;
80                let right_pad = pad.floor() as usize;
81                format!(
82                    "{}{}{}",
83                    " ".repeat(left_pad),
84                    self.value,
85                    " ".repeat(right_pad)
86                )
87            }
88        };
89
90        write!(f, "{}", s)
91    }
92}
93
94#[cfg(test)]
95mod tests {
96    use super::{Align, Cell};
97
98    #[test]
99    fn create_cell() {
100        let cell = Cell::new(String::from("Hello"));
101        assert_eq!(cell.value, String::from("Hello"));
102    }
103
104    #[test]
105    fn display_cell() {
106        let cell = Cell::new(String::from("Hello"));
107        assert_eq!("Hello", format!("{}", cell))
108    }
109
110    #[test]
111    fn display_cell_with_width() {
112        let mut cell = Cell::new(String::from("Hello"));
113        cell.set_width(10);
114        assert_eq!("Hello     ", format!("{}", cell))
115    }
116
117    #[test]
118    fn display_cell_with_align() {
119        struct TestCase<'a> {
120            value: &'a str,
121            width: usize,
122            align: Align,
123            expect: &'a str,
124        }
125
126        let data_set = vec![
127            TestCase {
128                value: "Hello",
129                width: 10,
130                align: Align::None,
131                expect: "Hello     ",
132            },
133            TestCase {
134                value: "Hello",
135                width: 10,
136                align: Align::Left,
137                expect: "Hello     ",
138            },
139            TestCase {
140                value: "Hello",
141                width: 10,
142                align: Align::Center,
143                expect: "   Hello  ",
144            },
145            TestCase {
146                value: "Hello",
147                width: 10,
148                align: Align::Right,
149                expect: "     Hello",
150            },
151        ];
152
153        data_set.iter().for_each(|case| {
154            let mut cell = Cell::new(case.value);
155            cell.set_width(case.width).set_align(case.align);
156            assert_eq!(case.expect, format!("{}", cell));
157        });
158    }
159}