tbltyp/
row.rs

1use std::fmt;
2use ansistr::{pad_str};
3use crate::{Column};
4
5/// Row structure which represents multiple formated columns.
6#[derive(Debug, Clone, PartialEq)]
7pub struct Row {
8    columns: Vec<Column>,
9}
10
11/// Style structure implementation.
12impl Row {
13
14    /// Returns new instance.
15    pub fn new() -> Self {
16        Self {
17            columns: Vec::new(),
18        }
19    }
20
21    /// Adds new row column.
22    pub fn add_column(mut self, column: Column) -> Self {
23        self.columns.push(column);
24        self
25    }
26
27    /// Returns a formatted row content as multiline string.
28    pub fn build(&self) -> String {
29        let mut result = Vec::new();
30
31        let xcount = match self.columns.len() {
32            0 => return "".to_string(),
33            v => v,
34        };
35        let ycount = match self.columns.iter()
36            .map(|c| c.to_string().matches("\n").count())
37            .max() {
38            Some(v) => v,
39            None => return "".to_string(),
40        };
41
42        for _ in 0..ycount {
43            result.push("".to_string());
44        }
45
46        for x in 0..xcount {
47            let column = self.columns.get(x).unwrap();
48            let rows: Vec<String> = column.to_string()
49                .split("\n")
50                .map(|c| c.to_string())
51                .collect();
52
53            for y in 0..ycount {
54                let row = rows.get(y);
55                let text = match row {
56                    Some(t) => t,
57                    None => "",
58                };
59                let text = match column.width() {
60                    Some(v) => pad_str(text, *v, column.text_align(), column.text_pad()),
61                    None => text.to_string(),
62                };
63                result[y] = format!("{}{}", result[y], text);
64            }
65        }
66    
67        format!("{}\n", result.join("\n"))
68    }
69}
70
71impl fmt::Display for Row {
72    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
73        write!(fmt, "{}", self.build())
74    }
75}
76
77impl From<Row> for String {
78    fn from(item: Row) -> String {
79        item.build()
80    }
81}
82
83#[cfg(test)]
84mod tests {
85    use crate::*;
86
87    #[test]
88    fn builds_multiline_string() {
89        let column0 = Column::new()
90            .set_width(30)
91            .set_text("Allocating \x1B[31mmemory is actually quite fast, and regardless you’re going to be copying the entire\x1B[39m string around.")
92            .set_text_pad("*");
93        let column1 = Column::new()
94            .set_width(3)
95            .set_text_pad("|");
96        let column2 = Column::new()
97            .set_width(20)
98            .set_text("Going 利干 to be the entire string around.")
99            .set_text_pad("+");
100        let row = Row::new()
101            .add_column(column0)
102            .add_column(column1)
103            .add_column(column2);
104        assert_eq!(row.build(), vec![
105            "Allocating \u{1b}[31mmemory is actually*\u{1b}[39m|||Going 利干 to be the\n",
106            "\u{1b}[31mquite fast, and regardless****\u{1b}[39m|||entire string+++++++\n",
107            "\u{1b}[31myou’re going to be copying****\u{1b}[39m|||around.+++++++++++++\n",
108            "\u{1b}[31mthe entire\u{1b}[39m string around.*****|||++++++++++++++++++++\n",
109        ].join(""));
110    }
111
112    #[test]
113    fn converts_to_string() {
114        fn convert<S: Into<String>>(txt: S) -> String {
115            txt.into()
116        }
117        assert_eq!(convert(
118            Row::new().add_column(
119                Column::new().set_text("foo")
120            )
121        ), "foo\n");
122    }
123}