sheeter_engine/
transform.rs1use crate::filter::Filter;
2use crate::CanonicalValue;
3
4#[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 pub dedup: Option<Vec<String>>,
15}
16
17impl Transforms {
18 pub fn is_empty(&self) -> bool {
19 self.filter.is_none()
20 && self.select.is_none()
21 && self.rename.is_empty()
22 && self.limit.is_none()
23 && self.dedup.is_none()
24 }
25
26 pub fn apply_to_row(
29 &self,
30 mut row: Vec<(String, CanonicalValue)>,
31 ) -> Option<Vec<(String, CanonicalValue)>> {
32 if let Some(f) = &self.filter {
33 let wrapped = CanonicalValue::Object(row.clone());
34 if !f.matches(&wrapped) {
35 return None;
36 }
37 }
38 for (from, to) in &self.rename {
39 for (k, _) in row.iter_mut() {
40 if k == from {
41 *k = to.clone();
42 break;
43 }
44 }
45 }
46 if let Some(sel) = &self.select {
47 let mut new_row = Vec::with_capacity(sel.len());
48 for name in sel {
49 match row.iter().find(|(k, _)| k == name) {
50 Some((k, v)) => new_row.push((k.clone(), v.clone())),
51 None => new_row.push((name.clone(), CanonicalValue::Null)),
52 }
53 }
54 row = new_row;
55 }
56 Some(row)
57 }
58
59 pub fn apply_to_array(&self, value: &CanonicalValue) -> CanonicalValue {
61 if let CanonicalValue::Object(o) = value {
62 if o.len() == 1
63 && matches!(
64 &o[0].1,
65 CanonicalValue::Array(_) | CanonicalValue::Object(_)
66 )
67 {
68 let inner = self.apply_to_array(&o[0].1);
69 return CanonicalValue::Object(vec![(o[0].0.clone(), inner)]);
70 }
71 }
72 if let CanonicalValue::Object(o) = value {
75 let all_scalar = o.iter().all(|(_, v)| {
76 matches!(
77 v,
78 CanonicalValue::Null
79 | CanonicalValue::Bool(_)
80 | CanonicalValue::Number(_)
81 | CanonicalValue::String(_)
82 )
83 });
84 if all_scalar && !o.is_empty() {
85 if let Some(new_row) = self.apply_to_row(o.clone()) {
86 return CanonicalValue::Object(new_row);
87 }
88 return CanonicalValue::Null;
89 }
90 }
91 let rows = match value {
92 CanonicalValue::Array(a) => a,
93 _ => return value.clone(),
94 };
95 let limit = self.limit.unwrap_or(usize::MAX);
96 let mut out = Vec::new();
97 let mut seen: std::collections::HashSet<String> = std::collections::HashSet::new();
98 for row in rows {
99 if out.len() >= limit {
100 break;
101 }
102 match row {
103 CanonicalValue::Object(o) => {
104 if let Some(new_row) = self.apply_to_row(o.clone()) {
105 if let Some(cols) = &self.dedup {
106 let key: String = cols
107 .iter()
108 .map(|c| {
109 new_row
110 .iter()
111 .find(|(k, _)| k == c)
112 .map(|(_, v)| format!("{v:?}"))
113 .unwrap_or_default()
114 })
115 .collect::<Vec<_>>()
116 .join("\x1f");
117 if !seen.insert(key) {
118 continue;
119 }
120 }
121 out.push(CanonicalValue::Object(new_row));
122 }
123 }
124 other => out.push(other.clone()),
125 }
126 }
127 CanonicalValue::Array(out)
128 }
129}