structfs_serde_store/
convert.rs1pub use crate::value_serde::{from_value, to_value};
4use crate::{Codec, JsonCodec};
5use structfs_core_store::{Error, Format, Value};
6
7pub fn value_to_json(value: Value) -> Result<serde_json::Value, Error> {
9 let bytes = JsonCodec.encode(&value, &Format::JSON)?;
10 serde_json::from_slice(&bytes).map_err(|e| Error::decode(Format::JSON, e.to_string()))
11}
12pub fn json_to_value(json: serde_json::Value) -> Value {
14 match json {
15 serde_json::Value::Null => Value::Null,
16 serde_json::Value::Bool(v) => Value::Bool(v),
17 serde_json::Value::String(v) => Value::String(v),
18 serde_json::Value::Number(n) => {
19 if let Some(i) = n.as_i64() {
20 Value::Integer(i)
21 } else if let Some(u) = n.as_u64() {
22 Value::from(u)
23 } else {
24 Value::from(n.as_f64().expect("finite JSON number"))
25 }
26 }
27 serde_json::Value::Array(a) => Value::Array(a.into_iter().map(json_to_value).collect()),
28 serde_json::Value::Object(m) => {
29 Value::Map(m.into_iter().map(|(k, v)| (k, json_to_value(v))).collect())
30 }
31 }
32}
33
34#[cfg(test)]
35mod tests {
36 use super::*;
37 use serde::{Deserialize, Serialize};
38
39 #[derive(Debug, PartialEq, Serialize, Deserialize)]
40 struct TestStruct {
41 name: String,
42 age: u32,
43 active: bool,
44 }
45
46 #[test]
47 fn roundtrip_struct() {
48 let original = TestStruct {
49 name: "Alice".to_string(),
50 age: 30,
51 active: true,
52 };
53
54 let value = to_value(&original).unwrap();
55 let recovered: TestStruct = from_value(value).unwrap();
56
57 assert_eq!(original, recovered);
58 }
59
60 #[test]
61 fn json_to_value_numbers() {
62 let json = serde_json::json!({
63 "integer": 42,
64 "float": 2.75,
65 "negative": -100
66 });
67
68 let value = json_to_value(json);
69 match value {
70 Value::Map(map) => {
71 assert_eq!(map.get("integer"), Some(&Value::Integer(42)));
72 assert_eq!(map.get("negative"), Some(&Value::Integer(-100)));
73 if let Some(Value::Float(f)) = map.get("float") {
75 assert!((f - 2.75).abs() < 0.001);
76 } else {
77 panic!("expected float");
78 }
79 }
80 _ => panic!("expected map"),
81 }
82 }
83
84 #[test]
85 fn value_to_json_arrays() {
86 let value = Value::Array(vec![
87 Value::Integer(1),
88 Value::Integer(2),
89 Value::Integer(3),
90 ]);
91
92 let json = value_to_json(value).unwrap();
93 assert_eq!(json, serde_json::json!([1, 2, 3]));
94 }
95
96 #[test]
97 fn value_to_json_null() {
98 let value = Value::Null;
99 let json = value_to_json(value).unwrap();
100 assert_eq!(json, serde_json::Value::Null);
101 }
102
103 #[test]
104 fn value_to_json_bool() {
105 assert_eq!(
106 value_to_json(Value::Bool(true)).unwrap(),
107 serde_json::Value::Bool(true)
108 );
109 assert_eq!(
110 value_to_json(Value::Bool(false)).unwrap(),
111 serde_json::Value::Bool(false)
112 );
113 }
114
115 #[test]
116 fn value_to_json_string() {
117 let value = Value::String("hello world".to_string());
118 let json = value_to_json(value).unwrap();
119 assert_eq!(json, serde_json::Value::String("hello world".to_string()));
120 }
121
122 #[test]
123 fn value_to_json_integer() {
124 let value = Value::Integer(12345);
125 let json = value_to_json(value).unwrap();
126 assert_eq!(json, serde_json::json!(12345));
127 }
128
129 #[test]
130 fn value_to_json_float() {
131 let value = Value::Float(1.23456);
132 let json = value_to_json(value).unwrap();
133 if let serde_json::Value::Number(n) = json {
134 assert!((n.as_f64().unwrap() - 1.23456).abs() < 0.00001);
135 } else {
136 panic!("expected number");
137 }
138 }
139
140 #[test]
141 fn value_to_json_rejects_lossy_values() {
142 assert!(value_to_json(Value::Float(f64::NAN)).is_err());
143 assert!(value_to_json(Value::Bytes(vec![1, 2, 3])).is_err());
144 }
145
146 #[test]
147 fn value_to_json_map() {
148 use std::collections::BTreeMap;
149 let mut map = BTreeMap::new();
150 map.insert("key".to_string(), Value::String("value".to_string()));
151 map.insert("num".to_string(), Value::Integer(42));
152
153 let json = value_to_json(Value::Map(map)).unwrap();
154 assert_eq!(json, serde_json::json!({"key": "value", "num": 42}));
155 }
156
157 #[test]
158 fn json_to_value_null() {
159 let json = serde_json::Value::Null;
160 let value = json_to_value(json);
161 assert_eq!(value, Value::Null);
162 }
163
164 #[test]
165 fn json_to_value_bool() {
166 assert_eq!(
167 json_to_value(serde_json::Value::Bool(true)),
168 Value::Bool(true)
169 );
170 assert_eq!(
171 json_to_value(serde_json::Value::Bool(false)),
172 Value::Bool(false)
173 );
174 }
175
176 #[test]
177 fn json_to_value_string() {
178 let json = serde_json::Value::String("test".to_string());
179 let value = json_to_value(json);
180 assert_eq!(value, Value::String("test".to_string()));
181 }
182
183 #[test]
184 fn json_to_value_array() {
185 let json = serde_json::json!([1, "two", true]);
186 let value = json_to_value(json);
187 match value {
188 Value::Array(arr) => {
189 assert_eq!(arr.len(), 3);
190 assert_eq!(arr[0], Value::Integer(1));
191 assert_eq!(arr[1], Value::String("two".to_string()));
192 assert_eq!(arr[2], Value::Bool(true));
193 }
194 _ => panic!("expected array"),
195 }
196 }
197
198 #[test]
199 fn json_to_value_object() {
200 let json = serde_json::json!({"a": 1, "b": "two"});
201 let value = json_to_value(json);
202 match value {
203 Value::Map(map) => {
204 assert_eq!(map.get("a"), Some(&Value::Integer(1)));
205 assert_eq!(map.get("b"), Some(&Value::String("two".to_string())));
206 }
207 _ => panic!("expected map"),
208 }
209 }
210
211 #[test]
212 fn from_value_error() {
213 let value = Value::String("not a struct".to_string());
215 let result: Result<TestStruct, _> = from_value(value);
216 assert!(result.is_err());
217 }
218
219 #[test]
220 fn to_value_primitives() {
221 assert_eq!(to_value(&42i32).unwrap(), Value::Integer(42));
222 assert_eq!(
223 to_value(&"hello").unwrap(),
224 Value::String("hello".to_string())
225 );
226 assert_eq!(to_value(&true).unwrap(), Value::Bool(true));
227 }
228
229 #[test]
230 fn to_value_vec() {
231 let vec = vec![1, 2, 3];
232 let value = to_value(&vec).unwrap();
233 match value {
234 Value::Array(arr) => {
235 assert_eq!(arr.len(), 3);
236 assert_eq!(arr[0], Value::Integer(1));
237 }
238 _ => panic!("expected array"),
239 }
240 }
241
242 #[test]
243 fn roundtrip_nested_struct() {
244 #[derive(Debug, PartialEq, Serialize, Deserialize)]
245 struct Inner {
246 value: i32,
247 }
248
249 #[derive(Debug, PartialEq, Serialize, Deserialize)]
250 struct Outer {
251 inner: Inner,
252 items: Vec<String>,
253 }
254
255 let original = Outer {
256 inner: Inner { value: 99 },
257 items: vec!["a".to_string(), "b".to_string()],
258 };
259
260 let value = to_value(&original).unwrap();
261 let recovered: Outer = from_value(value).unwrap();
262 assert_eq!(original, recovered);
263 }
264
265 #[test]
266 fn roundtrip_option() {
267 let some_value: Option<i32> = Some(42);
268 let none_value: Option<i32> = None;
269
270 let some_converted = to_value(&some_value).unwrap();
271 let none_converted = to_value(&none_value).unwrap();
272
273 let some_recovered: Option<i32> = from_value(some_converted).unwrap();
274 let none_recovered: Option<i32> = from_value(none_converted).unwrap();
275
276 assert_eq!(some_recovered, Some(42));
277 assert_eq!(none_recovered, None);
278 }
279}