1use std::collections::{BTreeMap, HashMap};
97use std::convert::Infallible;
98use std::fmt;
99use std::hash::Hash;
100use std::sync::{Mutex, PoisonError};
101
102use rich::{Console, ConsoleOptions, LineRenderable, Overflow, Renderable, Segment, Table, Text};
103
104use super::data::{TableData, normalize};
105use super::sort::{SortKey, compare_rows};
106use super::{Column, Frame, Value, frame_builders, headers, style};
107
108#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
110pub enum Window {
111 #[default]
113 All,
114 Head(usize),
116 Tail(usize),
119}
120
121#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
123pub struct RenderStats {
124 pub frames: u64,
126 pub rows_prepared: u64,
128 pub rows_rendered: u64,
131 pub relayouts: u64,
134}
135
136struct Entry<K> {
137 key: K,
138 values: Vec<Value>,
139 version: u64,
140}
141
142struct RowCache {
144 version: u64,
145 cells: Vec<Text>,
146 widths: Vec<usize>,
147 lines: Option<Vec<Vec<Segment>>>,
148}
149
150#[derive(Clone, Debug, PartialEq, Eq)]
152struct LayoutKey {
153 width: usize,
154 maxima: Vec<usize>,
155 ascii_only: bool,
156 safe_box: bool,
157 legacy_windows: bool,
158}
159
160#[derive(Default)]
161struct Cache {
162 rows: HashMap<u64, RowCache>,
163 layout: Option<LayoutKey>,
164 stats: RenderStats,
165}
166
167#[must_use = "builder methods return a new value; it does nothing until it is rendered or used"]
174pub struct StreamingTable<K> {
175 columns: Vec<Column>,
176 frame: Frame,
177 window: Window,
178 capacity: Option<usize>,
179 sort: Vec<SortKey>,
180 entries: BTreeMap<u64, Entry<K>>,
182 index: HashMap<K, u64>,
183 next_seq: u64,
184 next_version: u64,
185 evicted: u64,
186 cache: Mutex<Cache>,
187}
188
189impl<K> fmt::Debug for StreamingTable<K> {
190 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
191 f.debug_struct("StreamingTable")
192 .field("columns", &self.columns)
193 .field("rows", &self.entries.len())
194 .field("window", &self.window)
195 .field("capacity", &self.capacity)
196 .field("sort", &self.sort)
197 .field("evicted", &self.evicted)
198 .finish()
199 }
200}
201
202frame_builders!([K] StreamingTable<K>);
203
204impl<K: Eq + Hash + Clone> StreamingTable<K> {
205 pub fn new(columns: impl IntoIterator<Item = Column>) -> Self {
207 StreamingTable {
208 columns: columns.into_iter().collect(),
209 frame: Frame::default(),
210 window: Window::All,
211 capacity: None,
212 sort: Vec::new(),
213 entries: BTreeMap::new(),
214 index: HashMap::new(),
215 next_seq: 0,
216 next_version: 0,
217 evicted: 0,
218 cache: Mutex::new(Cache::default()),
219 }
220 }
221
222 pub fn window(mut self, window: Window) -> Self {
224 self.window = window;
225 self
226 }
227
228 pub fn set_window(&mut self, window: Window) {
230 self.window = window;
231 }
232
233 pub fn capacity(mut self, rows: usize) -> Self {
236 self.capacity = Some(rows);
237 self.evict();
238 self
239 }
240
241 pub fn set_sort(&mut self, keys: impl IntoIterator<Item = SortKey>) {
245 self.sort = keys.into_iter().collect();
246 }
247
248 pub fn sort_by(mut self, keys: impl IntoIterator<Item = SortKey>) -> Self {
250 self.set_sort(keys);
251 self
252 }
253
254 fn version(&mut self) -> u64 {
255 self.next_version += 1;
256 self.next_version
257 }
258
259 fn evict(&mut self) {
260 let Some(capacity) = self.capacity else {
261 return;
262 };
263 while self.entries.len() > capacity {
264 let Some((_, entry)) = self.entries.pop_first() else {
265 break;
266 };
267 self.index.remove(&entry.key);
268 self.evicted += 1;
269 }
270 }
271
272 pub fn upsert(&mut self, key: K, row: impl IntoIterator<Item = Value>) -> bool {
277 let values = normalize(row, self.columns.len());
278 if let Some(&seq) = self.index.get(&key) {
279 let entry = self.entries.get(&seq).expect("indexed rows exist");
280 if entry.values != values {
281 let version = self.version();
282 let entry = self.entries.get_mut(&seq).expect("indexed rows exist");
283 entry.values = values;
284 entry.version = version;
285 }
286 return false;
287 }
288 let seq = self.next_seq;
289 self.next_seq += 1;
290 let version = self.version();
291 self.index.insert(key.clone(), seq);
292 self.entries.insert(
293 seq,
294 Entry {
295 key,
296 values,
297 version,
298 },
299 );
300 self.evict();
301 true
302 }
303
304 pub fn update_cell(&mut self, key: &K, column: usize, value: impl Into<Value>) -> bool {
307 let value = value.into();
308 let Some(&seq) = self.index.get(key) else {
309 return false;
310 };
311 if column >= self.columns.len() {
312 return false;
313 }
314 if self.entries[&seq].values[column] != value {
315 let version = self.version();
316 let entry = self.entries.get_mut(&seq).expect("indexed rows exist");
317 entry.values[column] = value;
318 entry.version = version;
319 }
320 true
321 }
322
323 pub fn remove(&mut self, key: &K) -> Option<Vec<Value>> {
325 let seq = self.index.remove(key)?;
326 self.entries.remove(&seq).map(|entry| entry.values)
327 }
328
329 pub fn clear(&mut self) {
331 self.entries.clear();
332 self.index.clear();
333 }
334
335 pub fn get(&self, key: &K) -> Option<&[Value]> {
337 let seq = self.index.get(key)?;
338 self.entries.get(seq).map(|entry| entry.values.as_slice())
339 }
340
341 pub fn contains_key(&self, key: &K) -> bool {
343 self.index.contains_key(key)
344 }
345
346 pub fn len(&self) -> usize {
348 self.entries.len()
349 }
350
351 pub fn is_empty(&self) -> bool {
353 self.entries.is_empty()
354 }
355
356 pub fn evicted(&self) -> u64 {
358 self.evicted
359 }
360
361 pub fn columns(&self) -> &[Column] {
363 &self.columns
364 }
365
366 pub fn rows(&self) -> impl Iterator<Item = (&K, &[Value])> + '_ {
368 self.entries
369 .values()
370 .map(|entry| (&entry.key, entry.values.as_slice()))
371 }
372
373 pub fn stats(&self) -> RenderStats {
375 self.lock().stats
376 }
377
378 pub fn reset_stats(&self) {
380 self.lock().stats = RenderStats::default();
381 }
382
383 pub fn invalidate(&self) {
387 let mut cache = self.lock();
388 cache.rows.clear();
389 cache.layout = None;
390 }
391
392 fn lock(&self) -> std::sync::MutexGuard<'_, Cache> {
393 self.cache.lock().unwrap_or_else(PoisonError::into_inner)
394 }
395
396 fn order(&self) -> Vec<u64> {
398 let mut order: Vec<u64> = self.entries.keys().copied().collect();
399 if !self.sort.is_empty() {
400 order.sort_by(|a, b| {
401 compare_rows(&self.entries[a].values, &self.entries[b].values, &self.sort)
402 });
403 }
404 order
405 }
406
407 fn visible(&self) -> (Vec<u64>, u64, u64) {
410 let order = self.order();
411 let total = order.len();
412 match self.window {
413 Window::All => (order, self.evicted, 0),
414 Window::Head(n) => {
415 let shown = n.min(total);
416 (
417 order[..shown].to_vec(),
418 self.evicted,
419 (total - shown) as u64,
420 )
421 }
422 Window::Tail(n) => {
423 let start = total.saturating_sub(n);
424 (order[start..].to_vec(), self.evicted + start as u64, 0)
425 }
426 }
427 }
428
429 pub fn to_data(&self) -> TableData {
432 let mut data = TableData::new(self.columns.clone());
433 data.frame = self.frame.clone();
434 for seq in self.order() {
435 data.push(self.entries[&seq].values.iter().cloned());
436 }
437 data.sort_by(self.sort.iter().copied())
438 }
439
440 pub fn to_table(&self, console: &Console) -> Table {
443 let headers = headers(console, &self.columns, &self.sort);
444 let mut table = self.frame.table(&self.columns, &headers, true, true);
445 for seq in self.visible().0 {
446 let values = &self.entries[&seq].values;
447 table.add_row_text(
448 self.columns
449 .iter()
450 .zip(values)
451 .map(|(column, value)| column.cell(value))
452 .collect(),
453 );
454 }
455 table
456 }
457
458 fn indicator(
459 console: &Console,
460 options: &ConsoleOptions,
461 count: u64,
462 what: &str,
463 ) -> Vec<Vec<Segment>> {
464 let ellipsis = if console.ascii_only() { "..." } else { "…" };
465 let plural = if count == 1 { "" } else { "s" };
466 let text = Text::styled(
467 format!("{ellipsis} {count} {what} row{plural}"),
468 style(console, "table.more"),
469 )
470 .no_wrap(true)
471 .overflow(Overflow::Ellipsis);
472 let mut options = options.clone();
475 options.height = None;
476 console.render_lines(&text, &options, false)
477 }
478
479 fn render_lines(&self, console: &Console, options: &ConsoleOptions) -> Vec<Vec<Segment>> {
481 let mut cache = self.lock();
482 let cache = &mut *cache;
483 cache.stats.frames += 1;
484 let (visible, earlier, later) = self.visible();
485 let headers = headers(console, &self.columns, &self.sort);
486
487 let mut out = Vec::new();
488 if earlier > 0 {
489 out.extend(Self::indicator(console, options, earlier, "earlier"));
490 }
491 let shown: std::collections::HashSet<u64> = visible.iter().copied().collect();
493 cache.rows.retain(|seq, _| shown.contains(seq));
494
495 if self.columns.is_empty() || visible.is_empty() {
496 let table = self.frame.table(&self.columns, &headers, true, true);
497 out.extend(table_lines(&table, console, options));
498 } else {
499 self.render_rows(console, options, cache, &visible, &headers, &mut out);
500 }
501 if later > 0 {
502 out.extend(Self::indicator(console, options, later, "more"));
503 }
504 out
505 }
506
507 fn render_rows(
508 &self,
509 console: &Console,
510 options: &ConsoleOptions,
511 cache: &mut Cache,
512 visible: &[u64],
513 headers: &[Text],
514 out: &mut Vec<Vec<Segment>>,
515 ) {
516 for seq in visible {
518 let entry = &self.entries[seq];
519 if cache
520 .rows
521 .get(seq)
522 .is_some_and(|row| row.version == entry.version)
523 {
524 continue;
525 }
526 let cells: Vec<Text> = self
527 .columns
528 .iter()
529 .zip(&entry.values)
530 .map(|(column, value)| column.cell(value))
531 .collect();
532 let widths = cells.iter().map(|cell| cell.measurement().1).collect();
533 cache.rows.insert(
534 *seq,
535 RowCache {
536 version: entry.version,
537 cells,
538 widths,
539 lines: None,
540 },
541 );
542 cache.stats.rows_prepared += 1;
543 }
544
545 let mut widest: Vec<(usize, Option<u64>)> = headers
549 .iter()
550 .map(|header| (header.measurement().1, None))
551 .collect();
552 for seq in visible {
553 for (best, &width) in widest.iter_mut().zip(&cache.rows[seq].widths) {
554 if width > best.0 {
555 *best = (width, Some(*seq));
556 }
557 }
558 }
559 let proxy: Vec<Text> = widest
560 .iter()
561 .enumerate()
562 .map(|(column, (_, seq))| match seq {
563 Some(seq) => cache.rows[seq].cells[column].clone(),
564 None => headers[column].clone(),
565 })
566 .collect();
567 let key = LayoutKey {
568 width: options.max_width,
569 maxima: widest.iter().map(|(width, _)| *width).collect(),
570 ascii_only: console.ascii_only(),
571 safe_box: console.safe_box(),
572 legacy_windows: console.legacy_windows(),
573 };
574 if cache.layout.as_ref() != Some(&key) {
575 for row in cache.rows.values_mut() {
576 row.lines = None;
577 }
578 cache.layout = Some(key);
579 cache.stats.relayouts += 1;
580 }
581
582 let edge = self.frame.edge_lines();
584 let render = |frame: &Frame, rows: &[&[Text]], show_header: bool| {
585 let mut table = frame.table(&self.columns, headers, show_header, true);
586 for row in rows {
587 table.add_row_text(row.to_vec());
588 }
589 table_lines(&table, console, options)
590 };
591 let bare = Frame {
592 title: None,
593 caption: None,
594 ..self.frame.clone()
595 };
596 let proxy_height = render(&bare, &[&proxy], false).len() - 2 * edge;
597
598 let head = Frame {
600 caption: None,
601 ..self.frame.clone()
602 };
603 let head = render(&head, &[&proxy], true);
604 out.extend_from_slice(&head[..head.len() - proxy_height - edge]);
605
606 for seq in visible {
607 let row = cache.rows.get_mut(seq).expect("prepared above");
608 if row.lines.is_none() {
609 let lines = render(&bare, &[&proxy, &row.cells], false);
610 row.lines = Some(lines[edge + proxy_height..lines.len() - edge].to_vec());
611 cache.stats.rows_rendered += 1;
612 }
613 out.extend(row.lines.iter().flatten().cloned());
614 }
615
616 let foot = Frame {
618 title: None,
619 ..self.frame.clone()
620 };
621 let foot = render(&foot, &[&proxy], false);
622 out.extend_from_slice(&foot[edge + proxy_height..]);
623 }
624}
625
626pub(super) fn table_lines(
628 table: &Table,
629 console: &Console,
630 options: &ConsoleOptions,
631) -> Vec<Vec<Segment>> {
632 let mut lines = Vec::new();
633 let result: Result<(), Infallible> = table.try_for_each_line(console, options, |line| {
634 lines.push(line);
635 Ok(())
636 });
637 match result {
638 Ok(()) => lines,
639 Err(never) => match never {},
640 }
641}
642
643impl<K: Eq + Hash + Clone> Renderable for StreamingTable<K> {
644 fn rich_render(&self, console: &Console, options: &ConsoleOptions) -> Vec<Segment> {
645 crate::event::flatten(self.render_lines(console, options))
646 }
647
648 fn measure(&self, console: &Console, options: &ConsoleOptions) -> rich::measure::Measurement {
649 self.to_table(console).measure(console, options)
650 }
651}
652
653impl<K: Eq + Hash + Clone> crate::a11y::AccessibleText for StreamingTable<K> {
654 fn accessible_text(&self, width: usize) -> String {
655 let console = Console::builder().width(width.max(1)).build();
656 self.to_table(&console).accessible_text(width)
657 }
658}