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
use colored::*;
use std::cmp;
use term_size;
pub(crate) const CRATE_LIST_HEADING_COLOR: &'static str = "blue";
pub(crate) const SECTION_HEADING_COLOR: &'static str = "yellow";
pub(crate) const ENUM_HEADING_COLOR: &'static str = "green";
const SPACER: &'static str = " ";
const DEFAULT_TERM_WIDTH: usize = 90;
pub(crate) fn header(s: &str, color: &str) -> String {
format!("{} {}", "::".color(color).bold(), s.bold())
}
pub(crate) fn pprint_as_columns(elems: Vec<String>) -> String {
let col_width = elems.iter().map(String::len).max().unwrap();
let per_row = max_width() / (col_width + 1);
elems
.chunks(per_row)
.map(|chunk| {
chunk
.iter()
.map(|c| format!("{:01$}", c, col_width))
.collect::<Vec<String>>()
.join(" ")
})
.collect::<Vec<String>>()
.join("\n")
}
fn max_width() -> usize {
if let Some((w, _)) = term_size::dimensions() {
w
} else {
DEFAULT_TERM_WIDTH
}
}
struct Row {
cells: Vec<String>,
}
impl Row {
fn formatted(&self, column_widths: &Vec<usize>) -> String {
self.cells
.iter()
.zip(column_widths)
.map(|e| format!("{:01$}", e.0, e.1))
.collect::<Vec<String>>()
.join(SPACER)
}
fn two_column_wrapped(&self, column_widths: &Vec<usize>) -> String {
if self.cells.len() != 2 {
panic!(
"two_column_wrapped called on row with {} columns",
self.cells.len()
);
}
let max = max_width();
if column_widths.iter().sum::<usize>() + SPACER.len() <= max {
return self.formatted(&column_widths);
}
let mut buf = String::new();
let mut current = String::new();
let offset = vec![" "; column_widths[0] + SPACER.len() + 1].join("");
current.push_str(&(format!("{:01$}", self.cells[0], column_widths[0]) + SPACER));
for word in self.cells[1].split_whitespace() {
if current.len() + word.len() + 1 < max {
current.push_str(&format!(" {}", word));
} else {
buf.push_str(&format!("{}\n", current));
current.clear();
current.push_str(&format!("{}{}", offset, word));
}
}
buf.push_str(¤t);
return buf;
}
}
pub(crate) struct Table {
rows: Vec<Row>,
column_widths: Vec<usize>,
}
impl Table {
pub fn new() -> Self {
Table {
rows: vec![],
column_widths: vec![],
}
}
pub fn from_rows(rows: Vec<Vec<String>>) -> Self {
let mut t = Table::new();
rows.iter().for_each(|r| t.add_row(r.clone()));
return t;
}
pub fn add_row(&mut self, cells: Vec<String>) {
let diff = cells.len() - self.column_widths.len();
if diff > 0 {
self.column_widths.extend(vec![0; diff])
}
cells.iter().enumerate().for_each(|(i, cell)| {
self.column_widths[i] = cmp::max(cell.len(), self.column_widths[i])
});
self.rows.push(Row { cells });
}
pub fn as_string(&self) -> String {
self.rows
.iter()
.map(|r| r.two_column_wrapped(&self.column_widths))
.collect::<Vec<String>>()
.join("\n")
}
}