1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
use ansistr::{TextAlign, repaire_str, trucate_str, wrap_str, pad_str};
#[derive(Debug, Clone, PartialEq)]
pub struct Column {
width: Option<usize>,
text: Option<String>,
text_width: Option<usize>,
text_align: Option<TextAlign>,
text_tail: Option<String>,
text_pad: Option<String>,
}
impl Column {
pub fn new() -> Self {
Self {
width: None,
text: None,
text_width: None,
text_align: None,
text_tail: None,
text_pad: None,
}
}
pub fn from_str<S: Into<String>>(txt: S) -> Self {
Self {
width: None,
text: Some(txt.into()),
text_width: None,
text_align: None,
text_tail: None,
text_pad: None,
}
}
pub fn width(&self) -> &Option<usize> {
&self.width
}
pub fn text(&self) -> &Option<String> {
&self.text
}
pub fn text_width(&self) -> &Option<usize> {
&self.text_width
}
pub fn text_align(&self) -> &TextAlign {
match &self.text_align {
Some(t) => t,
None => &TextAlign::Left,
}
}
pub fn text_tail(&self) -> &str {
match &self.text_tail {
Some(t) => t,
None => "...",
}
}
pub fn text_pad(&self) -> &str {
match &self.text_pad {
Some(t) => t,
None => &" ",
}
}
pub fn set_width(mut self, width: usize) -> Self {
self.width = Some(width);
self
}
pub fn set_text<S: Into<String>>(mut self, text: S) -> Self {
self.text = Some(text.into());
self
}
pub fn set_text_width(mut self, width: usize) -> Self {
self.text_width = Some(width);
self
}
pub fn set_text_align(mut self, align: TextAlign) -> Self {
self.text_align = Some(align);
self
}
pub fn set_text_tail<S: Into<String>>(mut self, tail: S) -> Self {
self.text_tail = Some(tail.into());
self
}
pub fn set_text_pad<S: Into<String>>(mut self, pad: S) -> Self {
self.text_pad = Some(pad.into());
self
}
pub fn to_string(&self) -> String {
let mut text = match &self.text {
Some(t) => t.to_string(),
None => return "".to_string(),
};
if self.text_width.is_some() {
text = trucate_str(text, self.text_width.unwrap(), self.text_align(), self.text_tail());
}
let text = if self.width.is_some() {
let width = self.width.unwrap();
wrap_str(text, width)
.split("\n")
.map(|r| pad_str(r, width, self.text_align(), self.text_pad()))
.collect::<Vec<String>>()
.join("\n")
} else {
text
};
format!("{}\n", repaire_str(text))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn builds_multiline_string() {
let column = Column::new()
.set_text("Allocating memory \x1B[31mis actually quite fast, and regardless you’re going to be copying the entire\x1B[39m string around.")
.set_width(30)
.set_text_width(72)
.set_text_align(TextAlign::Center)
.set_text_tail("+++")
.set_text_pad("!");
assert_eq!(column.to_string(), [
"Allocating memory \u{1b}[31mis actually!\u{1b}[39m\n",
"\u{1b}[31m!quit+++be copying the entire\u{1b}[39m!\n",
"!!!!!!!!!string aroun!!!!!!!!!\n",
].join(""));
}
}