plotlars_core/plots/
table.rs1use bon::bon;
2
3use polars::frame::DataFrame;
4
5use crate::{
6 components::{Cell, Header, Text},
7 ir::layout::LayoutIR,
8 ir::trace::{TableIR, TraceIR},
9};
10
11#[derive(Clone)]
83#[allow(dead_code)]
84pub struct Table {
85 traces: Vec<TraceIR>,
86 layout: LayoutIR,
87}
88
89#[bon]
90impl Table {
91 #[builder(on(String, into), on(Text, into))]
92 pub fn new(
93 data: &DataFrame,
94 columns: Vec<&str>,
95 header: Option<&Header>,
96 cell: Option<&Cell>,
97 column_width: Option<f64>,
98 plot_title: Option<Text>,
99 ) -> Self {
100 let column_names: Vec<String> = if let Some(h) = header {
102 if let Some(custom_values) = &h.values {
103 custom_values.clone()
104 } else {
105 columns.iter().map(|&c| c.to_string()).collect()
106 }
107 } else {
108 columns.iter().map(|&c| c.to_string()).collect()
109 };
110
111 let mut column_data: Vec<Vec<String>> = Vec::new();
113 for column_name in &columns {
114 let col_data = crate::data::get_string_column(data, column_name);
115 let col_strings: Vec<String> = col_data
116 .iter()
117 .map(|opt| opt.clone().unwrap_or_default())
118 .collect();
119 column_data.push(col_strings);
120 }
121
122 let ir_trace = TraceIR::Table(TableIR {
124 header: header.cloned(),
125 cell: cell.cloned(),
126 column_names,
127 column_data,
128 column_width,
129 });
130 let traces = vec![ir_trace];
131 let layout = LayoutIR {
132 title: plot_title.clone(),
133 x_title: None,
134 y_title: None,
135 y2_title: None,
136 z_title: None,
137 legend_title: None,
138 legend: None,
139 dimensions: None,
140 bar_mode: None,
141 box_mode: None,
142 box_gap: None,
143 margin_bottom: None,
144 axes_2d: None,
145 scene_3d: None,
146 polar: None,
147 mapbox: None,
148 grid: None,
149 annotations: vec![],
150 };
151 Self { traces, layout }
152 }
153}
154
155#[bon]
156impl Table {
157 #[builder(
158 start_fn = try_builder,
159 finish_fn = try_build,
160 builder_type = TableTryBuilder,
161 on(String, into),
162 on(Text, into),
163 )]
164 pub fn try_new(
165 data: &DataFrame,
166 columns: Vec<&str>,
167 header: Option<&Header>,
168 cell: Option<&Cell>,
169 column_width: Option<f64>,
170 plot_title: Option<Text>,
171 ) -> Result<Self, crate::io::PlotlarsError> {
172 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
173 Self::__orig_new(data, columns, header, cell, column_width, plot_title)
174 }))
175 .map_err(|panic| {
176 let msg = panic
177 .downcast_ref::<String>()
178 .cloned()
179 .or_else(|| panic.downcast_ref::<&str>().map(|s| s.to_string()))
180 .unwrap_or_else(|| "unknown error".to_string());
181 crate::io::PlotlarsError::PlotBuild { message: msg }
182 })
183 }
184}
185
186impl crate::Plot for Table {
187 fn ir_traces(&self) -> &[TraceIR] {
188 &self.traces
189 }
190
191 fn ir_layout(&self) -> &LayoutIR {
192 &self.layout
193 }
194}
195
196#[cfg(test)]
197mod tests {
198 use super::*;
199 use crate::Plot;
200 use polars::prelude::*;
201
202 #[test]
203 fn test_basic_one_trace() {
204 let df = df![
205 "name" => ["Alice", "Bob"],
206 "age" => [30, 25]
207 ]
208 .unwrap();
209 let plot = Table::builder()
210 .data(&df)
211 .columns(vec!["name", "age"])
212 .build();
213 assert_eq!(plot.ir_traces().len(), 1);
214 }
215
216 #[test]
217 fn test_trace_variant() {
218 let df = df![
219 "col1" => ["a", "b"],
220 "col2" => ["c", "d"]
221 ]
222 .unwrap();
223 let plot = Table::builder()
224 .data(&df)
225 .columns(vec!["col1", "col2"])
226 .build();
227 assert!(matches!(plot.ir_traces()[0], TraceIR::Table(_)));
228 }
229
230 #[test]
231 fn test_layout_no_axes() {
232 let df = df![
233 "col1" => ["a"]
234 ]
235 .unwrap();
236 let plot = Table::builder().data(&df).columns(vec!["col1"]).build();
237 let layout = plot.ir_layout();
238 assert!(layout.axes_2d.is_none());
239 assert!(layout.scene_3d.is_none());
240 assert!(layout.polar.is_none());
241 }
242}