typedb_driver/answer/
json.rs

1/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements.  See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership.  The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License.  You may obtain a copy of the License at
9 *
10 *   http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied.  See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20use std::{
21    borrow::Cow,
22    collections::HashMap,
23    fmt::{self, Write},
24};
25
26#[derive(Clone, Debug)]
27pub enum JSON {
28    Object(HashMap<Cow<'static, str>, JSON>),
29    Array(Vec<JSON>),
30    String(Cow<'static, str>),
31    Number(f64),
32    Boolean(bool),
33    Null,
34}
35
36impl fmt::Display for JSON {
37    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38        match self {
39            JSON::Object(object) => {
40                f.write_char('{')?;
41                for (i, (k, v)) in object.iter().enumerate() {
42                    if i > 0 {
43                        f.write_str(", ")?;
44                    }
45                    write!(f, r#""{}": {}"#, k, v)?;
46                }
47                f.write_char('}')?;
48            }
49            JSON::Array(list) => {
50                f.write_char('[')?;
51                for (i, v) in list.iter().enumerate() {
52                    if i > 0 {
53                        f.write_str(", ")?;
54                    }
55                    write!(f, "{}", v)?;
56                }
57                f.write_char(']')?;
58            }
59            JSON::String(string) => write_escaped_string(string, f)?,
60            JSON::Number(number) => write!(f, "{number}")?,
61            JSON::Boolean(boolean) => write!(f, "{boolean}")?,
62            JSON::Null => write!(f, "null")?,
63        }
64        Ok(())
65    }
66}
67
68fn write_escaped_string(string: &str, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69    const HEX: u8 = 0;
70    const BSP: u8 = b'b';
71    const TAB: u8 = b't';
72    const LF_: u8 = b'n';
73    const FF_: u8 = b'f';
74    const CR_: u8 = b'r';
75
76    const ASCII_CONTROL: usize = 0x20;
77
78    const ESCAPE: [u8; ASCII_CONTROL] = [
79        HEX, HEX, HEX, HEX, HEX, HEX, HEX, HEX, //
80        BSP, TAB, LF_, HEX, FF_, CR_, HEX, HEX, //
81        HEX, HEX, HEX, HEX, HEX, HEX, HEX, HEX, //
82        HEX, HEX, HEX, HEX, HEX, HEX, HEX, HEX, //
83    ];
84
85    const HEX_DIGITS: &[u8; 0x10] = b"0123456789abcdef";
86
87    let mut buf = Vec::with_capacity(string.len());
88
89    for byte in string.bytes() {
90        if (byte as usize) < ASCII_CONTROL {
91            match ESCAPE[byte as usize] {
92                HEX => {
93                    buf.extend_from_slice(&[
94                        b'\\',
95                        b'u',
96                        b'0',
97                        b'0',
98                        HEX_DIGITS[(byte as usize & 0xF0) >> 4],
99                        HEX_DIGITS[byte as usize & 0x0F],
100                    ]);
101                }
102                special => buf.extend_from_slice(&[b'\\', special]),
103            }
104        } else {
105            match byte {
106                b'"' | b'\\' => buf.extend_from_slice(&[b'\\', byte]),
107                _ => buf.push(byte),
108            }
109        }
110    }
111
112    write!(f, r#""{}""#, unsafe { String::from_utf8_unchecked(buf) })
113}
114
115#[cfg(test)]
116mod test {
117    use std::borrow::Cow;
118
119    use super::JSON;
120
121    #[test]
122    fn test_against_serde() {
123        let string: String =
124            (0u8..0x7fu8).map(|byte| byte as char).chain("lorem ипсум どぉる سيتامعت".chars()).collect();
125        let serde_json_value = serde_json::value::Value::String(string.clone());
126        let json_string = JSON::String(Cow::Owned(string));
127        assert_eq!(serde_json::to_string(&serde_json_value).unwrap(), json_string.to_string());
128    }
129}