summaryrefslogtreecommitdiffstats
path: root/src/row.rs
blob: b4f7d7b771d0947ab1f46df1b18cfdab27682d45 (plain)
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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
//! This module contains definition of table rows stuff
use std::io::{Write, Error};
use std::iter::FromIterator;
use std::ops::{Index, IndexMut};

use term::Terminal;

use super::utils::NEWLINE;
use super::cell::Cell;
use super::format::{TableFormat, ColumnPosition};

/// Represent a table row made of cells
#[derive(Clone, Debug)]
pub struct Row {
	cells: Vec<Cell>
}

impl Row {
	/// Create a new `Row` backed with `cells` vector
	pub fn new(cells: Vec<Cell>) -> Row {
		return Row {
			cells: cells
		};
	}

	/// Create an row of length `size`, with empty strings stored
	pub fn empty() -> Row {
		return Self::new(vec![Cell::default(); 0]);
	}

	/// Get the number of cells in this row
	pub fn len(&self) -> usize {
		return self.cells.len();
	}

	/// Get the height of this row
	pub fn get_height(&self) -> usize {
		let mut height = 1; // Minimum height must be 1 to print empty rows
		for cell in &self.cells {
			let h = cell.get_height();
			if h > height {
				height = h;
			}
		}
		return height;
	}

	/// Get the minimum width required by the cell in the column `column`.
	/// Return 0 if the cell does not exist in this row
	pub fn get_cell_width(&self, column: usize) -> usize {
		return match self.cells.get(column) {
			Some(cell) => cell.get_width(),
			None => 0
		}
	}

	/// Get the cell at index `idx`
	pub fn get_cell(&self, idx: usize) -> Option<&Cell> {
		return self.cells.get(idx);
	}

	/// Get the mutable cell at index `idx`
	pub fn get_mut_cell(&mut self, idx: usize) -> Option<&mut Cell> {
		return self.cells.get_mut(idx);
	}

	/// Set the `cell` in the row at the given `column`
	pub fn set_cell(&mut self, cell: Cell, column: usize) -> Result<(), &str> {
		if column >= self.len() {
			return Err("Cannot find cell");
		}
		self.cells[column] = cell;
		return Ok(());
	}

	/// Append a `cell` at the end of the row
	pub fn add_cell(&mut self, cell: Cell) {
		self.cells.push(cell);
	}

	/// Insert `cell` at position `index`. If `index` is higher than the row lenght,
	/// the cell will be appended at the end
	pub fn insert_cell(&mut self, index: usize, cell: Cell) {
		if index < self.cells.len() {
			self.cells.insert(index, cell);
		} else {
			self.add_cell(cell);
		}
	}

	/// Remove the cell at position `index`. Silently skip if this cell does not exist
	pub fn remove_cell(&mut self, index: usize) {
		if index < self.cells.len() {
			self.cells.remove(index);
		}
	}

	/// Internal only
	fn __print<T:Write+?Sized, F>(&self, out: &mut T, format: &TableFormat, col_width: &[usize], f: F) -> Result<(), Error>
		where F: Fn(&Cell, &mut T, usize, usize, bool) -> Result<(), Error>
	{
		for i in 0..self.get_height() {
			try!(format.print_column_separator(out, ColumnPosition::Left));
			let (lp, rp) = format.get_padding();
			for j in 0..col_width.len() {
				try!(out.write(&vec![' ' as u8; lp]));
				let skip_r_fill = (j == col_width.len() - 1) && format.get_column_separator(ColumnPosition::Right).is_none();
				match self.get_cell(j) {
					Some(ref c) => try!(f(c, out, i, col_width[j], skip_r_fill)),
					None => try!(f(&Cell::default(), out, i, col_width[j], skip_r_fill))
				};
				try!(out.write(&vec![' ' as u8; rp]));
				if j < col_width.len() - 1 {
					try!(format.print_column_separator(out, ColumnPosition::Intern));
				}
			}
			try!(format.print_column_separator(out, ColumnPosition::Right));
			try!(out.write_all(NEWLINE));
		}
		return Ok(());
	}

	/// Print the row to `out`, with `separator` as column separator, and `col_width`
	/// specifying the width of each columns
	pub fn print<T: Write+?Sized>(&self, out: &mut T, format: &TableFormat, col_width: &[usize]) -> Result<(), Error> {
		return self.__print(out, format, col_width, Cell::print);
	}

	/// Print the row to terminal `out`, with `separator` as column separator, and `col_width`
	/// specifying the width of each columns. Apply style when needed
	pub fn print_term<T: Terminal+?Sized>(&self, out: &mut T, format: &TableFormat, col_width: &[usize]) -> Result<(), Error> {
		return self.__print(out, format, col_width, Cell::print_term);
	}
}

impl Default for Row {
	fn default() -> Row {
		return Row::empty();
	}
}

impl Index<usize> for Row {
	type Output = Cell;
	fn index(&self, idx: usize) -> &Self::Output {
		return &self.cells[idx];
	}
}

impl IndexMut<usize> for Row {
	fn index_mut(&mut self, idx: usize) -> &mut Self::Output {
		return &mut self.cells[idx];
	}
}

impl <A: ToString> FromIterator<A> for Row {
	fn from_iter<T>(iterator: T) -> Row where T: IntoIterator<Item=A> {
		return Self::new(iterator.into_iter().map(|ref e| Cell::from(e)).collect());
	}
}

impl <T, A> From<T> for Row where A: ToString, T : IntoIterator<Item=A> {
	fn from(it: T) -> Row {
		return Self::from_iter(it);
	}
}

/// This macro simplifies `Row` creation
///
/// The syntax support style spec
/// # Example
/// ```
/// # #[macro_use] extern crate prettytable;
/// # fn main() {
/// // Create a normal row
/// let row1 = row!["Element 1", "Element 2", "Element 3"];
/// // Create a row with all cells formatted with red foreground color, yellow background color
/// // bold, italic, align in the center of the cell
/// let row2 = row![FrBybic => "Element 1", "Element 2", "Element 3"];
/// // Create a row with first cell in blue, second one in red, and last one with default style
/// let row3 = row![Fb->"blue", Fr->"red", "normal"];
/// // Do something with rows
/// # drop(row1);
/// # drop(row2);
/// # drop(row3);
/// # }
/// ```
///
/// For details about style specifier syntax, check doc for [Cell::style_spec](cell/struct.Cell.html#method.style_spec) method
#[macro_export]
macro_rules! row {
	(($($out:tt)*); $value:expr) => (vec![$($out)* cell!($value)]);
	(($($out:tt)*); $value:expr, $($n:tt)*) => (row!(($($out)* cell!($value),); $($n)*));
	(($($out:tt)*); $style:ident -> $value:expr) => (vec![$($out)* cell!($style -> $value)]);
	(($($out:tt)*); $style:ident -> $value:expr, $($n: tt)*) => (row!(($($out)* cell!($style -> $value),); $($n)*));

	($($content:expr), *) => ($crate::row::Row::new(vec![$(cell!($content)), *]));
	($style:ident => $($content:expr), *) => ($crate::row::Row::new(vec![$(cell!($style -> $content)), *]));
	($($content:tt)*) => ($crate::row::Row::new(row!((); $($content)*)));
}