Skip to main content

reifydb_value/value/frame/
frame.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4use std::{
5	fmt::{self, Display, Formatter},
6	ops::{Deref, Index},
7};
8
9use serde::{Deserialize, Serialize};
10
11use super::column::FrameColumn;
12use crate::{
13	util::unicode::UnicodeWidthStr,
14	value::{
15		Value,
16		datetime::DateTime,
17		row_number::RowNumber,
18		system_columns::{SystemColumn, SystemColumns},
19	},
20};
21
22#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
23pub struct Frame {
24	pub system: SystemColumns,
25	pub columns: Vec<FrameColumn>,
26}
27
28impl Frame {
29	#[inline]
30	pub fn row_numbers(&self) -> &[RowNumber] {
31		self.system.row_numbers()
32	}
33
34	#[inline]
35	pub fn created_at(&self) -> &[DateTime] {
36		self.system.created_at()
37	}
38
39	#[inline]
40	pub fn updated_at(&self) -> &[DateTime] {
41		self.system.updated_at()
42	}
43
44	#[inline]
45	pub fn time(&self) -> &[DateTime] {
46		self.system.time()
47	}
48}
49
50impl Deref for Frame {
51	type Target = [FrameColumn];
52
53	fn deref(&self) -> &Self::Target {
54		&self.columns
55	}
56}
57
58impl Index<usize> for Frame {
59	type Output = FrameColumn;
60
61	fn index(&self, index: usize) -> &Self::Output {
62		self.columns.index(index)
63	}
64}
65
66fn escape_control_chars(s: &str) -> String {
67	s.replace('\n', "\\n").replace('\t', "\\t")
68}
69
70fn present_system_columns(frame: &Frame) -> Vec<(&'static str, Vec<String>)> {
71	let candidates = [
72		(
73			SystemColumn::RowNumbers.name(),
74			frame.row_numbers().iter().map(|v| v.to_string()).collect::<Vec<_>>(),
75		),
76		(SystemColumn::CreatedAt.name(), frame.created_at().iter().map(|v| v.to_string()).collect()),
77		(SystemColumn::UpdatedAt.name(), frame.updated_at().iter().map(|v| v.to_string()).collect()),
78	];
79	candidates.into_iter().filter(|(_, cells)| !cells.is_empty()).collect()
80}
81
82fn centered(width: usize, content: &str) -> String {
83	let pad = width - content.width();
84	let l = pad / 2;
85	let r = pad - l;
86	format!(" {:l$}{}{:r$} ", "", content, "")
87}
88
89impl Frame {
90	pub fn new(columns: Vec<FrameColumn>) -> Self {
91		Self {
92			system: SystemColumns::empty(),
93			columns,
94		}
95	}
96
97	pub fn with_row_numbers(columns: Vec<FrameColumn>, row_numbers: Vec<RowNumber>) -> Self {
98		Self {
99			system: SystemColumns::new(row_numbers, Vec::new(), Vec::new(), Vec::new(), Vec::new()),
100			columns,
101		}
102	}
103
104	pub fn to_rows(&self) -> Vec<Vec<(String, Value)>> {
105		let row_count = self.first().map_or(0, |c| c.data.len());
106		(0..row_count)
107			.map(|row_idx| {
108				self.columns.iter().map(|col| (col.name.clone(), col.data.get_value(row_idx))).collect()
109			})
110			.collect()
111	}
112}
113
114impl Display for Frame {
115	fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
116		let row_count = self.first().map_or(0, |c| c.data.len());
117		let system = present_system_columns(self);
118
119		let mut col_widths: Vec<usize> = Vec::new();
120
121		for (header, cells) in &system {
122			let max_val_width = cells.iter().map(|c| c.width()).max().unwrap_or(0);
123			col_widths.push(header.width().max(max_val_width));
124		}
125
126		for col in &self.columns {
127			let header_width = escape_control_chars(&col.name).width();
128			let mut max_val_width = 0;
129			for i in 0..col.data.len() {
130				max_val_width = max_val_width.max(escape_control_chars(&col.data.as_string(i)).width());
131			}
132			col_widths.push(header_width.max(max_val_width));
133		}
134
135		for w in &mut col_widths {
136			*w += 2;
137		}
138
139		let sep: String = if col_widths.is_empty() {
140			"++".to_string()
141		} else {
142			col_widths.iter().map(|w| format!("+{}", "-".repeat(*w + 2))).collect::<String>() + "+"
143		};
144
145		writeln!(f, "{}", sep)?;
146
147		let mut header_parts = Vec::new();
148		for (col_idx, (header, _)) in system.iter().enumerate() {
149			header_parts.push(centered(col_widths[col_idx], header));
150		}
151		for (offset, col) in self.columns.iter().enumerate() {
152			let name = escape_control_chars(&col.name);
153			header_parts.push(centered(col_widths[system.len() + offset], &name));
154		}
155		writeln!(f, "|{}|", header_parts.join("|"))?;
156		writeln!(f, "{}", sep)?;
157
158		for row_idx in 0..row_count {
159			let mut row_parts = Vec::new();
160			for (col_idx, (_, cells)) in system.iter().enumerate() {
161				row_parts.push(centered(col_widths[col_idx], &cells[row_idx]));
162			}
163			for (offset, col) in self.columns.iter().enumerate() {
164				let val = escape_control_chars(&col.data.as_string(row_idx));
165				row_parts.push(centered(col_widths[system.len() + offset], &val));
166			}
167			writeln!(f, "|{}|", row_parts.join("|"))?;
168		}
169
170		writeln!(f, "{}", sep)
171	}
172}