typedb_driver/answer/
json.rs1use 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, BSP, TAB, LF_, HEX, FF_, CR_, HEX, HEX, HEX, HEX, HEX, HEX, HEX, HEX, HEX, HEX, HEX, HEX, HEX, HEX, HEX, HEX, HEX, HEX, ];
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}