1use std::fmt;
2use ansistr::{pad_str};
3use crate::{Column};
4
5#[derive(Debug, Clone, PartialEq)]
7pub struct Row {
8 columns: Vec<Column>,
9}
10
11impl Row {
13
14 pub fn new() -> Self {
16 Self {
17 columns: Vec::new(),
18 }
19 }
20
21 pub fn add_column(mut self, column: Column) -> Self {
23 self.columns.push(column);
24 self
25 }
26
27 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}