Skip to main content

sheeter_engine/
transform.rs

1use crate::filter::Filter;
2use crate::CanonicalValue;
3
4/// Row-level transformations applied between reading and writing.
5///
6/// Transforms are applied in order: filter → rename → select → limit.
7/// All transforms are optional; an empty `Transforms` is a no-op.
8#[derive(Debug, Clone, Default)]
9pub struct Transforms {
10    pub filter: Option<Filter>,
11    pub select: Option<Vec<String>>,
12    pub rename: Vec<(String, String)>,
13    pub limit: Option<usize>,
14}
15
16impl Transforms {
17    pub fn is_empty(&self) -> bool {
18        self.filter.is_none()
19            && self.select.is_none()
20            && self.rename.is_empty()
21            && self.limit.is_none()
22    }
23
24    /// Applies transforms to a single row. Returns `None` if the row was
25    /// filtered out.
26    pub fn apply_to_row(
27        &self,
28        mut row: Vec<(String, CanonicalValue)>,
29    ) -> Option<Vec<(String, CanonicalValue)>> {
30        if let Some(f) = &self.filter {
31            let wrapped = CanonicalValue::Object(row.clone());
32            if !f.matches(&wrapped) {
33                return None;
34            }
35        }
36        for (from, to) in &self.rename {
37            for (k, _) in row.iter_mut() {
38                if k == from {
39                    *k = to.clone();
40                    break;
41                }
42            }
43        }
44        if let Some(sel) = &self.select {
45            let mut new_row = Vec::with_capacity(sel.len());
46            for name in sel {
47                match row.iter().find(|(k, _)| k == name) {
48                    Some((k, v)) => new_row.push((k.clone(), v.clone())),
49                    None => new_row.push((name.clone(), CanonicalValue::Null)),
50                }
51            }
52            row = new_row;
53        }
54        Some(row)
55    }
56
57    /// Applies transforms to a `CanonicalValue::Array` of objects.
58    pub fn apply_to_array(&self, value: &CanonicalValue) -> CanonicalValue {
59        let rows = match value {
60            CanonicalValue::Array(a) => a,
61            _ => return value.clone(),
62        };
63        let limit = self.limit.unwrap_or(usize::MAX);
64        let mut out = Vec::new();
65        for row in rows {
66            if out.len() >= limit {
67                break;
68            }
69            match row {
70                CanonicalValue::Object(o) => {
71                    if let Some(new_row) = self.apply_to_row(o.clone()) {
72                        out.push(CanonicalValue::Object(new_row));
73                    }
74                }
75                other => out.push(other.clone()),
76            }
77        }
78        CanonicalValue::Array(out)
79    }
80}