1 | use std::iter::FromIterator; |
2 | |
3 | use crate::{grid::records::vec_records::CellInfo, Table}; |
4 | |
5 | use super::IndexBuilder; |
6 | |
7 | /// Builder creates a [`Table`] from dynamic data set. |
8 | /// |
9 | /// It useful when the amount of columns or rows is not known statically. |
10 | /// |
11 | /// ```rust |
12 | /// use tabled::builder::Builder; |
13 | /// |
14 | /// let mut builder = Builder::default(); |
15 | /// builder.push_record(["index" , "measure" , "value" ]); |
16 | /// builder.push_record(["0" , "weight" , "0.443" ]); |
17 | /// |
18 | /// let table = builder.build(); |
19 | /// |
20 | /// println!("{}" , table); |
21 | /// ``` |
22 | /// |
23 | /// It may be useful to use [`FromIterator`] for building. |
24 | /// |
25 | /// ```rust |
26 | /// use tabled::builder::Builder; |
27 | /// use std::iter::FromIterator; |
28 | /// |
29 | /// let data = vec![ |
30 | /// ["column1" , "column2" ], |
31 | /// ["data1" , "data2" ], |
32 | /// ["data3" , "data4" ], |
33 | /// ]; |
34 | /// |
35 | /// let table = Builder::from_iter(data).build(); |
36 | /// |
37 | /// println!("{}" , table); |
38 | /// ``` |
39 | #[derive (Debug, Default, Clone)] |
40 | pub struct Builder { |
41 | /// A list of rows. |
42 | data: Vec<Vec<CellInfo<String>>>, |
43 | /// A number of columns. |
44 | count_columns: usize, |
45 | /// A content of cells which are created in case rows has different length. |
46 | empty_text: CellInfo<String>, |
47 | } |
48 | |
49 | impl Builder { |
50 | /// Creates a [`Builder`] instance. |
51 | /// |
52 | /// ``` |
53 | /// use tabled::builder::Builder; |
54 | /// |
55 | /// let builder = Builder::new(); |
56 | /// ``` |
57 | pub fn new() -> Self { |
58 | Self::default() |
59 | } |
60 | |
61 | /// Creates a [`Builder`] instance with a given row capacity. |
62 | /// |
63 | /// ``` |
64 | /// use tabled::builder::Builder; |
65 | /// |
66 | /// let mut builder = Builder::with_capacity(2, 3); |
67 | /// builder.push_record((0..3).map(|i| i.to_string())); |
68 | /// builder.push_record(["i" , "surname" , "lastname" ]); |
69 | /// ``` |
70 | pub fn with_capacity(count_records: usize, count_columns: usize) -> Self { |
71 | let mut builder = Self::new(); |
72 | builder.data = Vec::with_capacity(count_records); |
73 | builder.count_columns = count_columns; |
74 | |
75 | builder |
76 | } |
77 | |
78 | /// Creates a [`Builder`] instance. |
79 | /// |
80 | /// # Safety |
81 | /// |
82 | /// It's marked unsafe to emphasize that you shall make sure that all rows bound to have the same length. |
83 | /// |
84 | /// ``` |
85 | /// use tabled::builder::Builder; |
86 | /// |
87 | /// let data = vec![]; |
88 | /// let builder = Builder::from_vec(data); |
89 | /// ``` |
90 | pub fn from_vec(data: Vec<Vec<CellInfo<String>>>) -> Self { |
91 | let count_columns = if data.is_empty() { 0 } else { data[0].len() }; |
92 | |
93 | Self { |
94 | data, |
95 | count_columns, |
96 | empty_text: CellInfo::default(), |
97 | } |
98 | } |
99 | |
100 | /// Sets a content of cells which are created in case rows has different length. |
101 | /// |
102 | /// |
103 | /// ```rust |
104 | /// use tabled::builder::Builder; |
105 | /// |
106 | /// let mut builder = Builder::default(); |
107 | /// builder.set_empty("undefined" ); |
108 | /// builder.push_record((0..3).map(|i| i.to_string())); |
109 | /// builder.push_record(["i" ]); |
110 | /// ``` |
111 | pub fn set_empty<T>(&mut self, text: T) |
112 | where |
113 | T: Into<String>, |
114 | { |
115 | self.empty_text = CellInfo::new(text.into()); |
116 | } |
117 | |
118 | /// Build creates a [`Table`] instance. |
119 | /// |
120 | /// ```rust |
121 | /// use tabled::builder::Builder; |
122 | /// |
123 | /// let mut builder = Builder::default(); |
124 | /// builder.push_record(["i" , "column1" , "column2" ]); |
125 | /// builder.push_record(["0" , "value1" , "value2" ]); |
126 | /// ``` |
127 | pub fn build(self) -> Table { |
128 | Table::from(self) |
129 | } |
130 | |
131 | /// Add an index to the [`Table`]. |
132 | /// |
133 | /// Default index is a range 0-N where N is amount of records. |
134 | /// |
135 | /// # Example |
136 | /// |
137 | /// ``` |
138 | /// use tabled::Table; |
139 | /// |
140 | /// let table = Table::builder(&["Hello" , "World" , "!" ]).index().build(); |
141 | /// |
142 | /// assert_eq!( |
143 | /// table.to_string(), |
144 | /// "+---+-------+ \n\ |
145 | /// | | &str | \n\ |
146 | /// +---+-------+ \n\ |
147 | /// | 0 | Hello | \n\ |
148 | /// +---+-------+ \n\ |
149 | /// | 1 | World | \n\ |
150 | /// +---+-------+ \n\ |
151 | /// | 2 | ! | \n\ |
152 | /// +---+-------+" |
153 | /// ) |
154 | /// ``` |
155 | pub fn index(self) -> IndexBuilder { |
156 | IndexBuilder::from(self) |
157 | } |
158 | |
159 | /// Adds a row to a [`Table`]. |
160 | /// |
161 | /// ``` |
162 | /// use tabled::builder::Builder; |
163 | /// |
164 | /// let mut builder = Builder::default(); |
165 | /// builder.push_record((0..3).map(|i| i.to_string())); |
166 | /// builder.push_record(["i" , "surname" , "lastname" ]); |
167 | /// ``` |
168 | pub fn push_record<R>(&mut self, record: R) |
169 | where |
170 | R: IntoIterator, |
171 | R::Item: Into<String>, |
172 | { |
173 | let list = create_row(record, self.count_columns, &self.empty_text); |
174 | let list_length = list.len(); |
175 | |
176 | if !is_size_eq(self.count_columns, list_length) { |
177 | let size = list_length - self.count_columns; |
178 | resize_rows(&mut self.data, size, &self.empty_text) |
179 | } |
180 | |
181 | self.count_columns = list_length; |
182 | self.data.push(list); |
183 | } |
184 | |
185 | /// Insert a row into a specific position. |
186 | /// |
187 | /// # Panics |
188 | /// |
189 | /// Panics if `index > count_rows`. |
190 | pub fn insert_record<R>(&mut self, index: usize, record: R) |
191 | where |
192 | R: IntoIterator, |
193 | R::Item: Into<String>, |
194 | { |
195 | let list = create_row(record, self.count_columns, &self.empty_text); |
196 | let list_length = list.len(); |
197 | |
198 | if !is_size_eq(self.count_columns, list_length) { |
199 | let size = list_length - self.count_columns; |
200 | resize_rows(&mut self.data, size, &self.empty_text) |
201 | } |
202 | |
203 | self.count_columns = list_length; |
204 | self.data.insert(index, list); |
205 | } |
206 | |
207 | /// Clean removes empty columns and rows. |
208 | /// |
209 | /// # Example |
210 | /// |
211 | /// ``` |
212 | /// use tabled::Table; |
213 | /// |
214 | /// let mut builder = Table::builder(&["Hello" , "World" , "" ]); |
215 | /// builder.clean(); |
216 | /// |
217 | /// let table = builder.build(); |
218 | /// |
219 | /// assert_eq!( |
220 | /// table.to_string(), |
221 | /// "+-------+ \n\ |
222 | /// | &str | \n\ |
223 | /// +-------+ \n\ |
224 | /// | Hello | \n\ |
225 | /// +-------+ \n\ |
226 | /// | World | \n\ |
227 | /// +-------+" |
228 | /// ) |
229 | /// ``` |
230 | pub fn clean(&mut self) { |
231 | self.count_columns -= remove_empty_columns(&mut self.data, self.count_columns); |
232 | remove_empty_rows(&mut self.data, self.count_columns); |
233 | } |
234 | |
235 | /// Removes a row with a specific position. |
236 | /// |
237 | /// Index expected to be in range. |
238 | /// `Builder::count_records() < x >= 0` |
239 | /// |
240 | /// # Panics |
241 | /// |
242 | /// Panics if `row_index > count_rows`. |
243 | pub fn remove_record(&mut self, index: usize) { |
244 | let _ = self.data.remove(index); |
245 | } |
246 | |
247 | /// Removes a column with a specific position. |
248 | /// |
249 | /// Index expected to be in range. |
250 | /// `Builder::count_columns() < x >= 0` |
251 | /// |
252 | /// # Panics |
253 | /// |
254 | /// Panics if `index > count_columns`. |
255 | pub fn remove_column(&mut self, index: usize) { |
256 | for row in &mut self.data { |
257 | let _ = row.remove(index); |
258 | } |
259 | |
260 | self.count_columns -= 1; |
261 | } |
262 | |
263 | /// Push a column. |
264 | pub fn push_column<I>(&mut self, column: I) |
265 | where |
266 | I: IntoIterator, |
267 | I::Item: Into<String>, |
268 | { |
269 | let mut iter = column.into_iter(); |
270 | |
271 | for row in self.data.iter_mut() { |
272 | let text = iter |
273 | .next() |
274 | .map(Into::into) |
275 | .map(CellInfo::new) |
276 | .unwrap_or(self.empty_text.clone()); |
277 | |
278 | row.push(text); |
279 | } |
280 | |
281 | for text in iter { |
282 | let text = CellInfo::new(text.into()); |
283 | |
284 | let mut row = Vec::with_capacity(self.count_columns + 1); |
285 | for _ in 0..self.count_columns { |
286 | row.push(self.empty_text.clone()); |
287 | } |
288 | |
289 | row.push(text); |
290 | } |
291 | |
292 | self.count_columns += 1; |
293 | } |
294 | |
295 | /// Insert a column with a specific position. |
296 | /// |
297 | /// In case a column is bigger then the total amount of rows it will be truncated. |
298 | /// |
299 | /// # Panics |
300 | /// |
301 | /// Panics if `index > count_columns`. |
302 | pub fn insert_column<I>(&mut self, index: usize, column: I) |
303 | where |
304 | I: IntoIterator, |
305 | I::Item: Into<String>, |
306 | { |
307 | let mut iter = column.into_iter(); |
308 | |
309 | for row in self.data.iter_mut() { |
310 | let text = iter |
311 | .next() |
312 | .map(Into::into) |
313 | .map(CellInfo::new) |
314 | .unwrap_or(self.empty_text.clone()); |
315 | |
316 | row.insert(index, text); |
317 | } |
318 | |
319 | for text in iter { |
320 | let text = CellInfo::new(text.into()); |
321 | |
322 | let mut row = Vec::with_capacity(self.count_columns + 1); |
323 | for _ in 0..index { |
324 | row.push(self.empty_text.clone()); |
325 | } |
326 | |
327 | row.push(text); |
328 | |
329 | for _ in index..self.count_columns { |
330 | row.push(self.empty_text.clone()); |
331 | } |
332 | } |
333 | |
334 | self.count_columns += 1; |
335 | } |
336 | |
337 | /// Remove all records. |
338 | pub fn clear(&mut self) { |
339 | self.data.clear(); |
340 | self.count_columns = 0; |
341 | } |
342 | |
343 | /// Returns an amount of columns which would be present in a built table. |
344 | pub fn count_columns(&self) -> usize { |
345 | self.count_columns |
346 | } |
347 | |
348 | /// Returns an amount of rows which would be present in a built table. |
349 | /// |
350 | /// Notice that it does not include header if present; |
351 | /// It returns only amount of records. |
352 | pub fn count_records(&self) -> usize { |
353 | self.data.len() |
354 | } |
355 | } |
356 | |
357 | impl From<Builder> for Vec<Vec<String>> { |
358 | fn from(builder: Builder) -> Self { |
359 | builderimpl Iterator- >
|
360 | .data |
361 | .into_iter() |
362 | .map(|row: Vec>| row.into_iter().map(CellInfo::into_inner).collect()) |
363 | .collect() |
364 | } |
365 | } |
366 | |
367 | impl From<Builder> for Vec<Vec<CellInfo<String>>> { |
368 | fn from(builder: Builder) -> Self { |
369 | builder.data |
370 | } |
371 | } |
372 | |
373 | impl<R> FromIterator<R> for Builder |
374 | where |
375 | R: IntoIterator, |
376 | R::Item: Into<String>, |
377 | { |
378 | fn from_iter<T: IntoIterator<Item = R>>(iter: T) -> Self { |
379 | let mut builder: Builder = Self::new(); |
380 | for row in iter { |
381 | builder.push_record(row); |
382 | } |
383 | |
384 | builder |
385 | } |
386 | } |
387 | |
388 | impl<D> Extend<D> for Builder |
389 | where |
390 | D: Into<String>, |
391 | { |
392 | fn extend<T: IntoIterator<Item = D>>(&mut self, iter: T) { |
393 | self.push_record(iter); |
394 | } |
395 | } |
396 | |
397 | impl From<Vec<Vec<String>>> for Builder { |
398 | fn from(data: Vec<Vec<String>>) -> Self { |
399 | let mut data: Vec>> = dataimpl Iterator- >>
|
400 | .into_iter() |
401 | .map(|row: Vec| row.into_iter().map(CellInfo::new).collect()) |
402 | .collect(); |
403 | |
404 | let count_columns: usize = equalize_row_length(&mut data); |
405 | |
406 | Self { |
407 | data, |
408 | count_columns, |
409 | empty_text: CellInfo::default(), |
410 | } |
411 | } |
412 | } |
413 | |
414 | impl From<Vec<Vec<CellInfo<String>>>> for Builder { |
415 | fn from(mut data: Vec<Vec<CellInfo<String>>>) -> Self { |
416 | let count_columns: usize = equalize_row_length(&mut data); |
417 | |
418 | Self { |
419 | data, |
420 | count_columns, |
421 | empty_text: CellInfo::default(), |
422 | } |
423 | } |
424 | } |
425 | |
426 | fn create_row<R>(row: R, size: usize, default: &CellInfo<String>) -> Vec<CellInfo<String>> |
427 | where |
428 | R: IntoIterator, |
429 | R::Item: Into<String>, |
430 | { |
431 | let mut list: Vec> = Vec::with_capacity(size); |
432 | for text: impl Into in row { |
433 | let text: String = text.into(); |
434 | let text: CellInfo = CellInfo::new(text); |
435 | list.push(text); |
436 | } |
437 | |
438 | if list.len() < size { |
439 | for _ in 0..size - list.len() { |
440 | let text: CellInfo = default.clone(); |
441 | list.push(text); |
442 | } |
443 | } |
444 | |
445 | list |
446 | } |
447 | |
448 | fn remove_empty_columns(data: &mut [Vec<CellInfo<String>>], count_columns: usize) -> usize { |
449 | let mut deleted = 0; |
450 | for col in 0..count_columns { |
451 | let col = col - deleted; |
452 | |
453 | let mut is_empty_column = true; |
454 | for row in data.iter() { |
455 | let text = &row[col]; |
456 | if !text.as_ref().is_empty() { |
457 | is_empty_column = false; |
458 | break; |
459 | } |
460 | } |
461 | |
462 | if is_empty_column { |
463 | for row in data.iter_mut() { |
464 | let _ = row.remove(col); |
465 | } |
466 | |
467 | deleted += 1; |
468 | } |
469 | } |
470 | |
471 | deleted |
472 | } |
473 | |
474 | fn remove_empty_rows(data: &mut Vec<Vec<CellInfo<String>>>, count_columns: usize) { |
475 | let mut deleted: usize = 0; |
476 | |
477 | for row: usize in 0..data.len() { |
478 | let row: usize = row - deleted; |
479 | |
480 | let mut is_empty_row: bool = true; |
481 | for col: usize in 0..count_columns { |
482 | let cell: &CellInfo = &data[row][col]; |
483 | if !cell.as_ref().is_empty() { |
484 | is_empty_row = false; |
485 | break; |
486 | } |
487 | } |
488 | |
489 | if is_empty_row { |
490 | let _ = data.remove(index:row); |
491 | deleted += 1; |
492 | } |
493 | } |
494 | } |
495 | |
496 | fn resize_rows(data: &mut Vec<Vec<CellInfo<String>>>, size: usize, empty_text: &CellInfo<String>) { |
497 | for row: &mut Vec> in data { |
498 | append_vec(v:row, value:empty_text.clone(), n:size); |
499 | } |
500 | } |
501 | |
502 | fn append_vec<T>(v: &mut Vec<T>, value: T, n: usize) |
503 | where |
504 | T: Clone, |
505 | { |
506 | for _ in 0..n { |
507 | v.push(value.clone()); |
508 | } |
509 | } |
510 | |
511 | fn is_size_eq(expected: usize, new: usize) -> bool { |
512 | use std::cmp::Ordering; |
513 | |
514 | match new.cmp(&expected) { |
515 | Ordering::Less => { |
516 | unreachable!("must be impossible due to the assumptions/checks we do" ); |
517 | } |
518 | Ordering::Greater => false, |
519 | Ordering::Equal => true, |
520 | } |
521 | } |
522 | |
523 | fn equalize_row_length(data: &mut Vec<Vec<CellInfo<String>>>) -> usize { |
524 | if data.is_empty() { |
525 | return 0; |
526 | } |
527 | |
528 | let first_row_length: usize = data[0].len(); |
529 | let init: (usize, bool) = (first_row_length, true); |
530 | let (count_columns: usize, is_consistent: bool) = data.iter().fold(init, |mut acc: (usize, bool), cur: &Vec>| { |
531 | let length: usize = cur.len(); |
532 | acc.1 = acc.1 && acc.0 == length; |
533 | acc.0 = std::cmp::max(v1:acc.0, v2:length); |
534 | acc |
535 | }); |
536 | |
537 | if !is_consistent { |
538 | let empty_text: CellInfo = CellInfo::default(); |
539 | for row: &mut Vec> in data { |
540 | let size: usize = count_columns - row.len(); |
541 | append_vec(v:row, value:empty_text.clone(), n:size); |
542 | } |
543 | } |
544 | |
545 | count_columns |
546 | } |
547 | |