Skip to main content

kwik/table/
row.rs

1/*
2 * Copyright (c) Kia Shakiba
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8use std::{fmt::Display, io::Write};
9
10use crate::{
11	file::csv::{RowData, WriteRow},
12	table::cell::{Align, Cell, Style},
13};
14
15#[derive(Default)]
16pub struct Row {
17	cells:   Vec<Cell>,
18	max_len: usize,
19}
20
21#[derive(PartialEq)]
22pub enum ColumnJoinType {
23	Normal,
24	Spaced,
25	Plus,
26}
27
28impl Row {
29	/// Returns `true` if there are no columns in the row.
30	///
31	/// # Examples
32	/// ```
33	/// use kwik::table::{Row, Align, Style};
34	///
35	/// let mut row = Row::default();
36	///
37	/// assert!(row.is_empty());
38	///
39	/// row = row.push("Row 1", Align::Left, Style::Normal);
40	///
41	/// assert!(!row.is_empty());
42	/// ```
43	#[inline]
44	#[must_use]
45	pub fn is_empty(&self) -> bool {
46		self.cells.is_empty()
47	}
48
49	/// Returns the number of columns in the row.
50	///
51	/// # Examples
52	/// ```
53	/// use kwik::table::{Row, Align, Style};
54	///
55	/// let mut row = Row::default()
56	///     .push("Row 1", Align::Left, Style::Normal);
57	///
58	/// assert_eq!(row.len(), 1);
59	/// ```
60	#[inline]
61	#[must_use]
62	pub fn len(&self) -> usize {
63		self.cells.len()
64	}
65
66	/// Adds a new column to the end of the row.
67	///
68	/// # Examples
69	/// ```
70	/// use kwik::table::{Row, Align, Style};
71	///
72	/// let mut row = Row::default()
73	///     .push("Row 1", Align::Left, Style::Normal);
74	/// ```
75	#[inline]
76	#[must_use]
77	pub fn push<T>(mut self, value: T, align: Align, style: Style) -> Self
78	where
79		T: Display,
80	{
81		let value = value.to_string();
82		let len = value.len();
83		let cell = Cell::new(value, align, style);
84
85		if len > self.max_len {
86			self.max_len = len;
87		}
88
89		self.cells.push(cell);
90		self
91	}
92
93	/// Adds a blank column to the end of the row.
94	///
95	/// # Examples
96	/// ```
97	/// use kwik::table::{Row, Align, Style};
98	///
99	/// let mut row = Row::default()
100	///     .blank();
101	/// ```
102	#[inline]
103	#[must_use]
104	pub fn blank(self) -> Self {
105		self.push("", Align::Left, Style::Normal)
106	}
107
108	/// Returns the printed size of the row.
109	#[inline]
110	#[must_use]
111	pub fn size(&self) -> usize {
112		self.to_string(None, ColumnJoinType::Spaced).len()
113	}
114
115	/// Returns the printed size of the column at the supplied index.
116	///
117	/// # Panics
118	///
119	/// Panics if the column index is out of the bounds of the columns.
120	#[inline]
121	#[must_use]
122	pub fn get_column_size(&self, index: usize) -> usize {
123		assert!(index < self.cells.len(), "Invalid column index.");
124		self.cells[index].size()
125	}
126
127	/// Prints the column to the supplied stream.
128	#[inline]
129	pub fn print(&self, stdout: &mut impl Write, sizes: &Vec<usize>, join_type: ColumnJoinType) {
130		writeln!(stdout, "{}", self.to_string(Some(sizes), join_type)).unwrap();
131	}
132
133	/// Returns the string value of the row.
134	#[must_use]
135	fn to_string(&self, sizes: Option<&Vec<usize>>, join_type: ColumnJoinType) -> String {
136		let join_str = match join_type {
137			ColumnJoinType::Normal => "|",
138			ColumnJoinType::Spaced => " | ",
139			ColumnJoinType::Plus => "+",
140		};
141
142		let line = self
143			.cells
144			.iter()
145			.enumerate()
146			.map(|(index, cell)| {
147				let size = match sizes {
148					Some(sizes) => sizes[index],
149					None => cell.size(),
150				};
151
152				cell.to_sized_string(size)
153			})
154			.collect::<Vec<String>>()
155			.join(join_str);
156
157		if join_type == ColumnJoinType::Spaced {
158			format!("| {line} |")
159		} else {
160			format!("|{line}|")
161		}
162	}
163}
164
165impl WriteRow for Row {
166	fn as_row(&self, row_data: &mut RowData) {
167		for cell in &self.cells {
168			row_data.push(cell.value());
169		}
170	}
171}