Skip to main content

osp_cli/dsl/stages/
values.rs

1use crate::core::row::Row;
2use anyhow::Result;
3use serde_json::{Map, Value};
4
5use super::common::parse_terms;
6
7#[derive(Debug, Clone, Default)]
8pub(crate) struct ValuesPlan {
9    keys: Vec<String>,
10}
11
12impl ValuesPlan {
13    pub(crate) fn extract_row(&self, row: &Row) -> Vec<Row> {
14        let mut out = Vec::new();
15
16        if self.keys.is_empty() {
17            for value in row.values() {
18                emit_value_rows(&mut out, value);
19            }
20            return out;
21        }
22
23        for key in &self.keys {
24            if let Some(value) = row.get(key) {
25                emit_value_rows(&mut out, value);
26            }
27        }
28
29        out
30    }
31}
32
33pub(crate) fn compile(spec: &str) -> ValuesPlan {
34    ValuesPlan {
35        keys: parse_terms(spec),
36    }
37}
38
39pub fn apply(rows: Vec<Row>, spec: &str) -> Result<Vec<Row>> {
40    let plan = compile(spec);
41    let mut out: Vec<Row> = Vec::new();
42
43    for row in rows {
44        out.extend(plan.extract_row(&row));
45    }
46
47    Ok(out)
48}
49
50fn emit_value_rows(out: &mut Vec<Row>, value: &Value) {
51    match value {
52        Value::Array(values) => {
53            for item in values {
54                let mut row = Map::new();
55                row.insert("value".to_string(), item.clone());
56                out.push(row);
57            }
58        }
59        _ => {
60            let mut row = Map::new();
61            row.insert("value".to_string(), value.clone());
62            out.push(row);
63        }
64    }
65}
66
67#[cfg(test)]
68mod tests {
69    use serde_json::json;
70
71    use super::apply;
72
73    #[test]
74    fn explodes_array_values() {
75        let rows = vec![
76            json!({"members": ["a", "b"]})
77                .as_object()
78                .cloned()
79                .expect("object"),
80        ];
81
82        let output = apply(rows, "members").expect("values should work");
83        assert_eq!(output.len(), 2);
84    }
85
86    #[test]
87    fn emits_requested_scalar_values_and_ignores_missing_keys() {
88        let rows = vec![
89            json!({"uid": "oistes", "mail": "oistes@example.org"})
90                .as_object()
91                .cloned()
92                .expect("object"),
93        ];
94
95        let output = apply(rows, "uid missing").expect("values should work");
96        assert_eq!(output.len(), 1);
97        assert_eq!(
98            output[0].get("value").and_then(|value| value.as_str()),
99            Some("oistes")
100        );
101    }
102
103    #[test]
104    fn empty_spec_emits_all_scalar_and_array_values_in_order() {
105        let rows = vec![
106            json!({"uid": "oistes", "members": ["a", "b"], "active": true})
107                .as_object()
108                .cloned()
109                .expect("object"),
110        ];
111
112        let output = apply(rows, "").expect("empty values stage should enumerate all fields");
113        let mut values = output
114            .iter()
115            .map(|row| {
116                row.get("value")
117                    .cloned()
118                    .unwrap_or(serde_json::Value::Null)
119                    .to_string()
120            })
121            .collect::<Vec<_>>();
122        values.sort();
123
124        assert_eq!(values, vec!["\"a\"", "\"b\"", "\"oistes\"", "true"]);
125    }
126}