1use core::fmt;
10
11use intern_lang::Symbol;
12use serde::de::{self, Deserializer};
13use serde::ser::Serializer;
14use serde::{Deserialize, Serialize};
15
16use crate::{Unpacked, Value};
17
18#[derive(Serialize, Deserialize)]
20enum Repr {
21 Nil,
22 Bool(bool),
23 Int(i32),
24 Float(f64),
25 Sym(u32),
26}
27
28impl Serialize for Value {
29 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
30 where
31 S: Serializer,
32 {
33 let repr = match self.unpack() {
34 Unpacked::Nil => Repr::Nil,
35 Unpacked::Bool(b) => Repr::Bool(b),
36 Unpacked::Int(n) => Repr::Int(n),
37 Unpacked::Float(f) => Repr::Float(f),
38 Unpacked::Sym(s) => Repr::Sym(s.as_u32()),
39 };
40 repr.serialize(serializer)
41 }
42}
43
44impl<'de> Deserialize<'de> for Value {
45 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
46 where
47 D: Deserializer<'de>,
48 {
49 let value = match Repr::deserialize(deserializer)? {
50 Repr::Nil => Value::nil(),
51 Repr::Bool(b) => Value::bool(b),
52 Repr::Int(n) => Value::int(n),
53 Repr::Float(f) => Value::float(f),
54 Repr::Sym(id) => {
55 let symbol = Symbol::from_u32(id).ok_or_else(|| de::Error::custom(ZeroSymbol))?;
56 Value::sym(symbol)
57 }
58 };
59 Ok(value)
60 }
61}
62
63struct ZeroSymbol;
65
66impl fmt::Display for ZeroSymbol {
67 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68 f.write_str("invalid symbol id 0")
69 }
70}
71
72#[cfg(test)]
73mod tests {
74 #![allow(clippy::unwrap_used)]
75
76 use crate::Value;
77 use intern_lang::Symbol;
78
79 #[test]
80 fn test_json_roundtrip_every_kind() {
81 let s = Symbol::from_u32(11).unwrap();
82 for v in [
83 Value::nil(),
84 Value::bool(true),
85 Value::int(-5),
86 Value::float(2.5),
87 Value::sym(s),
88 ] {
89 let json = serde_json::to_string(&v).unwrap();
90 let back: Value = serde_json::from_str(&json).unwrap();
91 assert_eq!(back, v);
92 }
93 }
94
95 #[test]
96 fn test_symbol_serializes_as_raw_id() {
97 let s = Symbol::from_u32(42).unwrap();
98 let json = serde_json::to_string(&Value::sym(s)).unwrap();
99 assert_eq!(json, r#"{"Sym":42}"#);
100 }
101
102 #[test]
103 fn test_zero_symbol_id_is_rejected() {
104 let err = serde_json::from_str::<Value>(r#"{"Sym":0}"#);
105 assert!(err.is_err());
106 }
107}