datalogic_rs/value/
convert.rs

1//! Conversion utilities for DataValue.
2//!
3//! This module provides utilities for converting between DataValue and other formats,
4//! such as JSON.
5
6use super::data_value::DataValue;
7use super::number::NumberValue;
8use crate::arena::DataArena;
9use serde_json::{Map as JsonMap, Number as JsonNumber, Value as JsonValue};
10use std::collections::HashMap;
11
12/// A trait for converting from JSON to DataValue.
13pub trait FromJson<'a> {
14    /// Converts a JSON value to a DataValue, allocating in the given arena.
15    fn from_json(json: &JsonValue, arena: &'a DataArena) -> DataValue<'a>;
16}
17
18/// A trait for converting from DataValue to JSON.
19pub trait ToJson {
20    /// Converts a DataValue to a JSON value.
21    fn to_json(&self) -> JsonValue;
22}
23
24impl<'a> FromJson<'a> for DataValue<'a> {
25    fn from_json(json: &JsonValue, arena: &'a DataArena) -> DataValue<'a> {
26        match json {
27            JsonValue::Null => DataValue::null(),
28            JsonValue::Bool(b) => DataValue::bool(*b),
29            JsonValue::Number(n) => {
30                if let Some(i) = n.as_i64() {
31                    DataValue::integer(i)
32                } else if let Some(f) = n.as_f64() {
33                    DataValue::float(f)
34                } else {
35                    // This should never happen with valid JSON
36                    DataValue::null()
37                }
38            }
39            JsonValue::String(s) => {
40                // Try to parse as datetime
41                if let Ok(dt) = super::parse_datetime(s) {
42                    return DataValue::datetime(dt);
43                }
44
45                // Try to parse as duration
46                if let Ok(duration) = super::parse_duration(s) {
47                    return DataValue::duration(duration);
48                }
49
50                // Default to string
51                DataValue::string(arena, s)
52            }
53            JsonValue::Array(arr) => {
54                // Pre-allocate space for the array elements
55                let mut values = Vec::with_capacity(arr.len());
56
57                // Convert each element in the array
58                for item in arr.iter() {
59                    values.push(DataValue::from_json(item, arena));
60                }
61
62                // Create the array DataValue
63                DataValue::array(arena, &values)
64            }
65            JsonValue::Object(obj) => {
66                // Pre-allocate space for the object entries
67                let mut entries = Vec::with_capacity(obj.len());
68
69                // Convert each key-value pair in the object
70                for (key, value) in obj.iter() {
71                    let interned_key = arena.intern_str(key);
72                    entries.push((interned_key, DataValue::from_json(value, arena)));
73                }
74
75                // Create the object DataValue
76                DataValue::object(arena, &entries)
77            }
78        }
79    }
80}
81
82impl ToJson for DataValue<'_> {
83    fn to_json(&self) -> JsonValue {
84        match self {
85            DataValue::Null => JsonValue::Null,
86            DataValue::Bool(b) => JsonValue::Bool(*b),
87            DataValue::Number(n) => {
88                match n {
89                    NumberValue::Integer(i) => {
90                        // Create a JSON number directly from the integer to preserve its type
91                        JsonValue::Number((*i).into())
92                    }
93                    NumberValue::Float(f) => {
94                        if let Some(num) = JsonNumber::from_f64(*f) {
95                            JsonValue::Number(num)
96                        } else {
97                            // Handle NaN, Infinity, etc.
98                            JsonValue::Null
99                        }
100                    }
101                }
102            }
103            DataValue::String(s) => JsonValue::String(s.to_string()),
104            DataValue::Array(arr) => {
105                let json_arr: Vec<JsonValue> = arr.iter().map(|item| item.to_json()).collect();
106                JsonValue::Array(json_arr)
107            }
108            DataValue::Object(entries) => {
109                let mut map = JsonMap::new();
110                for (key, value) in entries.iter() {
111                    map.insert((*key).to_string(), value.to_json());
112                }
113                JsonValue::Object(map)
114            }
115            DataValue::DateTime(dt) => {
116                // Format the datetime as an ISO8601 string
117                JsonValue::String(dt.to_rfc3339())
118            }
119            DataValue::Duration(d) => {
120                // Format the duration as a simplified string representation
121                let total_seconds = d.num_seconds();
122                let days = total_seconds / 86400;
123                let hours = (total_seconds % 86400) / 3600;
124                let minutes = (total_seconds % 3600) / 60;
125                let seconds = total_seconds % 60;
126
127                if days > 0 {
128                    JsonValue::String(format!("{}d:{}h:{}m:{}s", days, hours, minutes, seconds))
129                } else if hours > 0 {
130                    JsonValue::String(format!("{}h:{}m:{}s", hours, minutes, seconds))
131                } else if minutes > 0 {
132                    JsonValue::String(format!("{}m:{}s", minutes, seconds))
133                } else {
134                    JsonValue::String(format!("{}s", seconds))
135                }
136            }
137        }
138    }
139}
140
141/// Converts a JSON value to a DataValue.
142pub fn json_to_data_value<'a>(json: &JsonValue, arena: &'a DataArena) -> DataValue<'a> {
143    DataValue::from_json(json, arena)
144}
145
146/// Converts a DataValue to a JSON value.
147pub fn data_value_to_json(value: &DataValue<'_>) -> JsonValue {
148    value.to_json()
149}
150
151/// Converts a HashMap to a DataValue object.
152pub fn hash_map_to_data_value<'a, V>(
153    map: &HashMap<String, V>,
154    arena: &'a DataArena,
155    value_converter: impl Fn(&V, &'a DataArena) -> DataValue<'a>,
156) -> DataValue<'a> {
157    let entries: Vec<(&'a str, DataValue<'a>)> = map
158        .iter()
159        .map(|(key, value)| {
160            let interned_key = arena.intern_str(key);
161            let data_value = value_converter(value, arena);
162            (interned_key, data_value)
163        })
164        .collect();
165
166    // Create the object DataValue
167    DataValue::object(arena, &entries)
168}
169
170#[cfg(test)]
171mod tests {
172    use super::*;
173    use serde_json::json;
174
175    #[test]
176    fn test_json_conversion() {
177        let arena = DataArena::new();
178
179        // Create a complex JSON value
180        let json = json!({
181            "null": null,
182            "bool": true,
183            "integer": 42,
184            "float": 3.14,
185            "string": "hello",
186            "array": [1, 2, 3],
187            "object": {
188                "a": 1,
189                "b": "two"
190            }
191        });
192
193        // Convert JSON to DataValue
194        let data_value = DataValue::from_json(&json, &arena);
195
196        // Convert back to JSON
197        let json2 = data_value.to_json();
198
199        // Verify the round-trip conversion
200        assert_eq!(json, json2);
201    }
202
203    #[test]
204    fn test_hash_map_conversion() {
205        let arena = DataArena::new();
206
207        // Create a HashMap
208        let mut map = HashMap::new();
209        map.insert("a".to_string(), 1);
210        map.insert("b".to_string(), 2);
211        map.insert("c".to_string(), 3);
212
213        // Convert HashMap to DataValue
214        let data_value = hash_map_to_data_value(&map, &arena, |v, _| DataValue::integer(*v));
215
216        // Verify the conversion
217        if let DataValue::Object(entries) = data_value {
218            assert_eq!(entries.len(), 3);
219
220            // Check each entry
221            let mut found_a = false;
222            let mut found_b = false;
223            let mut found_c = false;
224
225            for (key, value) in entries.iter() {
226                let v = value.as_i64().unwrap();
227
228                match *key {
229                    "a" => {
230                        assert_eq!(v, 1);
231                        found_a = true;
232                    }
233                    "b" => {
234                        assert_eq!(v, 2);
235                        found_b = true;
236                    }
237                    "c" => {
238                        assert_eq!(v, 3);
239                        found_c = true;
240                    }
241                    _ => panic!("Unexpected key: {}", key),
242                }
243            }
244
245            assert!(
246                found_a && found_b && found_c,
247                "Not all expected keys were found"
248            );
249        } else {
250            panic!("Expected DataValue::Object");
251        }
252    }
253
254    #[test]
255    fn test_json_to_data_value() {
256        let arena = DataArena::new();
257
258        // Create a JSON value
259        let json = json!({
260            "name": "John",
261            "age": 30,
262            "is_active": true
263        });
264
265        // Convert JSON to DataValue using the helper function
266        let data_value = json_to_data_value(&json, &arena);
267
268        // Verify the conversion
269        assert!(data_value.is_object());
270        let obj = data_value.as_object().unwrap();
271
272        // Find and verify each field
273        let mut found_name = false;
274        let mut found_age = false;
275        let mut found_is_active = false;
276
277        for (key, value) in obj.iter() {
278            match *key {
279                "name" => {
280                    assert_eq!(value.as_str(), Some("John"));
281                    found_name = true;
282                }
283                "age" => {
284                    assert_eq!(value.as_i64(), Some(30));
285                    found_age = true;
286                }
287                "is_active" => {
288                    assert_eq!(value.as_bool(), Some(true));
289                    found_is_active = true;
290                }
291                _ => panic!("Unexpected key: {}", key),
292            }
293        }
294
295        assert!(
296            found_name && found_age && found_is_active,
297            "Not all expected keys were found"
298        );
299    }
300
301    #[test]
302    fn test_data_value_to_json() {
303        let arena = DataArena::new();
304
305        // Create a DataValue
306        let data_value = DataValue::object(
307            &arena,
308            &[
309                (arena.intern_str("name"), DataValue::string(&arena, "Alice")),
310                (
311                    arena.intern_str("scores"),
312                    DataValue::array(
313                        &arena,
314                        &[
315                            DataValue::integer(95),
316                            DataValue::integer(87),
317                            DataValue::integer(92),
318                        ],
319                    ),
320                ),
321            ],
322        );
323
324        // Convert DataValue to JSON using the helper function
325        let json = data_value_to_json(&data_value);
326
327        // Verify the conversion
328        if let JsonValue::Object(map) = json {
329            assert_eq!(map.len(), 2);
330
331            // Check name field
332            if let Some(JsonValue::String(name)) = map.get("name") {
333                assert_eq!(name, "Alice");
334            } else {
335                panic!("Expected 'name' to be a string");
336            }
337
338            // Check scores field
339            if let Some(JsonValue::Array(scores)) = map.get("scores") {
340                assert_eq!(scores.len(), 3);
341                assert_eq!(scores[0], JsonValue::Number(95.into()));
342                assert_eq!(scores[1], JsonValue::Number(87.into()));
343                assert_eq!(scores[2], JsonValue::Number(92.into()));
344            } else {
345                panic!("Expected 'scores' to be an array");
346            }
347        } else {
348            panic!("Expected a JSON object");
349        }
350    }
351
352    #[test]
353    fn test_hash_map_to_data_value_with_complex_values() {
354        let arena = DataArena::new();
355
356        // Create a HashMap with complex values (nested structures)
357        let mut map = HashMap::new();
358        map.insert("user1".to_string(), ("Alice", 25));
359        map.insert("user2".to_string(), ("Bob", 30));
360
361        // Convert HashMap to DataValue with a custom converter
362        let data_value = hash_map_to_data_value(&map, &arena, |&(name, age), arena| {
363            DataValue::object(
364                arena,
365                &[
366                    (arena.intern_str("name"), DataValue::string(arena, name)),
367                    (arena.intern_str("age"), DataValue::integer(age)),
368                ],
369            )
370        });
371
372        // Verify the conversion
373        if let DataValue::Object(entries) = data_value {
374            assert_eq!(entries.len(), 2);
375
376            // Check each user
377            for (key, value) in entries.iter() {
378                match *key {
379                    "user1" => {
380                        if let DataValue::Object(user_entries) = value {
381                            let mut found_name = false;
382                            let mut found_age = false;
383
384                            for (user_key, user_value) in user_entries.iter() {
385                                match *user_key {
386                                    "name" => {
387                                        assert_eq!(user_value.as_str(), Some("Alice"));
388                                        found_name = true;
389                                    }
390                                    "age" => {
391                                        assert_eq!(user_value.as_i64(), Some(25));
392                                        found_age = true;
393                                    }
394                                    _ => panic!("Unexpected user key: {}", user_key),
395                                }
396                            }
397
398                            assert!(
399                                found_name && found_age,
400                                "Not all expected user fields were found"
401                            );
402                        } else {
403                            panic!("Expected user1 to be an object");
404                        }
405                    }
406                    "user2" => {
407                        if let DataValue::Object(user_entries) = value {
408                            let mut found_name = false;
409                            let mut found_age = false;
410
411                            for (user_key, user_value) in user_entries.iter() {
412                                match *user_key {
413                                    "name" => {
414                                        assert_eq!(user_value.as_str(), Some("Bob"));
415                                        found_name = true;
416                                    }
417                                    "age" => {
418                                        assert_eq!(user_value.as_i64(), Some(30));
419                                        found_age = true;
420                                    }
421                                    _ => panic!("Unexpected user key: {}", user_key),
422                                }
423                            }
424
425                            assert!(
426                                found_name && found_age,
427                                "Not all expected user fields were found"
428                            );
429                        } else {
430                            panic!("Expected user2 to be an object");
431                        }
432                    }
433                    _ => panic!("Unexpected key: {}", key),
434                }
435            }
436        } else {
437            panic!("Expected DataValue::Object");
438        }
439    }
440}