Skip to main content

lemma/serialization/
json.rs

1use crate::planning::semantics::{DataDefinition, DataPath};
2use crate::Error;
3use indexmap::IndexMap;
4use serde::{Deserialize, Deserializer, Serialize, Serializer};
5use serde_json::Value;
6use std::collections::HashMap;
7
8/// Parse JSON object to data values for [`ExecutionPlan::set_data_values`](crate::planning::ExecutionPlan::set_data_values).
9///
10/// - `null` values are skipped
11/// - JSON strings and numbers use convenience Lemma literal syntax
12/// - structured objects use the same serialization shape as evaluation output
13pub fn from_json(json: &[u8]) -> Result<HashMap<String, Value>, Error> {
14    let map: HashMap<String, Value> = serde_json::from_slice(json)
15        .map_err(|e| Error::validation(format!("JSON parse error: {}", e), None, None::<String>))?;
16
17    Ok(data_values_from_map(map))
18}
19
20/// Filter nulls from a parsed JSON object (e.g. WASM `run` data).
21pub fn data_values_from_map(map: HashMap<String, Value>) -> HashMap<String, Value> {
22    map.into_iter().filter(|(_, v)| !v.is_null()).collect()
23}
24
25/// Wrap convenience string maps (CLI forms, `Engine::run` call sites).
26pub fn data_values_from_strings(map: HashMap<String, String>) -> HashMap<String, Value> {
27    map.into_iter()
28        .map(|(k, v)| (k, Value::String(v)))
29        .collect()
30}
31
32/// Serializes IndexMap<DataPath, DataDefinition> as array of [DataPath, DataDefinition] tuples.
33pub fn serialize_resolved_data_value_map<S>(
34    map: &IndexMap<DataPath, DataDefinition>,
35    serializer: S,
36) -> Result<S::Ok, S::Error>
37where
38    S: Serializer,
39{
40    let entries: Vec<(&DataPath, &DataDefinition)> = map.iter().collect();
41    entries.serialize(serializer)
42}
43
44/// Deserializes from array of [DataPath, DataDefinition] tuples, preserving order.
45pub fn deserialize_resolved_data_value_map<'de, D>(
46    deserializer: D,
47) -> Result<IndexMap<DataPath, DataDefinition>, D::Error>
48where
49    D: Deserializer<'de>,
50{
51    let entries: Vec<(DataPath, DataDefinition)> = Vec::deserialize(deserializer)?;
52    Ok(entries.into_iter().collect())
53}
54
55#[cfg(test)]
56mod tests {
57    use super::*;
58
59    #[test]
60    fn test_json_string_preserved() {
61        let json = br#"{"name": "Alice"}"#;
62        let result = from_json(json).unwrap();
63        assert_eq!(
64            result.get("name"),
65            Some(&Value::String("Alice".to_string()))
66        );
67    }
68
69    #[test]
70    fn test_json_number_preserved_in_map() {
71        let json = br#"{"count": 42}"#;
72        let result = from_json(json).unwrap();
73        assert!(result.get("count").unwrap().is_number());
74    }
75
76    #[test]
77    fn test_null_value_skipped() {
78        let json = br#"{"name": null, "age": "30"}"#;
79        let result = from_json(json).unwrap();
80        assert_eq!(result.len(), 1);
81        assert!(!result.contains_key("name"));
82        assert_eq!(result.get("age"), Some(&Value::String("30".to_string())));
83    }
84
85    #[test]
86    fn test_invalid_json_syntax() {
87        let json = br#"{"name": }"#;
88        let result = from_json(json);
89        assert!(result.is_err());
90        assert!(result.unwrap_err().to_string().contains("JSON parse error"));
91    }
92}