Skip to main content

densa_arrow/
live.rs

1use densa_data::{LiveTable, RowRef, View, ViewId};
2use densa_grid::{CellStyle, CellText, Color, Column, GridSource, Palette};
3
4use crate::table::{ArrowTable, CellState, TableError};
5
6/// Shows views of a `densa_data::LiveTable` in the grid, formatted like an [`ArrowTable`].
7///
8/// It keeps a formatter for each of the live table's batches; [`view`](Self::view) brings them
9/// up to date first, which allocates only when batches were added or compacted since the
10/// last call. Columns are sized from the first rows and widened for later numbers that no
11/// longer fit, as in [`ArrowTable`]. Given another
12/// table (or a clone), it rebuilds its formatters; for a table with another schema it also
13/// resets its column settings.
14#[derive(Debug, Clone)]
15pub struct LiveGrid {
16    mirror: ArrowTable,
17    /// The table object (`LiveTable::instance`) and generation the formatters were built for.
18    table: (u64, u64),
19}
20
21impl LiveGrid {
22    /// A grid for views of `table`, with formatters for its current batches.
23    pub fn new(table: &LiveTable) -> Self {
24        let mut grid = Self {
25            mirror: ArrowTable::new(table.schema().clone()),
26            table: (table.instance(), table.generation()),
27        };
28        grid.sync(table);
29        grid
30    }
31
32    /// Grid columns, as [`ArrowTable::columns`].
33    pub fn columns(&self) -> Vec<Column> {
34        self.mirror.columns()
35    }
36
37    /// As [`ArrowTable::set_decimals`].
38    ///
39    /// # Errors
40    /// [`TableError::NoSuchColumn`] if there is no such column.
41    pub fn set_decimals(
42        &mut self,
43        column: usize,
44        decimals: Option<usize>,
45    ) -> Result<(), TableError> {
46        self.mirror.set_decimals(column, decimals)
47    }
48
49    /// As [`ArrowTable::set_column_chars`].
50    ///
51    /// # Errors
52    /// [`TableError::NoSuchColumn`] if there is no such column.
53    pub fn set_column_chars(&mut self, column: usize, chars: usize) -> Result<(), TableError> {
54        self.mirror.set_column_chars(column, chars)
55    }
56
57    /// A [`GridSource`] showing view `view` of `table` from `first_row` (scrolled), or
58    /// `None` if the view does not exist. Call it each frame, after the table's changes.
59    pub fn view<'a>(
60        &'a mut self,
61        table: &'a LiveTable,
62        view: ViewId,
63        first_row: usize,
64        palette: &Palette,
65    ) -> Option<LiveGridView<'a>> {
66        self.sync(table);
67        Some(LiveGridView {
68            mirror: &self.mirror,
69            rows: table.view(view)?,
70            first_row,
71            text: palette.text,
72            dim: palette.dim_text,
73        })
74    }
75
76    /// Changes whenever rows changed a column's width (see [`ArrowTable::push`]).
77    pub(crate) fn widths(&self) -> u64 {
78        self.mirror.widths()
79    }
80
81    /// Whether the formatters match `table`'s batches, so a [`RowRef`] of it reads the
82    /// right values.
83    pub(crate) fn is_synced(&self, table: &LiveTable) -> bool {
84        self.table == (table.instance(), table.generation())
85            && self.mirror.stored_batches() == table.batches().len()
86    }
87
88    /// Writes the value of `column` at `at` (a row of the synced table) into `text`.
89    pub(crate) fn write(&self, at: RowRef, column: usize, text: &mut CellText) -> CellState {
90        self.mirror.write_at(at.batch, at.row, column, text)
91    }
92
93    /// Adds formatters for new batches, or rebuilds them after a compaction or for another
94    /// table.
95    pub(crate) fn sync(&mut self, table: &LiveTable) {
96        let current = (table.instance(), table.generation());
97        if current != self.table {
98            if self.mirror.schema() != table.schema() {
99                self.mirror = ArrowTable::new(table.schema().clone());
100            }
101            self.mirror.clear_batches();
102            self.table = current;
103        }
104        let new = table
105            .batches()
106            .get(self.mirror.stored_batches()..)
107            .unwrap_or_default();
108        for batch in new {
109            // Same schema and never empty (a live table stores no empty batch), so a push
110            // cannot fail; if it ever did, show nothing rather than misaligned rows.
111            if self.mirror.push(batch.clone()).is_err() {
112                self.mirror.clear_batches();
113                return;
114            }
115        }
116    }
117}
118
119/// A live view scrolled to a first row, as a grid's data. Cheap to create each frame.
120#[derive(Debug, Clone, Copy)]
121pub struct LiveGridView<'a> {
122    mirror: &'a ArrowTable,
123    rows: View<'a>,
124    first_row: usize,
125    text: Color,
126    dim: Color,
127}
128
129impl GridSource for LiveGridView<'_> {
130    fn rows(&self) -> usize {
131        self.rows.len().saturating_sub(self.first_row)
132    }
133
134    fn cell(&self, row: usize, column: usize, text: &mut CellText) -> CellStyle {
135        let state = match self.rows.row(self.first_row.saturating_add(row)) {
136            Some(at) => self.mirror.write_at(at.batch, at.row, column, text),
137            None => CellState::OutOfRange,
138        };
139        let color = if state == CellState::Value {
140            self.text
141        } else {
142            self.dim
143        };
144        CellStyle::new(color)
145    }
146}