Skip to main content

fv_plan/
row.rs

1//! Ordered pipeline rows. Column order is significant (it becomes the output schema), so a row is an
2//! ordered list of (name, value) — not a sorted map. Values are `fv_value::Value`.
3
4use fv_value::{Scope, Value};
5
6/// One pipeline row, preserving column order (unlike a sorted map).
7#[derive(Debug, Clone, Default, PartialEq)]
8pub struct Row(pub Vec<(String, Value)>);
9
10impl Row {
11    pub fn new() -> Self {
12        Row(Vec::new())
13    }
14
15    pub fn get(&self, key: &str) -> Value {
16        self.0
17            .iter()
18            .find(|(k, _)| k == key)
19            .map(|(_, v)| v.clone())
20            .unwrap_or(Value::Null)
21    }
22
23    pub fn contains(&self, key: &str) -> bool {
24        self.0.iter().any(|(k, _)| k == key)
25    }
26
27    /// Set a key: overwrite in place if present (preserving position), else append.
28    pub fn set(&mut self, key: &str, value: Value) {
29        if let Some(slot) = self.0.iter_mut().find(|(k, _)| k == key) {
30            slot.1 = value;
31        } else {
32            self.0.push((key.to_string(), value));
33        }
34    }
35
36    pub fn columns(&self) -> Vec<String> {
37        self.0.iter().map(|(k, _)| k.clone()).collect()
38    }
39
40    /// A lookup scope for the value dialect (order irrelevant for evaluation). Prefer
41    /// [`lookup`](Self::lookup) on the hot path — it evaluates directly against the row with no map
42    /// build (T3).
43    pub fn scope(&self) -> Scope {
44        self.0.iter().cloned().collect()
45    }
46
47    /// The row as a zero-copy [`fv_value::Lookup`] — evaluate an expression against it without
48    /// building a `Scope` per row (the T0-measured 211ns/row saved on filter/applyExpression).
49    pub fn lookup(&self) -> &[(String, Value)] {
50        &self.0
51    }
52}