pub struct Table { /* private fields */ }Expand description
A bounded table of typed rows - the canonical structured-pipe value. Owned column names (so a
from_json parse can name columns dynamically), rows of Value, and a byte arena holding
the Str cells. All inline, no heap.
Implementations§
Source§impl Table
impl Table
Sourcepub fn add_col(&mut self, name: &[u8]) -> Option<usize>
pub fn add_col(&mut self, name: &[u8]) -> Option<usize>
Add a column by name; returns its index (or None if full / name too long → overflow).
Sourcepub fn intern(&mut self, s: &[u8]) -> Value
pub fn intern(&mut self, s: &[u8]) -> Value
Copy bytes into the arena and return a Str value (or Empty if the arena is full).
Sourcepub fn add_row(&mut self, vals: &[Value])
pub fn add_row(&mut self, vals: &[Value])
Append a row (values in column order). Loud-bounded: extra rows set Table::overflow.
Sourcepub fn cell_int(&self, r: usize, c: usize) -> Option<u64>
pub fn cell_int(&self, r: usize, c: usize) -> Option<u64>
The value of a numeric cell; None for a string or absent one. The twin of
Table::cell_bytes, for the same reason: deriving a view from a table beats rendering it
and parsing the text back.
Sourcepub fn cell_bytes(&self, r: usize, c: usize) -> &[u8]
pub fn cell_bytes(&self, r: usize, c: usize) -> &[u8]
The bytes of a string cell; empty for a numeric or absent one.
Exists so one table can be DERIVED from another - events deps reads the event table to count
what a service actually called. Without it the only way to consume a Table was to render it
and re-parse the text, which is the round-trip this record model exists to avoid.
Sourcepub fn overflow(&self) -> bool
pub fn overflow(&self) -> bool
True if any bound was exceeded while building (rows/cols/arena/name).
Sourcepub fn filter(&mut self, col: &str, op: &str, val: &str) -> bool
pub fn filter(&mut self, col: &str, op: &str, val: &str) -> bool
Keep only rows whose column col satisfies <op> val (in place). Ops: =/== != >
< >= <= ~(contains). Numeric when both sides parse as numbers, else textual.
Returns false (table unchanged) if col is not a column.
Keep rows matching col op val. Returns false if col is not a column - and in that case the
table is EMPTIED, not left alone.
It used to be left alone, so where peeer contains reply printed a notice and then handed the
whole table onward: a mistyped column produced output identical to “everything matched”, and a
| count downstream reported the full count. A predicate that could not be evaluated has not
selected anything, and saying so with zero rows is the honest answer (invariant 12) - the loud
notice stays, but it is no longer contradicted by the data underneath it.
Sourcepub fn select(&mut self, names: &[&str]) -> bool
pub fn select(&mut self, names: &[&str]) -> bool
Keep only the named columns, in the given order (in place). Returns false (table
unchanged) if any name is not a column. The arena (string storage) is untouched.
Sourcepub fn sort(&mut self, col: &str, reverse: bool) -> bool
pub fn sort(&mut self, col: &str, reverse: bool) -> bool
Order rows by column col (numeric when both ints, else by bytes), descending if
reverse. Returns false (table unchanged) if col is not a column.
Sourcepub fn aggregate(&self, col: &str, op: AggOp) -> Result<i128, AggErr>
pub fn aggregate(&self, col: &str, op: AggOp) -> Result<i128, AggErr>
Reduce a numeric column to a scalar (§5). Loud: NoColumn if col is not a column,
NonNumeric if any cell is not a number - never a silent 0. Empty table reduces to 0.
avg is integer, truncated toward zero. Signed columns reduce too (wifi list | max dbm is the
strongest signal), which is why the result is wide.
Sourcepub fn to_grid(&self, out: &mut impl RecordSink)
pub fn to_grid(&self, out: &mut impl RecordSink)
Render as an aligned text grid (the default view). String cells render in full (via the arena), so a long value is never silently clipped (§3.12).
Sourcepub fn grid_widths(&self) -> [usize; 8]
pub fn grid_widths(&self) -> [usize; 8]
Column widths for the grid rendering, computed over EVERY row.
Split out so a caller can render rows one at a time. A pager scrolling a long table needs the columns to stay put between frames, which means the widths must come from the whole table and not from the slice currently on screen.
Sourcepub fn grid_header(&self, out: &mut impl RecordSink, w: &[usize; 8])
pub fn grid_header(&self, out: &mut impl RecordSink, w: &[usize; 8])
The grid header line, at the widths from Table::grid_widths.
Sourcepub fn grid_row(&self, out: &mut impl RecordSink, r: usize, w: &[usize; 8])
pub fn grid_row(&self, out: &mut impl RecordSink, r: usize, w: &[usize; 8])
One row of the grid, at the widths from Table::grid_widths. An out-of-range r writes
nothing, so a pager asking past the end gets a blank line rather than a panic.
Sourcepub fn to_json(&self, out: &mut impl RecordSink)
pub fn to_json(&self, out: &mut impl RecordSink)
Render as a JSON array of objects (to json). Values are plain ASCII today - a real
string-escaper is a documented follow-up.
Sourcepub fn to_yaml(&self, out: &mut impl RecordSink)
pub fn to_yaml(&self, out: &mut impl RecordSink)
Render as YAML - a list of mappings (to yaml). String cells render in full.
Sourcepub fn from_json(input: &[u8]) -> Result<Table, &'static str>
pub fn from_json(input: &[u8]) -> Result<Table, &'static str>
Parse a JSON array of flat objects into a Table - the from json bridge. Bounded
subset: [ {"k": v, …}, … ] with string / number / true|false / null values, no
nesting. The first object defines the columns; later objects fill known columns (new
keys ignored). On malformed input, returns Err(reason) with a bare static reason (the
caller adds any from json: prefix).
Sourcepub fn encode(&self, out: &mut impl RecordSink)
pub fn encode(&self, out: &mut impl RecordSink)
Encode the table into a compact, bounded binary form for crossing a service
boundary as records (no JSON round-trip). This is emphatically not JSON - it is the
Table itself on the wire. Layout:
magic "GSR1" | ncols:u8 | nrows:u8
per column: name_len:u8 | name bytes
per cell: tag:u8 (0=empty 1=int 2=str 3=signed)
int → val:u64-le ; str → len:u16-le | bytes ; empty → (nothing)Symmetric with Table::decode. The whole encoding is bounded by the table’s own
bounds; a producer sends it as one IPC message (≤ 4 KiB) for a small table, or chunks it
- the shell drains chunks until EOT, then decodes. (A chunked producer must never emit a
lone
0x04chunk, which is the EOT marker; the magic guarantees the first chunk is not.)
Sourcepub fn decode(bytes: &[u8]) -> Result<Table, &'static str>
pub fn decode(bytes: &[u8]) -> Result<Table, &'static str>
Decode the binary form produced by Table::encode. Validates the magic and every length
against the table bounds and the buffer end - a truncated, oversized, or non-record buffer
is a loud Err, never a misparse (§3.12). Strings intern into the new table’s arena.