prettytable/format.rs
1//! Define table formatting utilities
2
3use std::io::{Write, Error};
4
5use encode_unicode::Utf8Char;
6
7use super::utils::NEWLINE;
8
9/// Alignment for cell's content
10#[derive(Clone, Debug, PartialEq, Copy, Hash, Eq)]
11pub enum Alignment {
12 /// Align left
13 LEFT,
14 /// Align in the center
15 CENTER,
16 /// Align right
17 RIGHT,
18}
19
20/// Position of a line separator in a table
21#[derive(Clone, Debug, PartialEq, Copy, Hash, Eq)]
22pub enum LinePosition {
23 /// Table's border on top
24 Top,
25 /// Line separator between the titles row,
26 /// and the first data row
27 Title,
28 /// Line separator between data rows
29 Intern,
30 /// Bottom table's border
31 Bottom,
32}
33
34/// Position of a column separator in a row
35#[derive(Clone, Debug, PartialEq, Copy, Hash, Eq)]
36pub enum ColumnPosition {
37 /// Left table's border
38 Left,
39 /// Internal column separators
40 Intern,
41 /// Rigth table's border
42 Right,
43}
44
45/// Contains the character used for printing a line separator
46#[derive(Clone, Debug, Copy, Hash, PartialEq, Eq)]
47pub struct LineSeparator {
48 /// Line separator
49 line: char,
50 /// Internal junction separator
51 junc: char,
52 /// Left junction separator
53 ljunc: char,
54 /// Right junction separator
55 rjunc: char,
56}
57
58impl LineSeparator {
59 /// Create a new line separator instance where `line` is the character used to separate 2 lines
60 /// and `junc` is the one used for junctions between columns and lines
61 pub fn new(line: char, junc: char, ljunc: char, rjunc: char) -> LineSeparator {
62 LineSeparator {
63 line: line,
64 junc: junc,
65 ljunc: ljunc,
66 rjunc: rjunc,
67 }
68 }
69
70 /// Print a full line separator to `out`. `col_width` is a slice containing the width of each column.
71 /// Returns the number of printed lines
72 fn print<T: Write + ?Sized>(&self,
73 out: &mut T,
74 col_width: &[usize],
75 padding: (usize, usize),
76 colsep: bool,
77 lborder: bool,
78 rborder: bool)
79 -> Result<usize, Error> {
80 if lborder {
81 out.write_all(Utf8Char::from(self.ljunc).as_bytes())?;
82 }
83 let mut iter = col_width.into_iter().peekable();
84 while let Some(width) = iter.next() {
85 for _ in 0..width + padding.0 + padding.1 {
86 out.write_all(Utf8Char::from(self.line).as_bytes())?;
87 }
88 if colsep && iter.peek().is_some() {
89 out.write_all(Utf8Char::from(self.junc).as_bytes())?;
90 }
91 }
92 if rborder {
93 out.write_all(Utf8Char::from(self.rjunc).as_bytes())?;
94 }
95 out.write_all(NEWLINE)?;
96 Ok(1)
97 }
98}
99
100impl Default for LineSeparator {
101 fn default() -> Self {
102 LineSeparator::new('-', '+', '+', '+')
103 }
104}
105
106/// Contains the table formatting rules
107#[derive(Clone, Debug, Copy, Hash, PartialEq, Eq)]
108pub struct TableFormat {
109 /// Optional column separator character
110 csep: Option<char>,
111 /// Optional left border character
112 lborder: Option<char>,
113 /// Optional right border character
114 rborder: Option<char>,
115 /// Optional internal line separator
116 lsep: Option<LineSeparator>,
117 /// Optional title line separator
118 tsep: Option<LineSeparator>,
119 /// Optional top line separator
120 top_sep: Option<LineSeparator>,
121 /// Optional bottom line separator
122 bottom_sep: Option<LineSeparator>,
123 /// Left padding
124 pad_left: usize,
125 /// Right padding
126 pad_right: usize,
127 /// Global indentation when rendering the table
128 indent: usize,
129}
130
131impl TableFormat {
132 /// Create a new empty TableFormat.
133 pub fn new() -> TableFormat {
134 TableFormat {
135 csep: None,
136 lborder: None,
137 rborder: None,
138 lsep: None,
139 tsep: None,
140 top_sep: None,
141 bottom_sep: None,
142 pad_left: 0,
143 pad_right: 0,
144 indent: 0,
145 }
146 }
147
148 /// Return a tuple with left and right padding
149 pub fn get_padding(&self) -> (usize, usize) {
150 (self.pad_left, self.pad_right)
151 }
152
153 /// Set left and right padding
154 pub fn padding(&mut self, left: usize, right: usize) {
155 self.pad_left = left;
156 self.pad_right = right;
157 }
158
159 /// Set the character used for internal column separation
160 pub fn column_separator(&mut self, separator: char) {
161 self.csep = Some(separator);
162 }
163
164 /// Set the character used for table borders
165 pub fn borders(&mut self, border: char) {
166 self.lborder = Some(border);
167 self.rborder = Some(border);
168 }
169
170 /// Set the character used for left table border
171 pub fn left_border(&mut self, border: char) {
172 self.lborder = Some(border);
173 }
174
175 /// Set the character used for right table border
176 pub fn right_border(&mut self, border: char) {
177 self.rborder = Some(border);
178 }
179
180 /// Set a line separator
181 pub fn separator(&mut self, what: LinePosition, separator: LineSeparator) {
182 *match what {
183 LinePosition::Top => &mut self.top_sep,
184 LinePosition::Bottom => &mut self.bottom_sep,
185 LinePosition::Title => &mut self.tsep,
186 LinePosition::Intern => &mut self.lsep,
187 } = Some(separator);
188 }
189
190 /// Set format for multiple kind of line separator
191 pub fn separators(&mut self, what: &[LinePosition], separator: LineSeparator) {
192 for pos in what {
193 self.separator(*pos, separator);
194 }
195 }
196
197 fn get_sep_for_line(&self, pos: LinePosition) -> &Option<LineSeparator> {
198 match pos {
199 LinePosition::Intern => &self.lsep,
200 LinePosition::Top => &self.top_sep,
201 LinePosition::Bottom => &self.bottom_sep,
202 LinePosition::Title => {
203 match &self.tsep {
204 s @ &Some(_) => s,
205 &None => &self.lsep,
206 }
207 }
208 }
209 }
210
211 /// Set global indentation in spaces used when rendering a table
212 pub fn indent(&mut self, spaces: usize) {
213 self.indent = spaces;
214 }
215
216 /// Get global indentation in spaces used when rendering a table
217 pub fn get_indent(&self) -> usize {
218 self.indent
219 }
220
221 /// Print a full line separator to `out`. `col_width` is a slice containing the width of each column.
222 /// Returns the number of printed lines
223 #[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_line_separator<T: Write + ?Sized>(&self,
225 out: &mut T,
226 col_width: &[usize],
227 pos: LinePosition)
228 -> Result<usize, Error> {
229 match *self.get_sep_for_line(pos) {
230 Some(ref l) => {
231 //TODO: Wrap this into dedicated function one day
232 out.write_all(&vec![b' '; self.get_indent()])?;
233 l.print(out,
234 col_width,
235 self.get_padding(),
236 self.csep.is_some(),
237 self.lborder.is_some(),
238 self.rborder.is_some())
239 }
240 None => Ok(0),
241 }
242 }
243
244 /// Returns the character used to separate columns.
245 /// `pos` specify if the separator is left/right final or internal to the table
246 pub fn get_column_separator(&self, pos: ColumnPosition) -> Option<char> {
247 match pos {
248 ColumnPosition::Left => self.lborder,
249 ColumnPosition::Intern => self.csep,
250 ColumnPosition::Right => self.rborder,
251 }
252 }
253
254 /// Print a column separator or a table border
255 #[deprecated(since="0.8.0", note="Will become private in future release. See [issue #87](https://github.com/phsym/prettytable-rs/issues/87)")]
256 pub fn print_column_separator<T: Write + ?Sized>(&self,
257 out: &mut T,
258 pos: ColumnPosition)
259 -> Result<(), Error> {
260 match self.get_column_separator(pos) {
261 Some(s) => out.write_all(Utf8Char::from(s).as_bytes()),
262 None => Ok(()),
263 }
264 }
265}
266
267impl Default for TableFormat {
268 fn default() -> Self {
269 TableFormat::new()
270 }
271}
272
273/// A builder to create a `TableFormat`
274pub struct FormatBuilder {
275 format: Box<TableFormat>,
276}
277
278impl FormatBuilder {
279 /// Creates a new builder
280 pub fn new() -> FormatBuilder {
281 FormatBuilder { format: Box::new(TableFormat::new()) }
282 }
283
284 /// Set left and right padding
285 pub fn padding(mut self, left: usize, right: usize) -> Self {
286 self.format.padding(left, right);
287 self
288 }
289
290 /// Set the character used for internal column separation
291 pub fn column_separator(mut self, separator: char) -> Self {
292 self.format.column_separator(separator);
293 self
294 }
295
296 /// Set the character used for table borders
297 pub fn borders(mut self, border: char) -> Self {
298 self.format.borders(border);
299 self
300 }
301
302 /// Set the character used for left table border
303 pub fn left_border(mut self, border: char) -> Self {
304 self.format.left_border(border);
305 self
306 }
307
308 /// Set the character used for right table border
309 pub fn right_border(mut self, border: char) -> Self {
310 self.format.right_border(border);
311 self
312 }
313
314 /// Set a line separator format
315 pub fn separator(mut self, what: LinePosition, separator: LineSeparator) -> Self {
316 self.format.separator(what, separator);
317 self
318 }
319
320 /// Set separator format for multiple kind of line separators
321 pub fn separators(mut self, what: &[LinePosition], separator: LineSeparator) -> Self {
322 self.format.separators(what, separator);
323 self
324 }
325
326 /// Set global indentation in spaces used when rendering a table
327 pub fn indent(mut self, spaces: usize) -> Self {
328 self.format.indent(spaces);
329 self
330 }
331
332 /// Return the generated `TableFormat`
333 pub fn build(&self) -> TableFormat {
334 *self.format
335 }
336}
337
338impl Into<TableFormat> for FormatBuilder {
339 fn into(self) -> TableFormat {
340 *self.format
341 }
342}
343
344impl From<TableFormat> for FormatBuilder {
345 fn from(fmt: TableFormat) -> Self {
346 FormatBuilder { format: Box::new(fmt) }
347 }
348}
349
350/// Predifined formats. Those constants are lazily evaluated when
351/// the corresponding struct is dereferenced
352pub mod consts {
353 use super::{TableFormat, LineSeparator, FormatBuilder, LinePosition};
354
355 lazy_static! {
356 /// A line separator made of `-` and `+`
357 static ref MINUS_PLUS_SEP: LineSeparator = LineSeparator::new('-', '+', '+', '+');
358 /// A line separator made of `=` and `+`
359 static ref EQU_PLUS_SEP: LineSeparator = LineSeparator::new('=', '+', '+', '+');
360
361 /// Default table format
362 ///
363 /// # Example
364 /// ```text
365 /// +----+----+
366 /// | T1 | T2 |
367 /// +====+====+
368 /// | a | b |
369 /// +----+----+
370 /// | d | c |
371 /// +----+----+
372 /// ```
373 pub static ref FORMAT_DEFAULT: TableFormat = FormatBuilder::new()
374 .column_separator('|')
375 .borders('|')
376 .separator(LinePosition::Intern, *MINUS_PLUS_SEP)
377 .separator(LinePosition::Title, *EQU_PLUS_SEP)
378 .separator(LinePosition::Bottom, *MINUS_PLUS_SEP)
379 .separator(LinePosition::Top, *MINUS_PLUS_SEP)
380 .padding(1, 1)
381 .build();
382
383 /// Similar to `FORMAT_DEFAULT` but without special separator after title line
384 ///
385 /// # Example
386 /// ```text
387 /// +----+----+
388 /// | T1 | T2 |
389 /// +----+----+
390 /// | a | b |
391 /// +----+----+
392 /// | c | d |
393 /// +----+----+
394 /// ```
395 pub static ref FORMAT_NO_TITLE: TableFormat = FormatBuilder::new()
396 .column_separator('|')
397 .borders('|')
398 .separator(LinePosition::Intern, *MINUS_PLUS_SEP)
399 .separator(LinePosition::Title, *MINUS_PLUS_SEP)
400 .separator(LinePosition::Bottom, *MINUS_PLUS_SEP)
401 .separator(LinePosition::Top, *MINUS_PLUS_SEP)
402 .padding(1, 1)
403 .build();
404
405 /// With no line separator, but with title separator
406 ///
407 /// # Example
408 /// ```text
409 /// +----+----+
410 /// | T1 | T2 |
411 /// +----+----+
412 /// | a | b |
413 /// | c | d |
414 /// +----+----+
415 /// ```
416 pub static ref FORMAT_NO_LINESEP_WITH_TITLE: TableFormat = FormatBuilder::new()
417 .column_separator('|')
418 .borders('|')
419 .separator(LinePosition::Title, *MINUS_PLUS_SEP)
420 .separator(LinePosition::Bottom, *MINUS_PLUS_SEP)
421 .separator(LinePosition::Top, *MINUS_PLUS_SEP)
422 .padding(1, 1)
423 .build();
424
425 /// With no line or title separator
426 ///
427 /// # Example
428 /// ```text
429 /// +----+----+
430 /// | T1 | T2 |
431 /// | a | b |
432 /// | c | d |
433 /// +----+----+
434 /// ```
435 pub static ref FORMAT_NO_LINESEP: TableFormat = FormatBuilder::new()
436 .column_separator('|')
437 .borders('|')
438 .separator(LinePosition::Bottom, *MINUS_PLUS_SEP)
439 .separator(LinePosition::Top, *MINUS_PLUS_SEP)
440 .padding(1, 1)
441 .build();
442
443 /// No column separator
444 ///
445 /// # Example
446 /// ```text
447 /// --------
448 /// T1 T2
449 /// ========
450 /// a b
451 /// --------
452 /// d c
453 /// --------
454 /// ```
455 pub static ref FORMAT_NO_COLSEP: TableFormat = FormatBuilder::new()
456 .separator(LinePosition::Intern, *MINUS_PLUS_SEP)
457 .separator(LinePosition::Title, *EQU_PLUS_SEP)
458 .separator(LinePosition::Bottom, *MINUS_PLUS_SEP)
459 .separator(LinePosition::Top, *MINUS_PLUS_SEP)
460 .padding(1, 1)
461 .build();
462
463 /// Format for printing a table without any separators (only alignment)
464 ///
465 /// # Example
466 /// ```text
467 /// T1 T2
468 /// a b
469 /// d c
470 /// ```
471 pub static ref FORMAT_CLEAN: TableFormat = FormatBuilder::new()
472 .padding(1, 1)
473 .build();
474
475 /// Format for a table with only external borders and title separator
476 ///
477 /// # Example
478 /// ```text
479 /// +--------+
480 /// | T1 T2 |
481 /// +========+
482 /// | a b |
483 /// | c d |
484 /// +--------+
485 /// ```
486 pub static ref FORMAT_BORDERS_ONLY: TableFormat = FormatBuilder::new()
487 .padding(1, 1)
488 .separator(LinePosition::Title, *EQU_PLUS_SEP)
489 .separator(LinePosition::Bottom, *MINUS_PLUS_SEP)
490 .separator(LinePosition::Top, *MINUS_PLUS_SEP)
491 .borders('|')
492 .build();
493
494 /// A table with no external border
495 ///
496 /// # Example
497 /// ```text
498 /// T1 | T2
499 /// ====+====
500 /// a | b
501 /// ----+----
502 /// c | d
503 /// ```
504 pub static ref FORMAT_NO_BORDER: TableFormat = FormatBuilder::new()
505 .padding(1, 1)
506 .separator(LinePosition::Intern, *MINUS_PLUS_SEP)
507 .separator(LinePosition::Title, *EQU_PLUS_SEP)
508 .column_separator('|')
509 .build();
510
511 /// A table with no external border and no line separation
512 ///
513 /// # Example
514 /// ```text
515 /// T1 | T2
516 /// ----+----
517 /// a | b
518 /// c | d
519 /// ```
520 pub static ref FORMAT_NO_BORDER_LINE_SEPARATOR: TableFormat = FormatBuilder::new()
521 .padding(1, 1)
522 .separator(LinePosition::Title, *MINUS_PLUS_SEP)
523 .column_separator('|')
524 .build();
525
526 /// A table with borders and delimiters made with box characters
527 ///
528 /// # Example
529 /// ```text
530 /// ┌────┬────┬────┐
531 /// │ t1 │ t2 │ t3 │
532 /// ├────┼────┼────┤
533 /// │ 1 │ 1 │ 1 │
534 /// ├────┼────┼────┤
535 /// │ 2 │ 2 │ 2 │
536 /// └────┴────┴────┘
537 /// ```
538 pub static ref FORMAT_BOX_CHARS: TableFormat = FormatBuilder::new()
539 .column_separator('│')
540 .borders('│')
541 .separators(&[LinePosition::Top],
542 LineSeparator::new('─',
543 '┬',
544 '┌',
545 '┐'))
546 .separators(&[LinePosition::Intern],
547 LineSeparator::new('─',
548 '┼',
549 '├',
550 '┤'))
551 .separators(&[LinePosition::Bottom],
552 LineSeparator::new('─',
553 '┴',
554 '└',
555 '┘'))
556 .padding(1, 1)
557 .build();
558 }
559}