reifydb_type/value/frame/
frame.rs

1// Copyright (c) reifydb.com 2025
2// This file is licensed under the MIT, see license.md file
3
4use std::{
5	fmt::{self, Display, Formatter},
6	ops::{Deref, Index},
7};
8
9use serde::{Deserialize, Serialize};
10
11use super::FrameColumn;
12use crate::{RowNumber, util::unicode::UnicodeWidthStr};
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct Frame {
16	pub row_numbers: Vec<RowNumber>,
17	pub columns: Vec<FrameColumn>,
18}
19
20impl Deref for Frame {
21	type Target = [FrameColumn];
22
23	fn deref(&self) -> &Self::Target {
24		&self.columns
25	}
26}
27
28impl Index<usize> for Frame {
29	type Output = FrameColumn;
30
31	fn index(&self, index: usize) -> &Self::Output {
32		self.columns.index(index)
33	}
34}
35
36fn escape_control_chars(s: &str) -> String {
37	s.replace('\n', "\\n").replace('\t', "\\t")
38}
39
40impl Frame {
41	pub fn new(columns: Vec<FrameColumn>) -> Self {
42		Self {
43			row_numbers: Vec::new(),
44			columns,
45		}
46	}
47
48	pub fn with_row_numbers(columns: Vec<FrameColumn>, row_numbers: Vec<RowNumber>) -> Self {
49		Self {
50			row_numbers,
51			columns,
52		}
53	}
54}
55
56impl Display for Frame {
57	fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
58		let row_count = self.first().map_or(0, |c| c.data.len());
59		let has_row_numbers = !self.row_numbers.is_empty();
60
61		// Calculate column widths
62		let mut col_widths: Vec<usize> = Vec::new();
63
64		// Row number column width
65		if has_row_numbers {
66			let header_width = "rownum".width();
67			let max_val_width = self.row_numbers.iter().map(|rn| rn.to_string().width()).max().unwrap_or(0);
68			col_widths.push(header_width.max(max_val_width));
69		}
70
71		// Regular column widths
72		for col in &self.columns {
73			let header_width = escape_control_chars(&col.qualified_name()).width();
74			let mut max_val_width = 0;
75			for i in 0..col.data.len() {
76				max_val_width = max_val_width.max(escape_control_chars(&col.data.as_string(i)).width());
77			}
78			col_widths.push(header_width.max(max_val_width));
79		}
80
81		// Add padding
82		for w in &mut col_widths {
83			*w += 2;
84		}
85
86		// Build separator
87		let sep: String = if col_widths.is_empty() {
88			"++".to_string()
89		} else {
90			col_widths.iter().map(|w| format!("+{}", "-".repeat(*w + 2))).collect::<String>() + "+"
91		};
92
93		writeln!(f, "{}", sep)?;
94
95		// Build header
96		let mut header_parts = Vec::new();
97		let mut col_idx = 0;
98		if has_row_numbers {
99			let name = "rownum";
100			let w = col_widths[col_idx];
101			let pad = w - name.width();
102			let l = pad / 2;
103			let r = pad - l;
104			header_parts.push(format!(" {:l$}{}{:r$} ", "", name, ""));
105			col_idx += 1;
106		}
107		for col in &self.columns {
108			let name = escape_control_chars(&col.qualified_name());
109			let w = col_widths[col_idx];
110			let pad = w - name.width();
111			let l = pad / 2;
112			let r = pad - l;
113			header_parts.push(format!(" {:l$}{}{:r$} ", "", name, ""));
114			col_idx += 1;
115		}
116		writeln!(f, "|{}|", header_parts.join("|"))?;
117		writeln!(f, "{}", sep)?;
118
119		// Build rows
120		for row_idx in 0..row_count {
121			let mut row_parts = Vec::new();
122			let mut col_idx = 0;
123			if has_row_numbers {
124				let w = col_widths[col_idx];
125				let val = if row_idx < self.row_numbers.len() {
126					self.row_numbers[row_idx].to_string()
127				} else {
128					"Undefined".to_string()
129				};
130				let pad = w - val.width();
131				let l = pad / 2;
132				let r = pad - l;
133				row_parts.push(format!(" {:l$}{}{:r$} ", "", val, ""));
134				col_idx += 1;
135			}
136			for col in &self.columns {
137				let w = col_widths[col_idx];
138				let val = escape_control_chars(&col.data.as_string(row_idx));
139				let pad = w - val.width();
140				let l = pad / 2;
141				let r = pad - l;
142				row_parts.push(format!(" {:l$}{}{:r$} ", "", val, ""));
143				col_idx += 1;
144			}
145			writeln!(f, "|{}|", row_parts.join("|"))?;
146		}
147
148		writeln!(f, "{}", sep)
149	}
150}