query_forge/output/formats/
text.rs1use crate::{QueryResult, QueryValue};
2use crate::value::display_value;
3
4pub fn write_text<W: std::io::Write + ?Sized>(
5 result: &QueryResult,
6 writer: &mut W,
7) -> std::io::Result<()> {
8 if result.columns.is_empty() {
9 return Ok(());
10 }
11
12 let mut column_widths = result
13 .columns
14 .iter()
15 .map(|column| column.len())
16 .collect::<Vec<_>>();
17
18 for row in &result.rows {
19 for index in 0..result.columns.len() {
20 let width = row.get(index).map(display_width).unwrap_or_default();
21 column_widths[index] = column_widths[index].max(width);
22 }
23 }
24
25 write_aligned_row(writer, &result.columns, &column_widths)?;
26 writer.write_all(b"\n")?;
27
28 for (index, width) in column_widths.iter().enumerate() {
29 if index > 0 {
30 writer.write_all(b"-+-")?;
31 }
32 writer.write_all("-".repeat(*width).as_bytes())?;
33 }
34
35 for row in &result.rows {
36 writer.write_all(b"\n")?;
37 write_aligned_result_row(writer, row, result.columns.len(), &column_widths)?;
38 }
39
40 Ok(())
41}
42
43fn display_width(value: &QueryValue) -> usize {
44 match value {
45 QueryValue::Null => 0,
46 QueryValue::Integer(value) => value.to_string().len(),
47 QueryValue::Real(value) => value.to_string().len(),
48 QueryValue::Text(value) => value.len(),
49 }
50}
51
52pub fn render_text(result: &QueryResult) -> String {
53 let mut output = Vec::new();
54 write_text(result, &mut output).expect("writing to memory cannot fail");
55 String::from_utf8(output).expect("table output should always be valid UTF-8")
56}
57
58fn write_aligned_row<W: std::io::Write + ?Sized>(
59 output: &mut W,
60 values: &[String],
61 column_widths: &[usize],
62) -> std::io::Result<()> {
63 for (index, value) in values.iter().enumerate() {
64 if index > 0 {
65 output.write_all(b" | ")?;
66 }
67
68 write!(output, "{value:<width$}", width = column_widths[index])?;
69 }
70
71 Ok(())
72}
73
74fn write_aligned_result_row<W: std::io::Write + ?Sized>(
75 output: &mut W,
76 row: &[crate::QueryValue],
77 column_count: usize,
78 column_widths: &[usize],
79) -> std::io::Result<()> {
80 for (index, width) in column_widths.iter().enumerate().take(column_count) {
81 if index > 0 {
82 output.write_all(b" | ")?;
83 }
84
85 let rendered_value = row.get(index).map(display_value).unwrap_or_default();
86 write!(output, "{rendered_value:<width$}")?;
87 }
88
89 Ok(())
90}