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