1use std::{
13 cmp,
14 fmt::{Display, Write},
15 io,
16};
17
18const CORNER_STR: &'static str = "+";
19const HORIZ_BORDER_STR: &'static str = "-";
20const VERT_BORDER_STR: &'static str = "|";
21const SPACE_STR: &'static str = " ";
22const NEW_LINE_STR: &'static str = "\n";
23
24pub fn render<W, T, R, C>(writer: &mut W, data: T) -> io::Result<()>
33where
34 W: io::Write,
35 T: AsRef<[R]>,
36 R: AsRef<[C]>,
37 C: Display,
38{
39 let widths = widths(&data);
40 let data = data.as_ref();
41
42 render_border_line(writer, &widths)?;
43 for row in data.iter() {
44 let row = row.as_ref();
45 render_text_line(writer, &widths, row)?;
46 render_border_line(writer, &widths)?;
47 }
48
49 Ok(())
50}
51
52fn widths<T, R, C>(data: T) -> Vec<usize>
57where
58 T: AsRef<[R]>,
59 R: AsRef<[C]>,
60 C: Display,
61{
62 let mut string_buf = String::new();
64 let data = data.as_ref();
65 if data.len() == 0 {
67 return vec![];
68 }
69 let row_len = data[0].as_ref().len();
71 let mut widths = vec![0; row_len];
72 for row in data.iter() {
73 let row = row.as_ref();
74 if row_len != row.len() {
75 panic!("rows must be the same length");
77 }
78 for (idx, cell) in row.iter().enumerate() {
79 string_buf.clear();
80 write!(string_buf, "{}", cell).unwrap(); widths[idx] = cmp::max(widths[idx], string_buf.len());
82 }
83 }
84 widths
85}
86
87fn render_border_line<W: io::Write>(writer: &mut W, lengths: &[usize]) -> io::Result<()> {
89 if lengths.len() == 0 || lengths[0] == 0 {
90 return Ok(());
91 }
92 write!(writer, "{}", CORNER_STR)?;
93 for len in lengths {
94 for _ in 0..(*len + 2) {
95 write!(writer, "{}", HORIZ_BORDER_STR)?;
96 }
97 write!(writer, "{}", CORNER_STR)?;
98 }
99 write!(writer, "\n")
100}
101
102fn render_text_line<W, C>(writer: &mut W, lengths: &[usize], row: &[C]) -> io::Result<()>
104where
105 W: io::Write,
106 C: Display,
107{
108 if lengths.len() == 0 || lengths[0] == 0 {
109 return Ok(());
110 }
111 let mut string_buf = String::new();
112 write!(writer, "{}", VERT_BORDER_STR)?;
113 for (cell, len) in row.iter().zip(lengths.iter()) {
114 string_buf.clear();
115 write!(string_buf, "{}", cell).unwrap(); let extra = len - string_buf.len();
117 write!(writer, "{}{}", SPACE_STR, string_buf)?;
118 for _ in 0..extra + 1 {
119 write!(writer, "{}", SPACE_STR)?;
120 }
121 write!(writer, "{}", VERT_BORDER_STR)?;
122 }
123 write!(writer, "{}", NEW_LINE_STR)?;
124
125 Ok(())
126}
127
128#[cfg(test)]
129mod tests {
130
131 #[test]
132 fn render() {
133 let tables = vec![
134 (vec![], &b""[..]),
135 (vec![vec![]], &b""[..]),
136 (
137 vec![vec!["single", "line", "a"], vec!["second", "lines", "a"]],
138 &b"\
139+--------+-------+---+
140| single | line | a |
141+--------+-------+---+
142| second | lines | a |
143+--------+-------+---+
144"[..],
145 ),
146 ];
147 for (table, result) in tables {
148 let mut out = Vec::new();
149 super::render(&mut out, &table).unwrap();
150 assert_eq!(out, &result[..], "{:#?}", table);
151 }
152 }
153}