1pub mod tbl;
2
3use terminal_size::{terminal_size, Height, Width};
4
5pub use yansi::{Color, Painted};
6
7pub enum Types {
8 Paragraph(String)
9}
10
11#[derive(Copy, Clone, Debug)]
12pub enum Align {
13 Left,
14 Center,
15 Right
16}
17
18pub struct Column {
19 pub title: String,
20 pub title_align: Align,
21 pub align: Align
22}
23
24impl Column {
25 #[allow(clippy::needless_pass_by_value)]
26 pub fn new(heading: impl ToString) -> Self {
27 Self {
28 title: heading.to_string(),
29 title_align: Align::Left,
30 align: Align::Left
31 }
32 }
33
34 #[must_use]
35 pub const fn title_align(mut self, align: Align) -> Self {
36 self.title_align = align;
37 self
38 }
39
40 #[must_use]
41 pub const fn align(mut self, align: Align) -> Self {
42 self.align = align;
43 self
44 }
45}
46
47
48fn wordify(s: &str) -> Vec<String> {
49 let mut words = Vec::new();
50 let splt = s.split_whitespace();
51
52 for w in splt {
53 words.push(w.to_string());
54 }
55 words
56}
57
58pub struct PPrint {
59 indent: u16,
60 hang: i16,
61 maxwidth: u16
62}
63
64impl Default for PPrint {
65 fn default() -> Self {
66 Self::new()
67 }
68}
69
70impl PPrint {
71 #[must_use]
72 pub fn new() -> Self {
73 let size = terminal_size();
74 let mut maxwidth: u16 = 80;
75 if let Some((Width(w), Height(_h))) = size {
76 maxwidth = w;
77 }
78
79 Self {
80 indent: 0,
81 hang: 0,
82 maxwidth
83 }
84 }
85
86 pub const fn set_indent(&mut self, indent: u16) -> &mut Self {
87 self.indent = indent;
88 self
89 }
90
91 pub const fn set_hang(&mut self, hang: i16) -> &mut Self {
93 self.hang = hang;
94 self
95 }
96
97 #[allow(clippy::cast_possible_truncation)]
107 #[allow(clippy::cast_possible_wrap)]
108 #[allow(clippy::cast_sign_loss)]
109 pub fn print_words<I, S>(&self, out: &mut dyn std::io::Write, words: I)
110 where
111 I: IntoIterator<Item = S>,
112 S: AsRef<str>
113 {
114 let mut firstline = true;
115 let mut newline = true;
116 let mut space: u16 = 0;
117 let mut col: u16 = 0;
118
119 for w in words {
120 let w = w.as_ref();
121 if col + space + w.len() as u16 > self.maxwidth {
122 out.write_all(b"\n").unwrap();
123 newline = true;
124 }
125
126 if newline {
127 let mut indent: i16 = 0;
128
129 indent += self.indent as i16;
130 if firstline {
131 indent += self.hang;
132 }
133
134 out
135 .write_all(" ".repeat(indent as usize).as_bytes())
136 .unwrap();
137 col = indent as u16;
138
139 newline = false;
140 space = 0;
141 firstline = false;
142 }
143
144 out
145 .write_all(" ".repeat(space as usize).as_bytes())
146 .unwrap();
147 col += space;
148
149 out.write_all(w.as_bytes()).unwrap();
150 col += w.len() as u16;
151
152 let ch = w.chars().last().unwrap();
153 match ch {
154 '.' | '?' | '!' => {
155 space = 2;
156 }
157 _ => {
158 space = 1;
159 }
160 }
161 }
162 out.write_all(b"\n").unwrap();
163 }
164
165 pub fn print_p(&self, out: &mut dyn std::io::Write, para: &str) {
166 let words = wordify(para);
167 self.print_words(out, &words);
168 }
169
170 pub fn print_plist<I, S>(&self, out: &mut dyn std::io::Write, parit: I)
171 where
172 I: IntoIterator<Item = S>,
173 S: AsRef<str>
174 {
175 for p in parit {
176 self.print_p(out, p.as_ref());
177 }
178 }
179}
180
181
182#[allow(clippy::similar_names)]
183pub fn print_table(cols: &[Column], body: &Vec<Vec<String>>) {
184 let mut colw: Vec<usize> = cols.iter().map(|col| col.title.len()).collect();
186
187 for row in body {
189 for (i, col) in row.iter().enumerate() {
190 if i == colw.len() {
192 break;
193 }
194 if col.len() > colw[i] {
195 colw[i] = col.len();
196 }
197 }
198 }
199
200 let mut fields = Vec::new();
202 for (i, col) in cols.iter().enumerate() {
203 match col.title_align {
204 Align::Left => {
205 fields.push(format!("{1:<0$}", colw[i], col.title));
206 }
207 Align::Center => {
208 fields.push(format!("{1:^0$}", colw[i], col.title));
209 }
210 Align::Right => {
211 fields.push(format!("{1:>0$}", colw[i], col.title));
212 }
213 }
214 }
215 println!("{}", fields.join(" "));
216
217 fields.clear();
219 for w in &colw {
220 fields.push("~".repeat(*w).to_string());
221 }
222 println!("{}", fields.join(" "));
223
224 for row in body {
225 fields.clear();
226
227 for (i, cell) in row.iter().enumerate() {
228 match cols[i].align {
229 Align::Left => {
230 fields.push(format!("{1:<0$}", colw[i], cell));
231 }
232 Align::Center => {
233 fields.push(format!("{1:^0$}", colw[i], cell));
234 }
235 Align::Right => {
236 fields.push(format!("{1:>0$}", colw[i], cell));
237 }
238 }
239 }
240 println!("{}", fields.join(" "));
241 }
242}
243
244