1use base64::Engine;
3use base64::engine::general_purpose::STANDARD as B64;
4use serde_json::Value as JsonValue;
5use std::borrow::Cow;
6
7use crate::errors::{DataError, DataErrorCode, DataResult};
8use crate::model::{EnumValue, StructFieldValue, Value};
9
10pub fn to_json(value: &Value) -> DataResult<String> {
12 let json = encode_value(value)?;
13 serde_json::to_string(&json)
14 .map_err(|e| DataError::new(DataErrorCode::Serialization, e.to_string()))
15}
16
17pub fn from_json(s: &str) -> DataResult<Value> {
19 let parsed: JsonValue = serde_json::from_str(s).map_err(|e| {
20 DataError::new(
21 DataErrorCode::Serialization,
22 format!("parse error at {}: {}", e.line(), e.column()),
23 )
24 })?;
25 decode_value(&parsed)
26}
27
28pub fn encode_value(value: &Value) -> DataResult<JsonValue> {
30 let mut obj = serde_json::Map::new();
31 match value {
32 Value::Unit => {
33 obj.insert("type".into(), JsonValue::String("unit".into()));
34 }
35 Value::Bool(b) => {
36 obj.insert("type".into(), JsonValue::String("bool".into()));
37 obj.insert("value".into(), JsonValue::Bool(*b));
38 }
39 Value::Int(i) => {
40 obj.insert("type".into(), JsonValue::String("int".into()));
41 obj.insert("value".into(), JsonValue::Number((*i).into()));
42 }
43 Value::Float(f) => {
44 if !f.is_finite() {
45 return Err(DataError::new(
46 DataErrorCode::Serialization,
47 "non-finite floats are not supported in JSON",
48 ));
49 }
50 obj.insert("type".into(), JsonValue::String("float".into()));
51 obj.insert(
52 "value".into(),
53 JsonValue::Number(serde_json::Number::from_f64(*f).unwrap()),
54 );
55 }
56 Value::String(s) => {
57 obj.insert("type".into(), JsonValue::String("string".into()));
58 obj.insert("value".into(), JsonValue::String(s.to_string()));
59 }
60 Value::Bytes(bytes) => {
61 obj.insert("type".into(), JsonValue::String("bytes".into()));
62 obj.insert("value".into(), JsonValue::String(B64.encode(bytes)));
63 }
64 Value::List(items) => {
65 obj.insert("type".into(), JsonValue::String("list".into()));
66 let mut arr = Vec::new();
67 for v in items {
68 arr.push(encode_value(v)?);
69 }
70 obj.insert("value".into(), JsonValue::Array(arr));
71 }
72 Value::Map(entries) => {
73 obj.insert("type".into(), JsonValue::String("map".into()));
74 let mut arr = Vec::new();
75 for (k, v) in entries {
76 arr.push(JsonValue::Array(vec![encode_value(k)?, encode_value(v)?]));
77 }
78 obj.insert("value".into(), JsonValue::Array(arr));
79 }
80 Value::Tuple(items) => {
81 obj.insert("type".into(), JsonValue::String("tuple".into()));
82 let mut arr = Vec::new();
83 for v in items {
84 arr.push(encode_value(v)?);
85 }
86 obj.insert("value".into(), JsonValue::Array(arr));
87 }
88 Value::Struct(fields) => {
89 obj.insert("type".into(), JsonValue::String("struct".into()));
90 let mut props = serde_json::Map::new();
91 let mut sorted = fields.clone();
92 sorted.sort_by(|a, b| a.name.cmp(&b.name));
93 for StructFieldValue { name, value } in sorted {
94 props.insert(name, encode_value(&value)?);
95 }
96 obj.insert("value".into(), JsonValue::Object(props));
97 }
98 Value::Enum(ev) => {
99 obj.insert("type".into(), JsonValue::String("enum".into()));
100 let mut inner = serde_json::Map::new();
101 inner.insert("name".into(), JsonValue::String(ev.name.clone()));
102 if let Some(v) = &ev.value {
103 inner.insert("value".into(), encode_value(v)?);
104 }
105 obj.insert("value".into(), JsonValue::Object(inner));
106 }
107 }
108 Ok(JsonValue::Object(obj))
109}
110
111pub fn decode_value(value: &JsonValue) -> DataResult<Value> {
113 let obj = value.as_object().ok_or_else(|| {
114 DataError::new(
115 DataErrorCode::Serialization,
116 "expected object with fields `type` and optional `value`",
117 )
118 })?;
119 let ty = obj.get("type").and_then(|v| v.as_str()).ok_or_else(|| {
120 DataError::new(
121 DataErrorCode::Serialization,
122 "missing or invalid `type` field",
123 )
124 })?;
125 match ty {
126 "unit" => Ok(Value::Unit),
127 "bool" => obj
128 .get("value")
129 .and_then(|v| v.as_bool())
130 .map(Value::Bool)
131 .ok_or_else(|| {
132 DataError::new(DataErrorCode::Serialization, "expected boolean in `value`")
133 }),
134 "int" => obj
135 .get("value")
136 .and_then(|v| v.as_i64())
137 .map(Value::Int)
138 .ok_or_else(|| {
139 DataError::new(DataErrorCode::Serialization, "expected integer in `value`")
140 }),
141 "float" => {
142 let f = obj.get("value").and_then(|v| v.as_f64()).ok_or_else(|| {
143 DataError::new(DataErrorCode::Serialization, "expected number in `value`")
144 })?;
145 if !f.is_finite() {
146 return Err(DataError::new(
147 DataErrorCode::Serialization,
148 "non-finite floats are not supported in JSON",
149 ));
150 }
151 Ok(Value::Float(f))
152 }
153 "string" => obj
154 .get("value")
155 .and_then(|v| v.as_str())
156 .map(|s| Value::String(Cow::Owned(s.to_string())))
157 .ok_or_else(|| {
158 DataError::new(DataErrorCode::Serialization, "expected string in `value`")
159 }),
160 "bytes" => {
161 let s = obj.get("value").and_then(|v| v.as_str()).ok_or_else(|| {
162 DataError::new(
163 DataErrorCode::Serialization,
164 "expected base64 string in `value`",
165 )
166 })?;
167 let decoded = B64.decode(s.as_bytes()).map_err(|e| {
168 DataError::new(
169 DataErrorCode::Serialization,
170 format!("invalid base64 for bytes: {e}"),
171 )
172 })?;
173 Ok(Value::Bytes(Cow::Owned(decoded)))
174 }
175 "list" => {
176 let arr = obj.get("value").and_then(|v| v.as_array()).ok_or_else(|| {
177 DataError::new(DataErrorCode::Serialization, "expected array in `value`")
178 })?;
179 let mut items = Vec::new();
180 for v in arr {
181 items.push(decode_value(v)?);
182 }
183 Ok(Value::List(items))
184 }
185 "map" => {
186 let arr = obj.get("value").and_then(|v| v.as_array()).ok_or_else(|| {
187 DataError::new(
188 DataErrorCode::Serialization,
189 "expected array of pairs in `value`",
190 )
191 })?;
192 let mut entries = Vec::new();
193 for pair in arr {
194 let elems = pair.as_array().ok_or_else(|| {
195 DataError::new(
196 DataErrorCode::Serialization,
197 "expected array pair in map entry",
198 )
199 })?;
200 if elems.len() != 2 {
201 return Err(DataError::new(
202 DataErrorCode::Serialization,
203 "map entry must have two elements",
204 ));
205 }
206 let key = decode_value(&elems[0])?;
207 let val = decode_value(&elems[1])?;
208 entries.push((key, val));
209 }
210 Ok(Value::Map(entries))
211 }
212 "tuple" => {
213 let arr = obj.get("value").and_then(|v| v.as_array()).ok_or_else(|| {
214 DataError::new(DataErrorCode::Serialization, "expected array in `value`")
215 })?;
216 let mut items = Vec::new();
217 for v in arr {
218 items.push(decode_value(v)?);
219 }
220 Ok(Value::Tuple(items))
221 }
222 "struct" => {
223 let m = obj
224 .get("value")
225 .and_then(|v| v.as_object())
226 .ok_or_else(|| {
227 DataError::new(DataErrorCode::Serialization, "expected object in `value`")
228 })?;
229 let mut fields = Vec::new();
230 for (name, val) in m {
231 fields.push(StructFieldValue {
232 name: name.clone(),
233 value: decode_value(val)?,
234 });
235 }
236 fields.sort_by(|a, b| a.name.cmp(&b.name));
237 Ok(Value::Struct(fields))
238 }
239 "enum" => {
240 let m = obj
241 .get("value")
242 .and_then(|v| v.as_object())
243 .ok_or_else(|| {
244 DataError::new(DataErrorCode::Serialization, "expected object in `value`")
245 })?;
246 let name = m
247 .get("name")
248 .and_then(|v| v.as_str())
249 .ok_or_else(|| DataError::new(DataErrorCode::Serialization, "missing enum name"))?;
250 let val = if let Some(v) = m.get("value") {
251 Some(Box::new(decode_value(v)?))
252 } else {
253 None
254 };
255 Ok(Value::Enum(EnumValue {
256 name: name.to_string(),
257 value: val,
258 }))
259 }
260 other => Err(DataError::new(
261 DataErrorCode::Serialization,
262 format!("unknown value type tag `{other}`"),
263 )),
264 }
265}
266
267#[cfg(test)]
268mod tests {
269 use super::*;
270
271 #[test]
272 fn round_trip_value() {
273 let v = Value::String("hi".into());
274 let json = to_json(&v).expect("json");
275 let back = from_json(&json).expect("back");
276 assert_eq!(v, back);
277 }
278
279 #[test]
280 fn round_trip_bytes() {
281 let v = Value::Bytes(vec![1, 2, 3, 42].into());
282 let json = to_json(&v).expect("json");
283 let back = from_json(&json).expect("back");
284 assert_eq!(v, back);
285 assert!(json.contains("AQIDKg"));
286 }
287
288 #[test]
289 fn errors_on_invalid_json() {
290 let err = from_json("not-json").unwrap_err();
291 assert_eq!(err.code(), DataErrorCode::Serialization);
292 }
293
294 #[test]
295 fn errors_on_wrong_type_tag() {
296 let err = decode_value(&serde_json::json!({"type":"nope","value":1})).unwrap_err();
297 assert_eq!(err.code(), DataErrorCode::Serialization);
298 }
299
300 #[test]
301 fn rejects_non_finite_float() {
302 let err = to_json(&Value::Float(f64::INFINITY)).unwrap_err();
303 assert_eq!(err.code(), DataErrorCode::Serialization);
304 }
305
306 #[test]
307 fn rejects_bad_base64() {
308 let err = decode_value(&serde_json::json!({"type":"bytes","value":"!!!"})).unwrap_err();
309 assert_eq!(err.code(), DataErrorCode::Serialization);
310 }
311
312 #[test]
313 fn deterministic_field_order() {
314 let json = to_json(&Value::Int(5)).unwrap();
315 assert!(json.find("\"type\"").unwrap() < json.find("\"value\"").unwrap());
317 }
318
319 #[test]
320 fn round_trip_struct_and_enum() {
321 let v = Value::Struct(vec![
322 StructFieldValue {
323 name: "a".into(),
324 value: Value::Int(1),
325 },
326 StructFieldValue {
327 name: "b".into(),
328 value: Value::Bool(true),
329 },
330 ]);
331 let json = to_json(&v).unwrap();
332 let back = from_json(&json).unwrap();
333 assert_eq!(back, v);
334
335 let e = Value::Enum(EnumValue {
336 name: "ok".into(),
337 value: Some(Box::new(Value::Int(2))),
338 });
339 let json = to_json(&e).unwrap();
340 let back = from_json(&json).unwrap();
341 assert_eq!(back, e);
342 }
343}