1pub mod style;
30
31use std::io;
32use std::fmt;
33use std::io::Write;
34use std::fmt::Display;
35use std::error::Error;
36use std::cell::RefCell;
37
38use crate::style::StyleOpt;
39use crate::style::stylize;
40
41pub struct GridPrinter {
71 rows: usize,
72 cols: usize,
73 max_widths: RefCell<Vec<usize>>,
74 col_spacing: usize,
75 col_styles: Option<Vec<Option<StyleOpt>>>,
76}
77
78impl GridPrinter {
79 pub fn new(rows: usize, cols: usize) -> Self {
80 Self {
81 rows,
82 cols,
83 ..GridPrinterBuilder::new(rows, cols).build()
84 }
85 }
86
87 pub fn builder(rows: usize, cols: usize) -> GridPrinterBuilder {
88 GridPrinterBuilder::new(rows, cols)
89 }
90
91 fn pad(n: usize) -> String {
92 vec![' '; n].into_iter().collect()
93 }
94
95 #[allow(clippy::print_with_newline)]
96 pub fn print_cell(&self, cell: &str, col_idx: usize, style_opt: Option<&StyleOpt>) {
97
98 let mut s = cell.to_string();
99 if let Some(style_opt) = style_opt {
100 s = stylize(cell, style_opt);
101 }
102 let col_width = self.max_widths.borrow()[col_idx];
103 let pad = GridPrinter::pad(col_width - cell.len() + self.col_spacing);
104 print!("{}{}", s, pad);
105 }
106
107 #[allow(clippy::print_with_newline)]
108 pub fn print<F: Display>(&self, source: &[Vec<F>]) {
109 let mut buff: Vec<String> = Vec::new();
110
111 for i in 0..self.rows {
112 let row = source.get(i);
113 for j in 0..self.cols {
114 let cell = match row {
115 None => "".to_string(),
116 Some(row) => match row.get(j) {
117 None => "".to_string(),
118 Some(el) => format!("{}", el),
119 }
120 };
121 let len = cell.len();
122 if len > self.max_widths.borrow()[j] {
123 self.max_widths.borrow_mut()[j] = len;
124 }
125 buff.push(cell);
127 }
128 }
129
130
131 for (i, cell) in buff.iter().enumerate() {
132 let col_idx = i % self.cols;
133 let _row_idx = i / self.rows;
134
135 let style_opt = match self.col_styles.as_ref() {
136 None => None,
137 Some(col_styles) => match col_styles.get(col_idx) {
138 None => None,
139 Some(style_opt) => style_opt.as_ref(),
140 }
141 };
142
143 self.print_cell(cell, col_idx, style_opt);
144
145 if (i + 1) % self.cols == 0 {
146 print!("\n");
147 io::stdout().flush().unwrap();
148 }
149 }
150
151
152 }
153}
154
155#[derive(Debug)]
167pub struct GridPrinterBuilder {
168 rows: usize,
169 cols: usize,
170 col_spacing: usize,
171 col_styles: Option<Vec<Option<StyleOpt>>>,
172}
173
174impl Default for GridPrinterBuilder {
175 fn default() -> Self {
176 Self {
177 rows: 1,
178 cols: 1,
179 col_spacing: 2,
180 col_styles: None,
181 }
182 }
183}
184
185impl GridPrinterBuilder {
186
187 pub fn new(rows: usize, cols: usize) -> Self {
188 GridPrinterBuilder {
189 rows,
190 cols,
191 ..Default::default()
192 }
193 }
194
195 pub fn col_spacing(mut self, col_spacing: usize) -> Self {
196 self.col_spacing = col_spacing;
197
198 self
199 }
200
201 pub fn col_styles(mut self, col_styles: Vec<Option<StyleOpt>>) -> Result<Self, GridPrinterErr> {
202 match col_styles.len() == self.cols {
203 false => Err(GridPrinterErr::DimensionErr),
204 true => {
205 self.col_styles = Some(col_styles);
206
207 Ok(self)
208 }
209 }
210 }
211
212 pub fn col_style(mut self, idx: usize, opt: StyleOpt) -> Result<Self, GridPrinterErr> {
213 if idx >= self.cols {
217 return Err(GridPrinterErr::DimensionErr);
218 }
219
220 let col_styles = self.col_styles.get_or_insert(vec![None; self.cols]);
221 let col_style = col_styles.get_mut(idx)
222 .ok_or(GridPrinterErr::DimensionErr)?;
223 *col_style = Some(opt);
224
225 Ok(self)
226 }
227
228 pub fn build(self) -> GridPrinter {
229 GridPrinter {
230 rows: self.rows,
231 cols: self.cols,
232 max_widths: RefCell::new(vec![0; self.cols]),
233 col_spacing: self.col_spacing,
234 col_styles: self.col_styles,
235 }
236 }
237
238}
239
240#[derive(Debug)]
241pub enum GridPrinterErr {
242 DimensionErr,
243}
244
245impl Display for GridPrinterErr {
246 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
247 match self {
248 GridPrinterErr::DimensionErr => {
249 write!(f, "DimensionErr. Caused by mismatch in dimension size between method calls.")
250 },
251 }
252 }
253}
254
255impl Error for GridPrinterErr {}
256
257
258#[cfg(test)]
259mod tests {
260
261 use super::*;
262
263 #[test]
264 fn test_2d_arr() {
265 let v = vec![
266 vec![1, 20, 3, ],
267 vec![40, 5, 6, ],
268 vec![7, 800, 9, ],
269 ];
270
271 let rows = v.len();
272 let cols = v[0].len();
273 let printer = GridPrinterBuilder::new(rows, cols)
274 .col_spacing(20)
275 .build();
276 printer.print(&v);
277 }
278
279}