Skip to main content

lemma/serialization/
json.rs

1use crate::parsing::ast::Span;
2use crate::planning::ExecutionPlan;
3use crate::semantic::{FactPath, LemmaFact, LemmaType, LiteralValue};
4use crate::LemmaError;
5use crate::Source;
6use serde::{Deserialize, Deserializer, Serializer};
7use serde_json::Value;
8use std::collections::{HashMap, HashSet};
9use std::sync::Arc;
10
11/// Parse JSON to string values for use with ExecutionPlan::with_values().
12///
13/// - `null` values are skipped
14/// - All other values are converted to their string representation
15pub fn from_json(json: &[u8], plan: &ExecutionPlan) -> Result<HashMap<String, String>, LemmaError> {
16    let map: HashMap<String, Value> = serde_json::from_slice(json).map_err(|e| {
17        LemmaError::engine(
18            format!("JSON parse error: {}", e),
19            Span {
20                start: 0,
21                end: 0,
22                line: 1,
23                col: 0,
24            },
25            "<input>",
26            Arc::from(""),
27            &plan.doc_name,
28            1,
29            None::<String>,
30        )
31    })?;
32
33    Ok(map
34        .into_iter()
35        .filter(|(_, v)| !v.is_null())
36        .map(|(k, v)| (k, json_value_to_string(&v)))
37        .collect())
38}
39
40fn json_value_to_string(value: &Value) -> String {
41    match value {
42        Value::String(s) => s.clone(),
43        Value::Number(n) => n.to_string(),
44        Value::Bool(b) => b.to_string(),
45        Value::Array(_) | Value::Object(_) => serde_json::to_string(value).unwrap_or_default(),
46        Value::Null => String::new(),
47    }
48}
49
50/// Custom serializer for HashMap<FactPath, LemmaFact>
51///
52/// JSON object keys must be strings, so FactPath keys are serialized as strings
53/// using their Display implementation (e.g., "age" or "employee.salary").
54pub fn serialize_fact_path_map<S>(
55    map: &HashMap<FactPath, LemmaFact>,
56    serializer: S,
57) -> Result<S::Ok, S::Error>
58where
59    S: Serializer,
60{
61    use serde::ser::SerializeMap;
62    let mut map_serializer = serializer.serialize_map(Some(map.len()))?;
63    for (key, value) in map {
64        map_serializer.serialize_entry(&key.to_string(), value)?;
65    }
66    map_serializer.end()
67}
68
69/// Custom deserializer for HashMap<FactPath, LemmaFact>
70///
71/// Deserializes string keys back to FactPath using FactPath::from_path().
72pub fn deserialize_fact_path_map<'de, D>(
73    deserializer: D,
74) -> Result<HashMap<FactPath, LemmaFact>, D::Error>
75where
76    D: Deserializer<'de>,
77{
78    let map: HashMap<String, LemmaFact> = HashMap::deserialize(deserializer)?;
79    let mut result = HashMap::new();
80    for (key_str, value) in map {
81        let path_parts: Vec<String> = key_str.split('.').map(|s| s.to_string()).collect();
82        let fact_path = FactPath::from_path(path_parts);
83        result.insert(fact_path, value);
84    }
85    Ok(result)
86}
87
88/// Custom serializer for HashMap<FactPath, LemmaType>
89///
90/// JSON object keys must be strings, so FactPath keys are serialized as strings
91/// using their Display implementation (e.g., "age" or "employee.salary").
92pub fn serialize_fact_type_map<S>(
93    map: &HashMap<FactPath, LemmaType>,
94    serializer: S,
95) -> Result<S::Ok, S::Error>
96where
97    S: Serializer,
98{
99    use serde::ser::SerializeMap;
100    let mut map_serializer = serializer.serialize_map(Some(map.len()))?;
101    for (key, value) in map {
102        map_serializer.serialize_entry(&key.to_string(), value)?;
103    }
104    map_serializer.end()
105}
106
107/// Custom deserializer for HashMap<FactPath, LemmaType>
108///
109/// Deserializes string keys back to FactPath using FactPath::from_path().
110pub fn deserialize_fact_type_map<'de, D>(
111    deserializer: D,
112) -> Result<HashMap<FactPath, LemmaType>, D::Error>
113where
114    D: Deserializer<'de>,
115{
116    let map: HashMap<String, LemmaType> = HashMap::deserialize(deserializer)?;
117    let mut result = HashMap::new();
118    for (key_str, value) in map {
119        let path_parts: Vec<String> = key_str.split('.').map(|s| s.to_string()).collect();
120        let fact_path = FactPath::from_path(path_parts);
121        result.insert(fact_path, value);
122    }
123    Ok(result)
124}
125
126/// Custom serializer for HashMap<FactPath, LiteralValue>
127///
128/// JSON object keys must be strings, so FactPath keys are serialized as strings
129/// using their Display implementation (e.g., "age" or "employee.salary").
130pub fn serialize_fact_value_map<S>(
131    map: &HashMap<FactPath, LiteralValue>,
132    serializer: S,
133) -> Result<S::Ok, S::Error>
134where
135    S: Serializer,
136{
137    use serde::ser::SerializeMap;
138    let mut map_serializer = serializer.serialize_map(Some(map.len()))?;
139    for (key, value) in map {
140        map_serializer.serialize_entry(&key.to_string(), value)?;
141    }
142    map_serializer.end()
143}
144
145/// Custom deserializer for HashMap<FactPath, LiteralValue>
146///
147/// Deserializes string keys back to FactPath using FactPath::from_path().
148pub fn deserialize_fact_value_map<'de, D>(
149    deserializer: D,
150) -> Result<HashMap<FactPath, LiteralValue>, D::Error>
151where
152    D: Deserializer<'de>,
153{
154    let map: HashMap<String, LiteralValue> = HashMap::deserialize(deserializer)?;
155    let mut result = HashMap::new();
156    for (key_str, value) in map {
157        let path_parts: Vec<String> = key_str.split('.').map(|s| s.to_string()).collect();
158        let fact_path = FactPath::from_path(path_parts);
159        result.insert(fact_path, value);
160    }
161    Ok(result)
162}
163
164/// Custom serializer for HashMap<FactPath, String> (document references)
165pub fn serialize_fact_doc_ref_map<S>(
166    map: &HashMap<FactPath, String>,
167    serializer: S,
168) -> Result<S::Ok, S::Error>
169where
170    S: Serializer,
171{
172    use serde::ser::SerializeMap;
173    let mut map_serializer = serializer.serialize_map(Some(map.len()))?;
174    for (key, value) in map {
175        map_serializer.serialize_entry(&key.to_string(), value)?;
176    }
177    map_serializer.end()
178}
179
180/// Custom deserializer for HashMap<FactPath, String> (document references)
181pub fn deserialize_fact_doc_ref_map<'de, D>(
182    deserializer: D,
183) -> Result<HashMap<FactPath, String>, D::Error>
184where
185    D: Deserializer<'de>,
186{
187    let map: HashMap<String, String> = HashMap::deserialize(deserializer)?;
188    let mut result = HashMap::new();
189    for (key_str, value) in map {
190        let path_parts: Vec<String> = key_str.split('.').map(|s| s.to_string()).collect();
191        let fact_path = FactPath::from_path(path_parts);
192        result.insert(fact_path, value);
193    }
194    Ok(result)
195}
196
197/// Custom serializer for HashMap<FactPath, Source> (fact sources)
198pub fn serialize_fact_source_map<S>(
199    map: &HashMap<FactPath, Source>,
200    serializer: S,
201) -> Result<S::Ok, S::Error>
202where
203    S: Serializer,
204{
205    use serde::ser::SerializeMap;
206    let mut map_serializer = serializer.serialize_map(Some(map.len()))?;
207    for (key, value) in map {
208        map_serializer.serialize_entry(&key.to_string(), value)?;
209    }
210    map_serializer.end()
211}
212
213/// Custom deserializer for HashMap<FactPath, Source> (fact sources)
214pub fn deserialize_fact_source_map<'de, D>(
215    deserializer: D,
216) -> Result<HashMap<FactPath, Source>, D::Error>
217where
218    D: Deserializer<'de>,
219{
220    let map: HashMap<String, Source> = HashMap::deserialize(deserializer)?;
221    let mut result = HashMap::new();
222    for (key_str, value) in map {
223        let path_parts: Vec<String> = key_str.split('.').map(|s| s.to_string()).collect();
224        let fact_path = FactPath::from_path(path_parts);
225        result.insert(fact_path, value);
226    }
227    Ok(result)
228}
229
230/// Custom serializer for HashSet<FactPath>
231///
232/// Serializes as a JSON array of strings using FactPath's Display implementation.
233pub fn serialize_fact_path_set<S>(set: &HashSet<FactPath>, serializer: S) -> Result<S::Ok, S::Error>
234where
235    S: Serializer,
236{
237    use serde::ser::SerializeSeq;
238    let mut seq = serializer.serialize_seq(Some(set.len()))?;
239    for item in set {
240        seq.serialize_element(&item.to_string())?;
241    }
242    seq.end()
243}
244
245/// Custom deserializer for HashSet<FactPath>
246///
247/// Deserializes array of strings back to FactPath using FactPath::from_path().
248pub fn deserialize_fact_path_set<'de, D>(deserializer: D) -> Result<HashSet<FactPath>, D::Error>
249where
250    D: Deserializer<'de>,
251{
252    let vec: Vec<String> = Vec::deserialize(deserializer)?;
253    let mut result = HashSet::new();
254    for key_str in vec {
255        let path_parts: Vec<String> = key_str.split('.').map(|s| s.to_string()).collect();
256        let fact_path = FactPath::from_path(path_parts);
257        result.insert(fact_path);
258    }
259    Ok(result)
260}
261
262#[cfg(test)]
263mod tests {
264    use super::*;
265    fn create_test_plan() -> ExecutionPlan {
266        ExecutionPlan {
267            doc_name: "test".to_string(),
268            fact_schema: HashMap::new(),
269            fact_values: HashMap::new(),
270            doc_refs: HashMap::new(),
271            fact_sources: HashMap::new(),
272            rules: vec![],
273            sources: HashMap::from([("<test>".to_string(), "".to_string())]),
274        }
275    }
276
277    #[test]
278    fn test_json_string_to_string() {
279        let plan = create_test_plan();
280        let json = br#"{"name": "Alice"}"#;
281        let result = from_json(json, &plan).unwrap();
282        assert_eq!(result.get("name"), Some(&"Alice".to_string()));
283    }
284
285    #[test]
286    fn test_json_number_to_string() {
287        let plan = create_test_plan();
288        let json = br#"{"name": 42}"#;
289        let result = from_json(json, &plan).unwrap();
290        assert_eq!(result.get("name"), Some(&"42".to_string()));
291    }
292
293    #[test]
294    fn test_json_boolean_to_string() {
295        let plan = create_test_plan();
296        let json = br#"{"name": true}"#;
297        let result = from_json(json, &plan).unwrap();
298        assert_eq!(result.get("name"), Some(&"true".to_string()));
299    }
300
301    #[test]
302    fn test_json_array_to_string() {
303        let plan = create_test_plan();
304        let json = br#"{"data": [1, 2, 3]}"#;
305        let result = from_json(json, &plan).unwrap();
306        assert_eq!(result.get("data"), Some(&"[1,2,3]".to_string()));
307    }
308
309    #[test]
310    fn test_json_object_to_string() {
311        let plan = create_test_plan();
312        let json = br#"{"config": {"key": "value"}}"#;
313        let result = from_json(json, &plan).unwrap();
314        assert_eq!(
315            result.get("config"),
316            Some(&"{\"key\":\"value\"}".to_string())
317        );
318    }
319
320    #[test]
321    fn test_null_value_skipped() {
322        let plan = create_test_plan();
323        let json = br#"{"name": null, "age": 30}"#;
324        let result = from_json(json, &plan).unwrap();
325        assert_eq!(result.len(), 1);
326        assert!(!result.contains_key("name"));
327        assert_eq!(result.get("age"), Some(&"30".to_string()));
328    }
329
330    #[test]
331    fn test_all_null_values() {
332        let plan = create_test_plan();
333        let json = br#"{"name": null}"#;
334        let result = from_json(json, &plan).unwrap();
335        assert!(result.is_empty());
336    }
337
338    #[test]
339    fn test_mixed_valid_types() {
340        let plan = create_test_plan();
341        let json = br#"{"name": "Test", "count": 5, "active": true, "discount": 21}"#;
342        let result = from_json(json, &plan).unwrap();
343        assert_eq!(result.len(), 4);
344        assert_eq!(result.get("name"), Some(&"Test".to_string()));
345        assert_eq!(result.get("count"), Some(&"5".to_string()));
346        assert_eq!(result.get("active"), Some(&"true".to_string()));
347        assert_eq!(result.get("discount"), Some(&"21".to_string()));
348    }
349
350    #[test]
351    fn test_invalid_json_syntax() {
352        let plan = create_test_plan();
353        let json = br#"{"name": }"#;
354        let result = from_json(json, &plan);
355        assert!(result.is_err());
356        let error_message = result.unwrap_err().to_string();
357        assert!(error_message.contains("JSON parse error"));
358    }
359}