1use std::fmt::{self, Display, Formatter};
5
6use reifydb_type::{Value, util::unicode::UnicodeWidthStr};
7
8use crate::domain::frame::{Frame, FrameColumn};
9
10fn display_width(s: &str) -> usize {
14 if s.contains('\n') {
15 s.lines().map(|line| line.width()).max().unwrap_or(0)
16 } else {
17 s.width()
18 }
19}
20
21fn escape_control_chars(s: &str) -> String {
24 s.replace('\n', "\\n").replace('\t', "\\t")
25}
26
27fn get_column_display_order(frame: &Frame) -> Vec<usize> {
29 (0..frame.len()).collect()
30}
31
32fn extract_string_value(col: &FrameColumn, row_number: usize) -> String {
34 let s = col.data.get(row_number).unwrap_or(&Value::Undefined).as_string();
35
36 escape_control_chars(&s)
37}
38
39impl Display for Frame {
40 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
41 let row_count = self.first().map_or(0, |c| c.data.len());
42 let has_row_numbers = !self.row_numbers.is_empty();
43 let col_count = self.len()
44 + if has_row_numbers {
45 1
46 } else {
47 0
48 };
49
50 let column_order = get_column_display_order(self);
52
53 let mut col_widths = vec![0; col_count];
54
55 let row_num_col_idx = if has_row_numbers {
57 let row_num_header = "__ROW__NUMBER__";
59 col_widths[0] = display_width(row_num_header);
60
61 for row_num in &self.row_numbers {
63 let s = row_num.to_string();
64 col_widths[0] = col_widths[0].max(display_width(&s));
65 }
66 1 } else {
68 0 };
70
71 for (display_idx, &col_idx) in column_order.iter().enumerate() {
72 let col = &self[col_idx];
73 let display_name = escape_control_chars(&col.qualified_name());
74 col_widths[row_num_col_idx + display_idx] = display_width(&display_name);
75 }
76
77 for row_numberx in 0..row_count {
78 for (display_idx, &col_idx) in column_order.iter().enumerate() {
79 let col = &self[col_idx];
80 let s = extract_string_value(col, row_numberx);
81 col_widths[row_num_col_idx + display_idx] =
82 col_widths[row_num_col_idx + display_idx].max(display_width(&s));
83 }
84 }
85
86 for w in &mut col_widths {
88 *w += 2;
89 }
90
91 let sep = format!("+{}+", col_widths.iter().map(|w| "-".repeat(*w + 2)).collect::<Vec<_>>().join("+"));
92 writeln!(f, "{}", sep)?;
93
94 let mut header = Vec::new();
95
96 if has_row_numbers {
98 let w = col_widths[0];
99 let name = "__ROW__NUMBER__";
100 let pad = w - display_width(name);
101 let l = pad / 2;
102 let r = pad - l;
103 header.push(format!(" {:left$}{}{:right$} ", "", name, "", left = l, right = r));
104 }
105
106 for (display_idx, &col_idx) in column_order.iter().enumerate() {
108 let col = &self[col_idx];
109 let w = col_widths[row_num_col_idx + display_idx];
110 let name = escape_control_chars(&col.qualified_name());
111 let pad = w - display_width(&name);
112 let l = pad / 2;
113 let r = pad - l;
114 header.push(format!(" {:left$}{}{:right$} ", "", name, "", left = l, right = r));
115 }
116
117 writeln!(f, "|{}|", header.join("|"))?;
118
119 writeln!(f, "{}", sep)?;
120
121 for row_numberx in 0..row_count {
122 let mut row = Vec::new();
123
124 if has_row_numbers {
126 let w = col_widths[0];
127 let s = if row_numberx < self.row_numbers.len() {
128 self.row_numbers[row_numberx].to_string()
129 } else {
130 "Undefined".to_string()
131 };
132 let pad = w - display_width(&s);
133 let l = pad / 2;
134 let r = pad - l;
135 row.push(format!(" {:left$}{}{:right$} ", "", s, "", left = l, right = r));
136 }
137
138 for (display_idx, &col_idx) in column_order.iter().enumerate() {
140 let col = &self[col_idx];
141 let w = col_widths[row_num_col_idx + display_idx];
142 let s = extract_string_value(col, row_numberx);
143 let pad = w - display_width(&s);
144 let l = pad / 2;
145 let r = pad - l;
146 row.push(format!(" {:left$}{}{:right$} ", "", s, "", left = l, right = r));
147 }
148
149 writeln!(f, "|{}|", row.join("|"))?;
150 }
151
152 writeln!(f, "{}", sep)
153 }
154}
155
156#[cfg(test)]
157mod tests {
158 use std::convert::TryFrom;
159
160 use reifydb_type::{
161 Blob, Date, DateTime, Duration, OrderedF32, OrderedF64, RowNumber, Time, Type, Uuid4, Uuid7, Value,
162 parse_uuid4, parse_uuid7,
163 };
164
165 use super::*;
166
167 macro_rules! column_with_undefineds {
169 ($name:expr, Bool, $data:expr) => {{
170 let result_data: Vec<Value> =
171 $data.into_iter().map(|opt| opt.map_or(Value::Undefined, Value::Boolean)).collect();
172
173 FrameColumn {
174 namespace: None,
175 store: None,
176 name: $name.to_string(),
177 r#type: Type::Boolean,
178 data: result_data,
179 }
180 }};
181 ($name:expr, Float4, $data:expr) => {{
182 let result_data: Vec<Value> = $data
183 .into_iter()
184 .map(|opt| {
185 opt.map_or(Value::Undefined, |v| {
186 Value::Float4(OrderedF32::try_from(v).unwrap())
187 })
188 })
189 .collect();
190
191 FrameColumn {
192 namespace: None,
193 store: None,
194 name: $name.to_string(),
195 r#type: Type::Float4,
196 data: result_data,
197 }
198 }};
199 ($name:expr, Float8, $data:expr) => {{
200 let result_data: Vec<Value> = $data
201 .into_iter()
202 .map(|opt| {
203 opt.map_or(Value::Undefined, |v| {
204 Value::Float8(OrderedF64::try_from(v).unwrap())
205 })
206 })
207 .collect();
208
209 FrameColumn {
210 namespace: None,
211 store: None,
212 name: $name.to_string(),
213 r#type: Type::Float8,
214 data: result_data,
215 }
216 }};
217 ($name:expr, Utf8, $data:expr) => {{
218 let result_data: Vec<Value> = $data
219 .into_iter()
220 .map(|opt| opt.map_or(Value::Undefined, |v| Value::Utf8(v.to_string())))
221 .collect();
222
223 FrameColumn {
224 namespace: None,
225 store: None,
226 name: $name.to_string(),
227 r#type: Type::Utf8,
228 data: result_data,
229 }
230 }};
231 ($name:expr, RowNumber, $data:expr) => {{
232 let result_data: Vec<Value> =
233 $data.into_iter().map(|opt| opt.map_or(Value::Undefined, Value::RowNumber)).collect();
234
235 FrameColumn {
236 namespace: None,
237 store: None,
238 name: "__ROW__NUMBER__".to_string(),
239 r#type: Type::RowNumber,
240 data: result_data,
241 }
242 }};
243 ($name:expr, $type:ident, $data:expr) => {{
244 let result_data: Vec<Value> =
245 $data.into_iter().map(|opt| opt.map_or(Value::Undefined, Value::$type)).collect();
246
247 FrameColumn {
248 namespace: None,
249 store: None,
250 name: $name.to_string(),
251 r#type: Type::$type,
252 data: result_data,
253 }
254 }};
255 }
256
257 fn undefined_column(name: &str, count: usize) -> FrameColumn {
258 FrameColumn {
259 namespace: None,
260 store: None,
261 name: name.to_string(),
262 r#type: Type::Undefined,
263 data: vec![Value::Undefined; count],
264 }
265 }
266
267 #[test]
268 fn test_bool() {
269 let frame = Frame::new(vec![], vec![column_with_undefineds!("bool", Bool, [Some(true), None])]);
270 let output = format!("{}", frame);
271 let expected = "\
272+-------------+
273| bool |
274+-------------+
275| true |
276| Undefined |
277+-------------+
278";
279 assert_eq!(output, expected);
280 }
281
282 #[test]
283 fn test_float4() {
284 let frame = Frame::new(vec![], vec![column_with_undefineds!("float4", Float4, [Some(1.2_f32), None])]);
285 let output = format!("{}", frame);
286 let expected = "\
287+-------------+
288| float4 |
289+-------------+
290| 1.2 |
291| Undefined |
292+-------------+
293";
294 assert_eq!(output, expected);
295 }
296
297 #[test]
298 #[allow(clippy::approx_constant)]
299 fn test_float8() {
300 let frame = Frame::new(vec![], vec![column_with_undefineds!("float8", Float8, [Some(3.14_f64), None])]);
301 let output = format!("{}", frame);
302 let expected = "\
303+-------------+
304| float8 |
305+-------------+
306| 3.14 |
307| Undefined |
308+-------------+
309";
310 assert_eq!(output, expected);
311 }
312
313 #[test]
314 fn test_int1() {
315 let frame = Frame::new(vec![], vec![column_with_undefineds!("int1", Int1, [Some(1_i8), None])]);
316 let output = format!("{}", frame);
317 let expected = "\
318+-------------+
319| int1 |
320+-------------+
321| 1 |
322| Undefined |
323+-------------+
324";
325 assert_eq!(output, expected);
326 }
327
328 #[test]
329 fn test_int2() {
330 let frame = Frame::new(vec![], vec![column_with_undefineds!("int2", Int2, [Some(100_i16), None])]);
331 let output = format!("{}", frame);
332 let expected = "\
333+-------------+
334| int2 |
335+-------------+
336| 100 |
337| Undefined |
338+-------------+
339";
340 assert_eq!(output, expected);
341 }
342
343 #[test]
344 fn test_int4() {
345 let frame = Frame::new(vec![], vec![column_with_undefineds!("int4", Int4, [Some(1000_i32), None])]);
346 let output = format!("{}", frame);
347 let expected = "\
348+-------------+
349| int4 |
350+-------------+
351| 1000 |
352| Undefined |
353+-------------+
354";
355 assert_eq!(output, expected);
356 }
357
358 #[test]
359 fn test_int8() {
360 let frame = Frame::new(vec![], vec![column_with_undefineds!("int8", Int8, [Some(10000_i64), None])]);
361 let output = format!("{}", frame);
362 let expected = "\
363+-------------+
364| int8 |
365+-------------+
366| 10000 |
367| Undefined |
368+-------------+
369";
370 assert_eq!(output, expected);
371 }
372
373 #[test]
374 fn test_int16() {
375 let frame =
376 Frame::new(vec![], vec![column_with_undefineds!("int16", Int16, [Some(100000_i128), None])]);
377 let output = format!("{}", frame);
378 let expected = "\
379+-------------+
380| int16 |
381+-------------+
382| 100000 |
383| Undefined |
384+-------------+
385";
386 assert_eq!(output, expected);
387 }
388
389 #[test]
390 fn test_uint1() {
391 let frame = Frame::new(vec![], vec![column_with_undefineds!("uint1", Uint1, [Some(1_u8), None])]);
392 let output = format!("{}", frame);
393 let expected = "\
394+-------------+
395| uint1 |
396+-------------+
397| 1 |
398| Undefined |
399+-------------+
400";
401 assert_eq!(output, expected);
402 }
403
404 #[test]
405 fn test_uint2() {
406 let frame = Frame::new(vec![], vec![column_with_undefineds!("uint2", Uint2, [Some(100_u16), None])]);
407 let output = format!("{}", frame);
408 let expected = "\
409+-------------+
410| uint2 |
411+-------------+
412| 100 |
413| Undefined |
414+-------------+
415";
416 assert_eq!(output, expected);
417 }
418
419 #[test]
420 fn test_uint4() {
421 let frame = Frame::new(vec![], vec![column_with_undefineds!("uint4", Uint4, [Some(1000_u32), None])]);
422 let output = format!("{}", frame);
423 let expected = "\
424+-------------+
425| uint4 |
426+-------------+
427| 1000 |
428| Undefined |
429+-------------+
430";
431 assert_eq!(output, expected);
432 }
433
434 #[test]
435 fn test_uint8() {
436 let frame = Frame::new(vec![], vec![column_with_undefineds!("uint8", Uint8, [Some(10000_u64), None])]);
437 let output = format!("{}", frame);
438 let expected = "\
439+-------------+
440| uint8 |
441+-------------+
442| 10000 |
443| Undefined |
444+-------------+
445";
446 assert_eq!(output, expected);
447 }
448
449 #[test]
450 fn test_uint16() {
451 let frame =
452 Frame::new(vec![], vec![column_with_undefineds!("uint16", Uint16, [Some(100000_u128), None])]);
453 let output = format!("{}", frame);
454 let expected = "\
455+-------------+
456| uint16 |
457+-------------+
458| 100000 |
459| Undefined |
460+-------------+
461";
462 assert_eq!(output, expected);
463 }
464
465 #[test]
466 fn test_string() {
467 let frame = Frame::new(vec![], vec![column_with_undefineds!("string", Utf8, [Some("foo"), None])]);
468 let output = format!("{}", frame);
469 let expected = "\
470+-------------+
471| string |
472+-------------+
473| foo |
474| Undefined |
475+-------------+
476";
477 assert_eq!(output, expected);
478 }
479
480 #[test]
481 fn test_undefined() {
482 let frame = Frame::new(vec![], vec![undefined_column("undefined", 2)]);
483 let output = format!("{}", frame);
484 let expected = "\
485+-------------+
486| undefined |
487+-------------+
488| Undefined |
489| Undefined |
490+-------------+
491";
492 assert_eq!(output, expected);
493 }
494
495 #[test]
496 fn test_date() {
497 let frame = Frame::new(
498 vec![],
499 vec![column_with_undefineds!("date", Date, [Some(Date::from_ymd(2025, 1, 15).unwrap()), None])],
500 );
501 let output = format!("{}", frame);
502 let expected = "\
503+--------------+
504| date |
505+--------------+
506| 2025-01-15 |
507| Undefined |
508+--------------+
509";
510 assert_eq!(output, expected);
511 }
512
513 #[test]
514 fn test_datetime() {
515 let frame = Frame::new(
516 vec![],
517 vec![column_with_undefineds!(
518 "datetime",
519 DateTime,
520 [Some(DateTime::from_timestamp(1642694400).unwrap()), None]
521 )],
522 );
523 let output = format!("{}", frame);
524 let expected = "\
525+----------------------------------+
526| datetime |
527+----------------------------------+
528| 2022-01-20T16:00:00.000000000Z |
529| Undefined |
530+----------------------------------+
531";
532 assert_eq!(output, expected);
533 }
534
535 #[test]
536 fn test_time() {
537 let frame = Frame::new(
538 vec![],
539 vec![column_with_undefineds!("time", Time, [Some(Time::from_hms(14, 30, 45).unwrap()), None])],
540 );
541 let output = format!("{}", frame);
542 let expected = "\
543+----------------------+
544| time |
545+----------------------+
546| 14:30:45.000000000 |
547| Undefined |
548+----------------------+
549";
550 assert_eq!(output, expected);
551 }
552
553 #[test]
554 fn test_interval() {
555 let frame = Frame::new(
556 vec![],
557 vec![column_with_undefineds!("interval", Duration, [Some(Duration::from_days(30)), None])],
558 );
559 let output = format!("{}", frame);
560
561 let expected = "\
562+-------------+
563| interval |
564+-------------+
565| P30D |
566| Undefined |
567+-------------+
568";
569 assert_eq!(output, expected);
570 }
571
572 #[test]
573 fn test_row_number() {
574 let frame = Frame::new(
575 vec![],
576 vec![column_with_undefineds!("__ROW__NUMBER__", RowNumber, [Some(RowNumber(1234)), None])],
577 );
578 let output = format!("{}", frame);
579 let expected = "\
580+-------------------+
581| __ROW__NUMBER__ |
582+-------------------+
583| 1234 |
584| Undefined |
585+-------------------+
586";
587 assert_eq!(output, expected);
588 }
589
590 #[test]
591 fn test_row_number_display() {
592 let regular_column = column_with_undefineds!("name", Utf8, [Some("Alice"), Some("Bob")]);
594
595 let age_column = column_with_undefineds!("age", Int4, [Some(25_i32), Some(30_i32)]);
596
597 let frame = Frame::new(vec![1, 2], vec![regular_column, age_column]);
599 let output = format!("{}", frame);
600
601 let lines: Vec<&str> = output.lines().collect();
603 let header_line = lines[1]; assert!(header_line.starts_with("| __ROW__NUMBER__"));
606
607 let first_data_line = lines[3]; assert!(first_data_line.contains("| 1 |")); }
611
612 #[test]
613 fn test_row_number_undefined_display() {
614 let row_number_column = column_with_undefineds!(
616 "__ROW__NUMBER__",
617 RowNumber,
618 [Some(RowNumber::new(1)), None] );
621
622 let frame = Frame::new(vec![], vec![row_number_column]);
623 let output = format!("{}", frame);
624
625 let lines: Vec<&str> = output.lines().collect();
627 let first_data_line = lines[3]; let second_data_line = lines[4]; assert!(first_data_line.contains("1")); assert!(second_data_line.contains("Undefined")); }
634
635 #[test]
636 fn test_blob() {
637 let frame = Frame::new(
638 vec![],
639 vec![column_with_undefineds!("blob", Blob, [Some(Blob::new(vec![0x01, 0x02, 0x03])), None])],
640 );
641 let output = format!("{}", frame);
642 let expected = "\
643+-------------+
644| blob |
645+-------------+
646| 0x010203 |
647| Undefined |
648+-------------+
649";
650 assert_eq!(output, expected);
651 }
652
653 #[test]
654 fn test_uuid4() {
655 let frame = Frame::new(
656 vec![],
657 vec![column_with_undefineds!(
658 "uuid4",
659 Uuid4,
660 [Some(Uuid4::from(parse_uuid4("550e8400-e29b-41d4-a716-446655440000").unwrap())), None]
661 )],
662 );
663 let output = format!("{}", frame);
664 let expected = "\
665+----------------------------------------+
666| uuid4 |
667+----------------------------------------+
668| 550e8400-e29b-41d4-a716-446655440000 |
669| Undefined |
670+----------------------------------------+
671";
672 assert_eq!(output, expected);
673 }
674
675 #[test]
676 fn test_uuid7() {
677 let frame = Frame::new(
678 vec![],
679 vec![column_with_undefineds!(
680 "uuid7",
681 Uuid7,
682 [Some(Uuid7::from(parse_uuid7("01890a5d-ac96-774b-b9aa-789c0686aaa4").unwrap())), None]
683 )],
684 );
685 let output = format!("{}", frame);
686 let expected = "\
687+----------------------------------------+
688| uuid7 |
689+----------------------------------------+
690| 01890a5d-ac96-774b-b9aa-789c0686aaa4 |
691| Undefined |
692+----------------------------------------+
693";
694 assert_eq!(output, expected);
695 }
696}