1use rich::table::Cell;
4use rich::{Console, ConsoleOptions, Renderable, Segment, Table, Text};
5
6use super::group::{Aggregate, GroupBy};
7use super::rules::{ResolvedRules, StyleRules};
8use super::sort::{sorted_indices, SortKey};
9use super::{frame_builders, headers, style, Column, Frame, Value};
10
11#[derive(Clone, Debug)]
57pub struct TableData {
58 columns: Vec<Column>,
59 rows: Vec<Vec<Value>>,
60 sort: Vec<SortKey>,
61 group: Option<GroupBy>,
62 totals: Vec<Aggregate>,
63 totals_label: String,
64 rules: StyleRules,
65 pub(super) frame: Frame,
66}
67
68frame_builders!([] TableData);
69
70impl TableData {
71 pub fn new(columns: impl IntoIterator<Item = Column>) -> Self {
73 TableData {
74 columns: columns.into_iter().collect(),
75 rows: Vec::new(),
76 sort: Vec::new(),
77 group: None,
78 totals: Vec::new(),
79 totals_label: "total".to_string(),
80 rules: StyleRules::new(),
81 frame: Frame::default(),
82 }
83 }
84
85 pub fn push(&mut self, row: impl IntoIterator<Item = Value>) -> &mut Self {
87 let row = normalize(row, self.columns.len());
88 self.rows.push(row);
89 self
90 }
91
92 pub fn extend<R: IntoIterator<Item = Value>>(
94 &mut self,
95 rows: impl IntoIterator<Item = R>,
96 ) -> &mut Self {
97 for row in rows {
98 self.push(row);
99 }
100 self
101 }
102
103 pub fn sort_by(mut self, keys: impl IntoIterator<Item = SortKey>) -> Self {
105 self.set_sort(keys);
106 self
107 }
108
109 pub fn set_sort(&mut self, keys: impl IntoIterator<Item = SortKey>) {
111 self.sort = keys.into_iter().collect();
112 }
113
114 pub fn group_by(mut self, group: GroupBy) -> Self {
116 self.group = Some(group);
117 self
118 }
119
120 pub fn totals(
124 mut self,
125 label: impl Into<String>,
126 aggregates: impl IntoIterator<Item = Aggregate>,
127 ) -> Self {
128 self.totals_label = label.into();
129 self.totals = aggregates.into_iter().collect();
130 self
131 }
132
133 pub fn style_rules(mut self, rules: StyleRules) -> Self {
137 self.rules = rules;
138 self
139 }
140
141 pub fn columns(&self) -> &[Column] {
143 &self.columns
144 }
145
146 pub fn rows(&self) -> &[Vec<Value>] {
148 &self.rows
149 }
150
151 pub fn sort_keys(&self) -> &[SortKey] {
153 &self.sort
154 }
155
156 pub fn order(&self) -> Vec<usize> {
158 sorted_indices(&self.rows, &self.sort)
159 }
160
161 pub fn to_table(&self, console: &Console) -> Table {
164 let headers = headers(console, &self.columns, &self.sort);
165 let mut table = self.frame.table(&self.columns, &headers, true, true);
166 let order = self.order();
167 let headers: Vec<&str> = self.columns.iter().map(Column::header).collect();
168 let rules = (!self.rules.is_empty()).then(|| self.rules.resolve(&headers, &self.rows));
169 let add = |table: &mut Table, row: &[Value]| match &rules {
170 None => {
171 table.add_row_text(self.cells(row));
172 }
173 Some(rules) => self.add_styled(table, rules, row),
174 };
175 match &self.group {
176 None => {
177 for &row in &order {
178 add(&mut table, &self.rows[row]);
179 }
180 }
181 Some(group) => {
182 let group_style = style(console, "table.group");
183 for g in group.groups(&self.rows, &order) {
184 let mut label = if g.key.is_empty() {
185 Text::new("(empty)")
186 } else {
187 self.columns
188 .get(group.column())
189 .map_or_else(|| g.key.to_text(), |c| c.cell(&g.key))
190 };
191 if group.column() != 0 {
192 if let Some(column) = self.columns.get(group.column()) {
193 label = Text::new(format!("{}: ", column.header())).append_text(&label);
194 }
195 }
196 let len = label.plain().len();
197 label.stylize(group_style.clone(), 0, len);
198 let mut header = vec![Text::new(""); self.columns.len()];
199 if let Some(first) = header.first_mut() {
200 *first = label;
201 }
202 table.add_row_text(header);
203 for &row in &g.rows {
204 add(&mut table, &self.rows[row]);
205 }
206 if !group.aggregates().is_empty() {
207 table.add_row_text(self.summary(
208 console,
209 group.summary_label(),
210 group.aggregates(),
211 &g.aggregates,
212 ));
213 }
214 }
215 }
216 }
217 if !self.totals.is_empty() {
218 let values: Vec<Value> = self
219 .totals
220 .iter()
221 .map(|aggregate| aggregate.over(&self.rows, &order))
222 .collect();
223 table.add_row_text(self.summary(console, &self.totals_label, &self.totals, &values));
224 }
225 table
226 }
227
228 fn cells(&self, row: &[Value]) -> Vec<Text> {
229 self.columns
230 .iter()
231 .zip(row)
232 .map(|(column, value)| column.cell(value))
233 .collect()
234 }
235
236 fn add_styled(&self, table: &mut Table, rules: &ResolvedRules<'_>, row: &[Value]) {
239 let cells = self
240 .cells(row)
241 .into_iter()
242 .enumerate()
243 .map(|(index, mut text)| {
244 if let Some(style) = rules.cell_style(row, index) {
245 let len = text.plain().len();
246 text.stylize(style, 0, len);
247 }
248 Cell::Text(text)
249 })
250 .collect();
251 table.add_row_with(cells, rules.row_style(row).map(Into::into), false);
252 }
253
254 fn summary(
258 &self,
259 console: &Console,
260 label: &str,
261 aggregates: &[Aggregate],
262 values: &[Value],
263 ) -> Vec<Text> {
264 let mut cells: Vec<Option<Text>> = vec![None; self.columns.len()];
265 for (aggregate, value) in aggregates.iter().zip(values) {
266 let Some(column) = self.columns.get(aggregate.column()) else {
267 continue;
268 };
269 let text = if aggregate.is_count() {
270 value.to_text()
271 } else {
272 column.cell(value)
273 };
274 let cell = &mut cells[aggregate.column()];
275 *cell = Some(match cell.take() {
276 Some(previous) => previous.append_text(&Text::new(", ")).append_text(&text),
277 None => text,
278 });
279 }
280 if let Some(first) = cells.first_mut() {
281 *first = match first.take() {
282 None => Some(Text::new(label)),
283 Some(value) if label.is_empty() => Some(value),
284 Some(value) => Some(Text::new(format!("{label}: ")).append_text(&value)),
285 };
286 }
287 let aggregate_style = style(console, "table.aggregate");
288 cells
289 .into_iter()
290 .map(|cell| {
291 let mut text = cell.unwrap_or_else(|| Text::new(""));
292 let len = text.plain().len();
293 text.stylize(aggregate_style.clone(), 0, len);
294 text
295 })
296 .collect()
297 }
298}
299
300pub(crate) fn normalize(row: impl IntoIterator<Item = Value>, columns: usize) -> Vec<Value> {
302 let mut row: Vec<Value> = row.into_iter().take(columns).collect();
303 row.resize(columns, Value::Null);
304 row
305}
306
307impl Renderable for TableData {
308 fn rich_render(&self, console: &Console, options: &ConsoleOptions) -> Vec<Segment> {
309 self.to_table(console).rich_render(console, options)
310 }
311
312 fn measure(&self, console: &Console, options: &ConsoleOptions) -> rich::measure::Measurement {
313 self.to_table(console).measure(console, options)
314 }
315}
316
317impl crate::a11y::AccessibleText for TableData {
318 fn accessible_text(&self, width: usize) -> String {
319 let console = Console::builder().width(width.max(1)).build();
320 self.to_table(&console).accessible_text(width)
321 }
322}