session_rust/
encoders.rs

1use serde::{Deserialize, Serialize};
2use std::fs;
3
4/// Serialize data to JSON string with pretty formatting.
5pub fn json_dumps<T: Serialize>(
6    data: &T,
7    pretty: bool,
8) -> Result<String, Box<dyn std::error::Error>> {
9    if pretty {
10        Ok(serde_json::to_string_pretty(data)?)
11    } else {
12        Ok(serde_json::to_string(data)?)
13    }
14}
15
16/// Deserialize data from JSON string.
17pub fn json_loads<T: for<'de> Deserialize<'de>>(
18    json_str: &str,
19) -> Result<T, Box<dyn std::error::Error>> {
20    Ok(serde_json::from_str(json_str)?)
21}
22
23/// Write data to JSON file with pretty formatting.
24pub fn json_dump<T: Serialize>(
25    data: &T,
26    filepath: &str,
27    pretty: bool,
28) -> Result<(), Box<dyn std::error::Error>> {
29    let json_str = json_dumps(data, pretty)?;
30    fs::write(filepath, json_str)?;
31    Ok(())
32}
33
34/// Read data from JSON file.
35pub fn json_load<T: for<'de> Deserialize<'de>>(
36    filepath: &str,
37) -> Result<T, Box<dyn std::error::Error>> {
38    let json_str = fs::read_to_string(filepath)?;
39    json_loads(&json_str)
40}
41
42/// Encode a value to JSON (wrapper for Serialize types).
43/// This function automatically calls the serde serialization.
44pub fn encode_value<T: Serialize>(
45    value: &T,
46) -> Result<serde_json::Value, Box<dyn std::error::Error>> {
47    Ok(serde_json::to_value(value)?)
48}
49
50#[cfg(test)]
51mod tests {
52    use super::*;
53    use crate::line::Line;
54    use crate::point::Point;
55    use crate::vector::Vector;
56
57    #[test]
58    fn test_json_dump_and_load() {
59        let mut original = Point::new(1.5, 2.5, 3.5);
60        original.name = "test_point".to_string();
61
62        let filepath = "test_encoders_point.json";
63        json_dump(&original, filepath, true).unwrap();
64
65        let loaded: Point = json_load(filepath).unwrap();
66
67        assert_eq!(loaded.x(), original.x());
68        assert_eq!(loaded.y(), original.y());
69        assert_eq!(loaded.z(), original.z());
70        assert_eq!(loaded.name, original.name);
71
72        std::fs::remove_file(filepath).ok();
73    }
74
75    #[test]
76    fn test_json_dumps_and_loads() {
77        let mut original = Vector::new(42.1, 84.2, 126.3);
78        original.name = "test_vector".to_string();
79
80        let json_str = json_dumps(&original, true).unwrap();
81        assert!(!json_str.is_empty());
82        assert!(json_str.contains("Vector"));
83
84        let loaded: Vector = json_loads(&json_str).unwrap();
85
86        assert_eq!(loaded.x(), original.x());
87        assert_eq!(loaded.y(), original.y());
88        assert_eq!(loaded.z(), original.z());
89        assert_eq!(loaded.name, original.name);
90    }
91
92    #[test]
93    fn test_encode_collection() {
94        let points = vec![
95            Point::new(1.0, 2.0, 3.0),
96            Point::new(4.0, 5.0, 6.0),
97            Point::new(7.0, 8.0, 9.0),
98        ];
99
100        let json_str = json_dumps(&points, true).unwrap();
101        assert!(!json_str.is_empty());
102
103        let loaded: Vec<Point> = json_loads(&json_str).unwrap();
104        assert_eq!(loaded.len(), 3);
105        assert_eq!(loaded[0].x(), 1.0);
106        assert_eq!(loaded[1].y(), 5.0);
107        assert_eq!(loaded[2].z(), 9.0);
108    }
109
110    #[test]
111    fn test_nested_collections() {
112        let lines = vec![
113            Line::new(0.0, 0.0, 0.0, 1.0, 0.0, 0.0),
114            Line::new(0.0, 0.0, 0.0, 0.0, 1.0, 0.0),
115        ];
116
117        let json_str = json_dumps(&lines, true).unwrap();
118        let loaded: Vec<Line> = json_loads(&json_str).unwrap();
119
120        assert_eq!(loaded.len(), 2);
121        assert_eq!(loaded[0].end().x(), 1.0);
122        assert_eq!(loaded[1].end().y(), 1.0);
123    }
124
125    #[test]
126    fn test_roundtrip_with_file() {
127        let vectors = vec![
128            Vector::new(1.0, 0.0, 0.0),
129            Vector::new(0.0, 1.0, 0.0),
130            Vector::new(0.0, 0.0, 1.0),
131        ];
132
133        let filepath = "test_encoders_collection.json";
134        json_dump(&vectors, filepath, true).unwrap();
135
136        let loaded: Vec<Vector> = json_load(filepath).unwrap();
137
138        assert_eq!(loaded.len(), 3);
139        assert_eq!(loaded[0].x(), 1.0);
140        assert_eq!(loaded[1].y(), 1.0);
141        assert_eq!(loaded[2].z(), 1.0);
142
143        std::fs::remove_file(filepath).ok();
144    }
145
146    #[test]
147    fn test_pretty_vs_compact() {
148        let point = Point::new(1.0, 2.0, 3.0);
149
150        let pretty = json_dumps(&point, true).unwrap();
151        let compact = json_dumps(&point, false).unwrap();
152
153        assert!(pretty.len() > compact.len());
154        assert!(pretty.contains("\n"));
155        assert!(!compact.contains("\n"));
156
157        let loaded_pretty: Point = json_loads(&pretty).unwrap();
158        let loaded_compact: Point = json_loads(&compact).unwrap();
159
160        assert_eq!(loaded_pretty.x(), 1.0);
161        assert_eq!(loaded_compact.x(), 1.0);
162    }
163
164    #[test]
165    fn test_decode_primitives() {
166        let num: i32 = 42;
167        let json_str = json_dumps(&num, false).unwrap();
168        let loaded: i32 = json_loads(&json_str).unwrap();
169        assert_eq!(loaded, 42);
170
171        let float: f64 = 2.5;
172        let json_str = json_dumps(&float, false).unwrap();
173        let loaded: f64 = json_loads(&json_str).unwrap();
174        assert_eq!(loaded, 2.5);
175
176        let text = "hello";
177        let json_str = json_dumps(&text, false).unwrap();
178        let loaded: String = json_loads(&json_str).unwrap();
179        assert_eq!(loaded, "hello");
180
181        let flag = true;
182        let json_str = json_dumps(&flag, false).unwrap();
183        let loaded: bool = json_loads(&json_str).unwrap();
184        assert!(loaded);
185    }
186
187    #[test]
188    fn test_decode_list() {
189        let data = vec![1, 2, 3];
190        let json_str = json_dumps(&data, false).unwrap();
191        let loaded: Vec<i32> = json_loads(&json_str).unwrap();
192        assert_eq!(loaded, vec![1, 2, 3]);
193
194        let points = vec![Point::new(1.0, 2.0, 3.0), Point::new(4.0, 5.0, 6.0)];
195        let json_str = json_dumps(&points, false).unwrap();
196        let loaded: Vec<Point> = json_loads(&json_str).unwrap();
197        assert_eq!(loaded.len(), 2);
198        assert_eq!(loaded[0].x(), 1.0);
199        assert_eq!(loaded[1].x(), 4.0);
200    }
201
202    #[test]
203    fn test_decode_dict() {
204        use std::collections::HashMap;
205
206        let mut data = HashMap::new();
207        data.insert("a".to_string(), 1);
208        data.insert("b".to_string(), 2);
209        let json_str = json_dumps(&data, false).unwrap();
210        let loaded: HashMap<String, i32> = json_loads(&json_str).unwrap();
211        assert_eq!(loaded.get("a"), Some(&1));
212        assert_eq!(loaded.get("b"), Some(&2));
213
214        let vec = Vector::new(1.0, 2.0, 3.0);
215        let json_str = json_dumps(&vec, false).unwrap();
216        let loaded: Vector = json_loads(&json_str).unwrap();
217        assert_eq!(loaded.x(), 1.0);
218    }
219
220    #[test]
221    fn test_list_in_list_in_list() {
222        let data = vec![vec![vec![1, 2], vec![3, 4]], vec![vec![5, 6], vec![7, 8]]];
223        let json_str = json_dumps(&data, false).unwrap();
224        let loaded: Vec<Vec<Vec<i32>>> = json_loads(&json_str).unwrap();
225
226        assert_eq!(loaded[0][0][0], 1);
227        assert_eq!(loaded[1][1][1], 8);
228        assert_eq!(loaded.len(), 2);
229    }
230
231    #[test]
232    fn test_dict_of_lists() {
233        use serde_json::json;
234
235        let points = vec![Point::new(1.0, 0.0, 0.0), Point::new(0.0, 1.0, 0.0)];
236
237        let data = json!({
238            "numbers": [1, 2, 3],
239            "letters": ["a", "b", "c"],
240            "points": points
241        });
242
243        let json_str = data.to_string();
244        let loaded: serde_json::Value = json_loads(&json_str).unwrap();
245
246        assert_eq!(loaded["numbers"].as_array().unwrap().len(), 3);
247        assert_eq!(loaded["letters"][0], "a");
248
249        let loaded_points: Vec<Point> = serde_json::from_value(loaded["points"].clone()).unwrap();
250        assert_eq!(loaded_points.len(), 2);
251        assert_eq!(loaded_points[0].x(), 1.0);
252    }
253
254    #[test]
255    fn test_list_of_dict() {
256        use serde_json::json;
257
258        let point = Point::new(1.0, 2.0, 3.0);
259
260        let data = json!([
261            {"name": "point1", "value": 10},
262            {"name": "point2", "value": 20},
263            {"geometry": point}
264        ]);
265
266        let json_str = data.to_string();
267        let loaded: serde_json::Value = json_loads(&json_str).unwrap();
268
269        assert_eq!(loaded.as_array().unwrap().len(), 3);
270        assert_eq!(loaded[0]["name"], "point1");
271        assert_eq!(loaded[1]["value"], 20);
272
273        let loaded_point: Point = serde_json::from_value(loaded[2]["geometry"].clone()).unwrap();
274        assert_eq!(loaded_point.z(), 3.0);
275    }
276
277    #[test]
278    fn test_dict_of_dicts() {
279        use serde_json::json;
280
281        let point = Point::new(1.0, 2.0, 3.0);
282        let vec = Vector::new(0.0, 0.0, 1.0);
283
284        let data = json!({
285            "config": {
286                "tolerance": 0.001,
287                "scale": 1000
288            },
289            "geometry": {
290                "point": point,
291                "vector": vec
292            }
293        });
294
295        let json_str = data.to_string();
296        let loaded: serde_json::Value = json_loads(&json_str).unwrap();
297
298        assert_eq!(loaded["config"]["tolerance"], 0.001);
299        assert_eq!(loaded["config"]["scale"], 1000);
300
301        let loaded_point: Point =
302            serde_json::from_value(loaded["geometry"]["point"].clone()).unwrap();
303        let loaded_vec: Vector =
304            serde_json::from_value(loaded["geometry"]["vector"].clone()).unwrap();
305        assert_eq!(loaded_point.x(), 1.0);
306        assert_eq!(loaded_vec.z(), 1.0);
307    }
308}