Skip to main content

mint_core/data/
json.rs

1use serde_json::Value;
2use std::collections::HashMap;
3use std::path::Path;
4
5use super::DataSource;
6use super::error::DataError;
7use crate::layout::value::{DataValue, ValueSource};
8
9/// Shared JSON-based data source that reads version data from JSON objects.
10/// Result: `Vec<HashMap<String, Value>>` in version priority order.
11pub struct JsonDataSource {
12    version_columns: Vec<HashMap<String, Value>>,
13}
14
15impl JsonDataSource {
16    fn new(version_columns: Vec<HashMap<String, Value>>) -> Self {
17        JsonDataSource { version_columns }
18    }
19
20    /// Creates a JSON data source from a JSON object.
21    /// Expected format: `{ "VersionName": { "key1": value1, "key2": value2, ... }, ... }`
22    pub fn from_value(data: Value, versions: &[String]) -> Result<Self, DataError> {
23        let data: HashMap<String, HashMap<String, Value>> = serde_json::from_value(data)
24            .map_err(|e| DataError::FileError(format!("failed to parse JSON: {}", e)))?;
25
26        Self::from_version_map(data, versions)
27    }
28
29    /// Creates a JSON data source from JSON text.
30    pub fn from_str(json_content: &str, versions: &[String]) -> Result<Self, DataError> {
31        let data: HashMap<String, HashMap<String, Value>> = serde_json::from_str(json_content)
32            .map_err(|e| DataError::FileError(format!("failed to parse JSON: {}", e)))?;
33
34        Self::from_version_map(data, versions)
35    }
36
37    /// Creates a JSON data source from a JSON file path.
38    pub fn from_path(path: impl AsRef<Path>, versions: &[String]) -> Result<Self, DataError> {
39        let path = path.as_ref();
40        let json_content = std::fs::read_to_string(path).map_err(|_| {
41            DataError::FileError(format!("failed to open file: {}", path.display()))
42        })?;
43
44        Self::from_str(&json_content, versions)
45    }
46
47    fn from_version_map(
48        data: HashMap<String, HashMap<String, Value>>,
49        versions: &[String],
50    ) -> Result<Self, DataError> {
51        let mut version_columns = Vec::with_capacity(versions.len());
52
53        for version in versions {
54            let map = data
55                .get(version)
56                .ok_or_else(|| {
57                    DataError::RetrievalError(format!(
58                        "version '{}' not found in JSON data",
59                        version
60                    ))
61                })?
62                .clone();
63            version_columns.push(map);
64        }
65
66        Ok(Self::new(version_columns))
67    }
68
69    fn lookup(&self, name: &str) -> Option<&Value> {
70        self.version_columns
71            .iter()
72            .find_map(|map| map.get(name).filter(|v| !v.is_null()))
73    }
74
75    fn value_to_data_value(value: &Value) -> Result<DataValue, DataError> {
76        match value {
77            Value::Bool(b) => Ok(DataValue::Bool(*b)),
78            Value::Number(n) => {
79                if let Some(u) = n.as_u64() {
80                    Ok(DataValue::U64(u))
81                } else if let Some(i) = n.as_i64() {
82                    Ok(DataValue::I64(i))
83                } else if let Some(f) = n.as_f64() {
84                    Ok(DataValue::F64(f))
85                } else {
86                    Err(DataError::RetrievalError(
87                        "unsupported numeric type".to_owned(),
88                    ))
89                }
90            }
91            Value::String(s) => Ok(DataValue::Str(s.clone())),
92            _ => Err(DataError::RetrievalError(
93                "expected scalar value".to_owned(),
94            )),
95        }
96    }
97}
98
99impl DataSource for JsonDataSource {
100    fn retrieve_single_value(&self, name: &str) -> Result<DataValue, DataError> {
101        let result = (|| {
102            let value = self
103                .lookup(name)
104                .ok_or_else(|| DataError::RetrievalError("key not found in any version".into()))?;
105
106            let dv = Self::value_to_data_value(value)?;
107            match dv {
108                DataValue::Str(_) => Err(DataError::RetrievalError(
109                    "Found non-numeric single value".to_owned(),
110                )),
111                _ => Ok(dv),
112            }
113        })();
114
115        result.map_err(|e| DataError::WhileRetrieving {
116            name: name.to_owned(),
117            source: Box::new(e),
118        })
119    }
120
121    fn retrieve_1d_array_or_string(&self, name: &str) -> Result<ValueSource, DataError> {
122        let result = (|| {
123            let value = self
124                .lookup(name)
125                .ok_or_else(|| DataError::RetrievalError("key not found in any version".into()))?;
126
127            match value {
128                Value::Array(arr) => {
129                    let items: Result<Vec<_>, _> =
130                        arr.iter().map(Self::value_to_data_value).collect();
131                    Ok(ValueSource::Array(items?))
132                }
133                Value::String(s) => Ok(ValueSource::Single(DataValue::Str(s.clone()))),
134                _ => Err(DataError::RetrievalError(
135                    "expected array or string for 1D array".to_owned(),
136                )),
137            }
138        })();
139
140        result.map_err(|e| DataError::WhileRetrieving {
141            name: name.to_owned(),
142            source: Box::new(e),
143        })
144    }
145
146    fn retrieve_2d_array(&self, name: &str) -> Result<Vec<Vec<DataValue>>, DataError> {
147        let result = (|| {
148            let value = self
149                .lookup(name)
150                .ok_or_else(|| DataError::RetrievalError("key not found in any version".into()))?;
151
152            let Value::Array(outer) = value else {
153                return Err(DataError::RetrievalError(
154                    "expected 2D array (array of arrays)".to_owned(),
155                ));
156            };
157
158            outer
159                .iter()
160                .map(|row_val| {
161                    let Value::Array(inner) = row_val else {
162                        return Err(DataError::RetrievalError(
163                            "expected array for 2D array row".to_owned(),
164                        ));
165                    };
166                    inner.iter().map(Self::value_to_data_value).collect()
167                })
168                .collect()
169        })();
170
171        result.map_err(|e| DataError::WhileRetrieving {
172            name: name.to_owned(),
173            source: Box::new(e),
174        })
175    }
176}