1use super::format::Alignment;
4use super::utils::display_width;
5use super::utils::print_align;
6use super::{color, Attr, Terminal};
7use std::io::{Error, Write};
8use std::string::ToString;
9use std::str::FromStr;
10
11#[derive(Clone, Debug, Hash, PartialEq, Eq)]
16pub struct Cell {
17 content: Vec<String>,
18 width: usize,
19 align: Alignment,
20 style: Vec<Attr>,
21 hspan: usize,
22}
23
24impl Cell {
25 pub fn new_align(string: &str, align: Alignment) -> Cell {
28 let content: Vec<String> = string.lines().map(|x| x.to_string()).collect();
29 let mut width = 0;
30 for cont in &content {
31 let l = display_width(&cont[..]);
32 if l > width {
33 width = l;
34 }
35 }
36 Cell {
37 content: content,
38 width: width,
39 align: align,
40 style: Vec::new(),
41 hspan: 1,
42 }
43 }
44
45 pub fn new(string: &str) -> Cell {
48 Cell::new_align(string, Alignment::LEFT)
49 }
50
51 pub fn align(&mut self, align: Alignment) {
53 self.align = align;
54 }
55
56 pub fn style(&mut self, attr: Attr) {
58 self.style.push(attr);
59 }
60
61 pub fn with_style(mut self, attr: Attr) -> Cell {
63 self.style(attr);
64 self
65 }
66
67 pub fn with_hspan(mut self, hspan: usize) -> Cell {
69 self.set_hspan(hspan);
70 self
71 }
72
73 pub fn reset_style(&mut self) {
75 self.style.clear();
76 self.align(Alignment::LEFT);
77 }
78
79 pub fn style_spec(mut self, spec: &str) -> Cell {
117 self.reset_style();
118 let mut foreground = false;
119 let mut background = false;
120 let mut it = spec.chars().peekable();
121 while let Some(c) = it.next() {
122 if foreground || background {
123 let color = match c {
124 'r' => color::RED,
125 'R' => color::BRIGHT_RED,
126 'b' => color::BLUE,
127 'B' => color::BRIGHT_BLUE,
128 'g' => color::GREEN,
129 'G' => color::BRIGHT_GREEN,
130 'y' => color::YELLOW,
131 'Y' => color::BRIGHT_YELLOW,
132 'c' => color::CYAN,
133 'C' => color::BRIGHT_CYAN,
134 'm' => color::MAGENTA,
135 'M' => color::BRIGHT_MAGENTA,
136 'w' => color::WHITE,
137 'W' => color::BRIGHT_WHITE,
138 'd' => color::BLACK,
139 'D' => color::BRIGHT_BLACK,
140 _ => {
141 foreground = false;
143 background = false;
144 continue;
145 }
146 };
147 if foreground {
148 self.style(Attr::ForegroundColor(color));
149 } else if background {
150 self.style(Attr::BackgroundColor(color));
151 }
152 foreground = false;
153 background = false;
154 } else {
155 match c {
156 'F' => foreground = true,
157 'B' => background = true,
158 'b' => self.style(Attr::Bold),
159 'i' => self.style(Attr::Italic(true)),
160 'u' => self.style(Attr::Underline(true)),
161 'c' => self.align(Alignment::CENTER),
162 'l' => self.align(Alignment::LEFT),
163 'r' => self.align(Alignment::RIGHT),
164 'H' => {
165 let mut span_s = String::new();
166 while let Some('0'..='9') = it.peek() {
167 span_s.push(it.next().unwrap());
168 }
169 let span = usize::from_str(&span_s).unwrap();
170 self.set_hspan(span);
171 }
172 _ => { }
173 }
174 }
175 }
176 self
177 }
178
179 #[deprecated(since="0.8.0", note="Will become private in future release. See [issue #87](https://github.com/phsym/prettytable-rs/issues/87)")]
181 pub fn get_height(&self) -> usize {
182 self.content.len()
183 }
184
185 #[deprecated(since="0.8.0", note="Will become private in future release. See [issue #87](https://github.com/phsym/prettytable-rs/issues/87)")]
187 pub fn get_width(&self) -> usize {
188 self.width
189 }
190
191 pub fn set_hspan(&mut self, hspan: usize) {
193 self.hspan = if hspan <= 0 {1} else {hspan};
194 }
195
196 pub fn get_hspan(&self) -> usize {
198 self.hspan
199 }
200
201 pub fn get_content(&self) -> String {
203 self.content.join("\n")
204 }
205
206 #[deprecated(since="0.8.0", note="Will become private in future release. See [issue #87](https://github.com/phsym/prettytable-rs/issues/87)")]
211 pub fn print<T: Write + ?Sized>(
212 &self,
213 out: &mut T,
214 idx: usize,
215 col_width: usize,
216 skip_right_fill: bool,
217 ) -> Result<(), Error> {
218 let c = self.content.get(idx).map(|s| s.as_ref()).unwrap_or("");
219 print_align(out, self.align, c, ' ', col_width, skip_right_fill)
220 }
221
222 #[deprecated(since="0.8.0", note="Will become private in future release. See [issue #87](https://github.com/phsym/prettytable-rs/issues/87)")]
224 pub fn print_term<T: Terminal + ?Sized>(
225 &self,
226 out: &mut T,
227 idx: usize,
228 col_width: usize,
229 skip_right_fill: bool,
230 ) -> Result<(), Error> {
231 for a in &self.style {
232 match out.attr(*a) {
233 Ok(..) | Err(::term::Error::NotSupported) | Err(::term::Error::ColorOutOfRange) => {
234 ()
235 } Err(e) => return Err(term_error_to_io_error(e)),
237 };
238 }
239 self.print(out, idx, col_width, skip_right_fill)?;
240 match out.reset() {
241 Ok(..) | Err(::term::Error::NotSupported) | Err(::term::Error::ColorOutOfRange) => {
242 Ok(())
243 }
244 Err(e) => Err(term_error_to_io_error(e)),
245 }
246 }
247}
248
249fn term_error_to_io_error(te: ::term::Error) -> Error {
250 match te {
251 ::term::Error::Io(why) => why,
252 _ => Error::new(::std::io::ErrorKind::Other, te),
253 }
254}
255
256impl<'a, T: ToString> From<&'a T> for Cell {
257 fn from(f: &T) -> Cell {
258 Cell::new(&f.to_string())
259 }
260}
261
262impl ToString for Cell {
263 fn to_string(&self) -> String {
264 self.get_content()
265 }
266}
267
268impl Default for Cell {
269 fn default() -> Cell {
271 Cell {
272 content: vec!["".to_string(); 1],
273 width: 0,
274 align: Alignment::LEFT,
275 style: Vec::new(),
276 hspan: 1,
277 }
278 }
279}
280
281#[macro_export]
309macro_rules! cell {
310 () => {
311 $crate::Cell::default()
312 };
313 ($value:expr) => {
314 $crate::Cell::new(&$value.to_string())
315 };
316 ($style:ident -> $value:expr) => {
317 cell!($value).style_spec(stringify!($style))
318 };
319}
320
321#[cfg(test)]
322mod tests {
323 use Cell;
324 use format::Alignment;
325 use term::{color, Attr};
326 use utils::StringWriter;
327
328 #[test]
329 fn get_content() {
330 let cell = Cell::new("test");
331 assert_eq!(cell.get_content(), "test");
332 }
333
334 #[test]
335 fn print_ascii() {
336 let ascii_cell = Cell::new("hello");
337 assert_eq!(ascii_cell.get_width(), 5);
338
339 let mut out = StringWriter::new();
340 let _ = ascii_cell.print(&mut out, 0, 10, false);
341 assert_eq!(out.as_string(), "hello ");
342 }
343
344 #[test]
345 fn print_unicode() {
346 let unicode_cell = Cell::new("привет");
347 assert_eq!(unicode_cell.get_width(), 6);
348
349 let mut out = StringWriter::new();
350 let _ = unicode_cell.print(&mut out, 0, 10, false);
351 assert_eq!(out.as_string(), "привет ");
352 }
353
354 #[test]
355 fn print_cjk() {
356 let unicode_cell = Cell::new("由系统自动更新");
357 assert_eq!(unicode_cell.get_width(), 14);
358 let mut out = StringWriter::new();
359 let _ = unicode_cell.print(&mut out, 0, 20, false);
360 assert_eq!(out.as_string(), "由系统自动更新 ");
361 }
362
363 #[test]
364 fn align_left() {
365 let cell = Cell::new_align("test", Alignment::LEFT);
366 let mut out = StringWriter::new();
367 let _ = cell.print(&mut out, 0, 10, false);
368 assert_eq!(out.as_string(), "test ");
369 }
370
371 #[test]
372 fn align_center() {
373 let cell = Cell::new_align("test", Alignment::CENTER);
374 let mut out = StringWriter::new();
375 let _ = cell.print(&mut out, 0, 10, false);
376 assert_eq!(out.as_string(), " test ");
377 }
378
379 #[test]
380 fn align_right() {
381 let cell = Cell::new_align("test", Alignment::RIGHT);
382 let mut out = StringWriter::new();
383 let _ = cell.print(&mut out, 0, 10, false);
384 assert_eq!(out.as_string(), " test");
385 }
386
387 #[test]
388 fn style_spec() {
389 let mut cell = Cell::new("test").style_spec("FrBBbuic");
390 assert_eq!(cell.style.len(), 5);
391 assert!(cell.style.contains(&Attr::Underline(true)));
392 assert!(cell.style.contains(&Attr::Italic(true)));
393 assert!(cell.style.contains(&Attr::Bold));
394 assert!(cell.style.contains(&Attr::ForegroundColor(color::RED)));
395 assert!(
396 cell.style
397 .contains(&Attr::BackgroundColor(color::BRIGHT_BLUE))
398 );
399 assert_eq!(cell.align, Alignment::CENTER);
400
401 cell = cell.style_spec("FDBwr");
402 assert_eq!(cell.style.len(), 2);
403 assert!(
404 cell.style
405 .contains(&Attr::ForegroundColor(color::BRIGHT_BLACK))
406 );
407 assert!(cell.style.contains(&Attr::BackgroundColor(color::WHITE)));
408 assert_eq!(cell.align, Alignment::RIGHT);
409
410 cell = cell.clone();
412 cell = cell.style_spec("FzBr");
413 assert!(cell.style.contains(&Attr::BackgroundColor(color::RED)));
414 assert_eq!(cell.style.len(), 1);
415 cell = cell.style_spec("zzz");
416 assert!(cell.style.is_empty());
417 assert_eq!(cell.get_hspan(), 1);
418 cell = cell.style_spec("FDBwH03r");
419 assert_eq!(cell.get_hspan(), 3);
420 }
421
422 #[test]
423 fn reset_style() {
424 let mut cell = Cell::new("test")
425 .with_style(Attr::ForegroundColor(color::BRIGHT_BLACK))
426 .with_style(Attr::BackgroundColor(color::WHITE));
427 cell.align(Alignment::RIGHT);
428
429 assert_eq!(cell.style.len(), 2);
431 assert_eq!(cell.align, Alignment::RIGHT);
432 cell.reset_style();
433 assert_eq!(cell.style.len(), 0);
434 assert_eq!(cell.align, Alignment::LEFT);
435 }
436
437 #[test]
438 fn default_empty_cell() {
439 let cell = Cell::default();
440 assert_eq!(cell.align, Alignment::LEFT);
441 assert!(cell.style.is_empty());
442 assert_eq!(cell.get_content(), "");
443 assert_eq!(cell.to_string(), "");
444 assert_eq!(cell.get_height(), 1);
445 assert_eq!(cell.get_width(), 0);
446 }
447}