gluesql_core/data/value/
json.rs

1use {
2    super::{Value, ValueError},
3    crate::result::{Error, Result},
4    chrono::{TimeZone, offset::Utc},
5    core::str::FromStr,
6    serde_json::{Map as JsonMap, Number as JsonNumber, Value as JsonValue},
7    std::collections::BTreeMap,
8    uuid::Uuid,
9};
10
11pub trait BTreeMapJsonExt {
12    fn parse_json_object(value: &str) -> Result<BTreeMap<String, Value>>;
13
14    fn try_from_json_map(json_map: JsonMap<String, JsonValue>) -> Result<BTreeMap<String, Value>>;
15}
16
17impl BTreeMapJsonExt for BTreeMap<String, Value> {
18    fn parse_json_object(value: &str) -> Result<BTreeMap<String, Value>> {
19        let value = serde_json::from_str(value)
20            .map_err(|_| ValueError::InvalidJsonString(value.to_owned()))?;
21
22        match value {
23            JsonValue::Object(json_map) => BTreeMap::try_from_json_map(json_map),
24            _ => Err(ValueError::JsonObjectTypeRequired.into()),
25        }
26    }
27
28    fn try_from_json_map(json_map: JsonMap<String, JsonValue>) -> Result<BTreeMap<String, Value>> {
29        json_map
30            .into_iter()
31            .map(|(key, value)| value.try_into().map(|value| (key, value)))
32            .collect::<Result<BTreeMap<String, Value>>>()
33    }
34}
35
36impl Value {
37    pub fn parse_json_map(value: &str) -> Result<Value> {
38        BTreeMap::parse_json_object(value).map(Value::Map)
39    }
40
41    pub fn parse_json_list(value: &str) -> Result<Value> {
42        let value = serde_json::from_str(value)
43            .map_err(|_| ValueError::InvalidJsonString(value.to_owned()))?;
44
45        if !matches!(value, JsonValue::Array(_)) {
46            return Err(ValueError::JsonArrayTypeRequired.into());
47        }
48
49        value.try_into()
50    }
51}
52
53impl TryFrom<Value> for JsonValue {
54    type Error = Error;
55
56    fn try_from(value: Value) -> Result<Self> {
57        match value {
58            Value::Bool(v) => Ok(JsonValue::Bool(v)),
59            Value::I8(v) => Ok(v.into()),
60            Value::I16(v) => Ok(v.into()),
61            Value::I32(v) => Ok(v.into()),
62            Value::I64(v) => Ok(v.into()),
63            Value::I128(v) => JsonNumber::from_str(&v.to_string())
64                .map(JsonValue::Number)
65                .map_err(|_| ValueError::UnreachableJsonNumberParseFailure(v.to_string()).into()),
66            Value::U8(v) => Ok(v.into()),
67            Value::U16(v) => Ok(v.into()),
68            Value::U32(v) => Ok(v.into()),
69            Value::U64(v) => Ok(v.into()),
70            Value::U128(v) => JsonNumber::from_str(&v.to_string())
71                .map(JsonValue::Number)
72                .map_err(|_| ValueError::UnreachableJsonNumberParseFailure(v.to_string()).into()),
73            Value::F32(v) => Ok(v.into()),
74            Value::F64(v) => Ok(v.into()),
75            Value::Decimal(v) => JsonNumber::from_str(&v.to_string())
76                .map(JsonValue::Number)
77                .map_err(|_| ValueError::UnreachableJsonNumberParseFailure(v.to_string()).into()),
78            Value::Str(v) => Ok(v.into()),
79            Value::Bytea(v) => Ok(hex::encode(v).into()),
80            Value::Inet(v) => Ok(v.to_string().into()),
81            Value::Date(v) => Ok(v.to_string().into()),
82            Value::Timestamp(v) => Ok(Utc.from_utc_datetime(&v).to_string().into()),
83            Value::Time(v) => Ok(v.to_string().into()),
84            Value::Interval(v) => Ok(v.to_sql_str().into()),
85            Value::Uuid(v) => Ok(Uuid::from_u128(v).hyphenated().to_string().into()),
86            Value::Map(v) => v
87                .into_iter()
88                .map(|(key, value)| value.try_into().map(|value| (key, value)))
89                .collect::<Result<Vec<(String, JsonValue)>>>()
90                .map(|v| JsonMap::from_iter(v).into()),
91            Value::List(v) => v
92                .into_iter()
93                .map(TryInto::try_into)
94                .collect::<Result<Vec<JsonValue>>>()
95                .map(Into::into),
96            Value::Point(v) => Ok(v.to_string().into()),
97            Value::Null => Ok(JsonValue::Null),
98        }
99    }
100}
101
102impl TryFrom<JsonValue> for Value {
103    type Error = Error;
104
105    fn try_from(json_value: JsonValue) -> Result<Self> {
106        match json_value {
107            JsonValue::Null => Ok(Value::Null),
108            JsonValue::Bool(v) => Ok(Value::Bool(v)),
109            JsonValue::Number(v) => {
110                if let Some(value) = v.as_i64().map(Value::I64) {
111                    return Ok(value);
112                }
113
114                v.as_f64().map(Value::F64).ok_or_else(|| {
115                    ValueError::UnreachableJsonNumberParseFailure(v.to_string()).into()
116                })
117            }
118            JsonValue::String(v) => Ok(Value::Str(v)),
119            JsonValue::Array(json_array) => json_array
120                .into_iter()
121                .map(Value::try_from)
122                .collect::<Result<Vec<Value>>>()
123                .map(Value::List),
124            JsonValue::Object(json_map) => json_map
125                .into_iter()
126                .map(|(key, value)| value.try_into().map(|value| (key, value)))
127                .collect::<Result<BTreeMap<String, Value>>>()
128                .map(Value::Map),
129        }
130    }
131}
132
133#[cfg(test)]
134mod tests {
135    use {
136        crate::data::{Interval, Point, Value, ValueError, value::uuid::parse_uuid},
137        chrono::{NaiveDate, NaiveTime},
138        rust_decimal::Decimal,
139        serde_json::{Number as JsonNumber, Value as JsonValue, json},
140        std::{net::IpAddr, str::FromStr},
141    };
142
143    #[test]
144    fn parse_json() {
145        assert_eq!(
146            Value::parse_json_map("[1, 2, 3]"),
147            Err(ValueError::JsonObjectTypeRequired.into())
148        );
149        assert_eq!(
150            Value::parse_json_list(r#"{ "a": 30 }"#),
151            Err(ValueError::JsonArrayTypeRequired.into())
152        );
153    }
154
155    #[test]
156    fn value_to_json() {
157        assert_eq!(Value::Bool(true).try_into(), Ok(JsonValue::Bool(true)));
158        assert_eq!(Value::I8(16).try_into(), Ok(JsonValue::Number(16.into())));
159        assert_eq!(
160            Value::I16(100).try_into(),
161            Ok(JsonValue::Number(100.into()))
162        );
163        assert_eq!(
164            Value::I32(100).try_into(),
165            Ok(JsonValue::Number(100.into()))
166        );
167        assert_eq!(
168            Value::I64(100).try_into(),
169            Ok(JsonValue::Number(100.into()))
170        );
171        assert_eq!(
172            Value::I128(100).try_into(),
173            Ok(JsonValue::Number(100.into()))
174        );
175        assert_eq!(Value::U8(100).try_into(), Ok(JsonValue::Number(100.into())));
176        assert_eq!(
177            Value::U16(100).try_into(),
178            Ok(JsonValue::Number(100.into()))
179        );
180        assert_eq!(
181            Value::U32(100).try_into(),
182            Ok(JsonValue::Number(100.into()))
183        );
184        assert_eq!(
185            Value::U64(100).try_into(),
186            Ok(JsonValue::Number(100.into()))
187        );
188        assert_eq!(
189            Value::U128(100).try_into(),
190            Ok(JsonValue::Number(100.into()))
191        );
192        assert!(JsonValue::try_from(Value::I128(i128::MAX)).is_ok());
193
194        assert_eq!(
195            Value::F32(1.23_f32).try_into(),
196            Ok(JsonValue::Number(
197                JsonNumber::from_f64(f64::from(1.23_f32)).unwrap()
198            ))
199        );
200        assert_eq!(
201            Value::F64(1.23).try_into(),
202            Ok(JsonValue::Number(JsonNumber::from_f64(1.23).unwrap()))
203        );
204        assert_eq!(
205            Value::Decimal(Decimal::ONE).try_into(),
206            Ok(JsonValue::Number(1.into()))
207        );
208        assert_eq!(
209            Value::Str("abc".to_owned()).try_into(),
210            Ok(JsonValue::String("abc".to_owned()))
211        );
212        assert_eq!(
213            Value::Bytea(hex::decode("a1b2").unwrap()).try_into(),
214            Ok(JsonValue::String("a1b2".to_owned()))
215        );
216        assert_eq!(
217            Value::Inet(IpAddr::from_str("::1").unwrap()).try_into(),
218            Ok(JsonValue::String("::1".to_owned()))
219        );
220        assert_eq!(
221            Value::Date(NaiveDate::from_ymd_opt(2020, 1, 3).unwrap()).try_into(),
222            Ok(JsonValue::String("2020-01-03".to_owned()))
223        );
224        assert_eq!(
225            Value::Timestamp(
226                NaiveDate::from_ymd_opt(2022, 6, 11)
227                    .unwrap()
228                    .and_hms_opt(13, 30, 1)
229                    .unwrap()
230            )
231            .try_into(),
232            Ok(JsonValue::String("2022-06-11 13:30:01 UTC".to_owned()))
233        );
234        assert_eq!(
235            Value::Time(NaiveTime::from_hms_opt(20, 11, 59).unwrap()).try_into(),
236            Ok(JsonValue::String("20:11:59".to_owned()))
237        );
238        assert_eq!(
239            Value::Interval(Interval::Month(17)).try_into(),
240            Ok(JsonValue::String("'1-5' YEAR TO MONTH".to_owned()))
241        );
242
243        let uuid = "43185717-59af-4e2b-9cd3-3264bf3691a4";
244        assert_eq!(
245            Value::Uuid(parse_uuid(uuid).unwrap()).try_into(),
246            Ok(JsonValue::String(uuid.to_owned()))
247        );
248
249        assert_eq!(
250            Value::parse_json_map(r#"{ "a": 10, "b": { "c": true, "d": "hello" }}"#)
251                .unwrap()
252                .try_into(),
253            Ok(json!({
254                "a": 10,
255                "b": {
256                    "c": true,
257                    "d": "hello",
258                }
259            }))
260        );
261        assert_eq!(
262            Value::parse_json_list(r#"[1, 2, { "a": 3 }]"#)
263                .unwrap()
264                .try_into(),
265            Ok(json!([1, 2, { "a": 3 }]))
266        );
267        assert_eq!(
268            Value::Point(Point::new(0.34, 0.56)).try_into(),
269            Ok(JsonValue::String("POINT(0.34 0.56)".to_owned()))
270        );
271        assert_eq!(Value::Null.try_into(), Ok(JsonValue::Null));
272    }
273
274    #[test]
275    fn json_to_value() {
276        use utils::Tribool::True;
277
278        assert!(Value::try_from(JsonValue::Null).unwrap().is_null());
279        assert_eq!(
280            True,
281            Value::try_from(JsonValue::Bool(false))
282                .unwrap()
283                .evaluate_eq(&Value::Bool(false))
284        );
285        assert_eq!(
286            True,
287            Value::try_from(JsonValue::Number(54321.into()))
288                .unwrap()
289                .evaluate_eq(&Value::I32(54321))
290        );
291        assert_eq!(
292            True,
293            Value::try_from(JsonValue::Number(54321.into()))
294                .unwrap()
295                .evaluate_eq(&Value::I64(54321))
296        );
297        assert_eq!(
298            True,
299            Value::try_from(JsonValue::Number(54321.into()))
300                .unwrap()
301                .evaluate_eq(&Value::I128(54321))
302        );
303        assert_eq!(
304            True,
305            Value::try_from(JsonValue::Number(JsonNumber::from_f64(3.21).unwrap()))
306                .unwrap()
307                .evaluate_eq(&Value::F64(3.21))
308        );
309        assert_eq!(
310            True,
311            Value::try_from(JsonValue::String("world".to_owned()))
312                .unwrap()
313                .evaluate_eq(&Value::Str("world".to_owned()))
314        );
315        assert_eq!(
316            True,
317            Value::try_from(JsonValue::Array(vec![JsonValue::Bool(true)]))
318                .unwrap()
319                .evaluate_eq(&Value::List(vec![Value::Bool(true)]))
320        );
321        assert_eq!(
322            True,
323            Value::try_from(json!({ "a": true }))
324                .unwrap()
325                .evaluate_eq(&Value::Map(
326                    [("a".to_owned(), Value::Bool(true))].into_iter().collect()
327                ))
328        );
329    }
330}