1use std::collections::BTreeMap;
5
6use nuvec_core::{Error, Result};
7
8#[derive(Debug, Clone, PartialEq)]
10pub enum Json {
11 Null,
13 Bool(bool),
15 Int(i64),
17 Float(f64),
19 Str(String),
21 Array(Vec<Json>),
23 Object(Vec<(String, Json)>),
25}
26
27impl Json {
28 pub fn get(&self, key: &str) -> Option<&Json> {
30 match self {
31 Self::Object(items) => items.iter().find(|(k, _)| k == key).map(|(_, v)| v),
32 _ => None,
33 }
34 }
35
36 pub fn as_str(&self) -> Option<&str> {
38 match self {
39 Self::Str(s) => Some(s),
40 _ => None,
41 }
42 }
43
44 pub fn as_i64(&self) -> Option<i64> {
46 match self {
47 Self::Int(i) => Some(*i),
48 _ => None,
49 }
50 }
51
52 pub fn as_f64(&self) -> Option<f64> {
54 match self {
55 Self::Int(i) => Some(*i as f64),
56 Self::Float(f) => Some(*f),
57 _ => None,
58 }
59 }
60
61 pub fn as_bool(&self) -> Option<bool> {
63 match self {
64 Self::Bool(b) => Some(*b),
65 _ => None,
66 }
67 }
68
69 pub fn as_array(&self) -> Option<&[Json]> {
71 match self {
72 Self::Array(items) => Some(items),
73 _ => None,
74 }
75 }
76
77 pub fn as_object(&self) -> Option<&[(String, Json)]> {
79 match self {
80 Self::Object(items) => Some(items),
81 _ => None,
82 }
83 }
84}
85
86fn invalid(msg: &str, at: usize) -> Error {
87 Error::InvalidArgument(format!("JSON: {msg} at byte {at}"))
88}
89
90struct Parser<'a> {
91 s: &'a [u8],
92 i: usize,
93}
94
95impl Parser<'_> {
96 fn ws(&mut self) {
97 while self.s.get(self.i).is_some_and(u8::is_ascii_whitespace) {
98 self.i += 1;
99 }
100 }
101
102 fn eat(&mut self, c: u8) -> Result<()> {
103 self.ws();
104 if self.s.get(self.i) == Some(&c) {
105 self.i += 1;
106 Ok(())
107 } else {
108 Err(invalid(&format!("expected `{}`", c as char), self.i))
109 }
110 }
111
112 fn value(&mut self) -> Result<Json> {
113 self.ws();
114 match self.s.get(self.i) {
115 Some(b'{') => {
116 self.i += 1;
117 let mut items = Vec::new();
118 self.ws();
119 if self.s.get(self.i) == Some(&b'}') {
120 self.i += 1;
121 return Ok(Json::Object(items));
122 }
123 loop {
124 self.ws();
125 let key = self.string()?;
126 self.eat(b':')?;
127 items.push((key, self.value()?));
128 self.ws();
129 match self.s.get(self.i) {
130 Some(b',') => self.i += 1,
131 Some(b'}') => {
132 self.i += 1;
133 return Ok(Json::Object(items));
134 }
135 _ => return Err(invalid("expected `,` or `}`", self.i)),
136 }
137 }
138 }
139 Some(b'[') => {
140 self.i += 1;
141 let mut items = Vec::new();
142 self.ws();
143 if self.s.get(self.i) == Some(&b']') {
144 self.i += 1;
145 return Ok(Json::Array(items));
146 }
147 loop {
148 items.push(self.value()?);
149 self.ws();
150 match self.s.get(self.i) {
151 Some(b',') => self.i += 1,
152 Some(b']') => {
153 self.i += 1;
154 return Ok(Json::Array(items));
155 }
156 _ => return Err(invalid("expected `,` or `]`", self.i)),
157 }
158 }
159 }
160 Some(b'"') => Ok(Json::Str(self.string()?)),
161 Some(b't') if self.s[self.i..].starts_with(b"true") => {
162 self.i += 4;
163 Ok(Json::Bool(true))
164 }
165 Some(b'f') if self.s[self.i..].starts_with(b"false") => {
166 self.i += 5;
167 Ok(Json::Bool(false))
168 }
169 Some(b'n') if self.s[self.i..].starts_with(b"null") => {
170 self.i += 4;
171 Ok(Json::Null)
172 }
173 Some(c) if *c == b'-' || c.is_ascii_digit() => {
174 let start = self.i;
175 self.i += 1;
176 while self
177 .s
178 .get(self.i)
179 .is_some_and(|c| c.is_ascii_digit() || b".eE+-".contains(c))
180 {
181 self.i += 1;
182 }
183 let text = std::str::from_utf8(&self.s[start..self.i]).unwrap_or("");
184 text.parse()
185 .map(Json::Int)
186 .or_else(|_| text.parse().map(Json::Float))
187 .map_err(|_| invalid("number expected", start))
188 }
189 _ => Err(invalid("value expected", self.i)),
190 }
191 }
192
193 fn string(&mut self) -> Result<String> {
194 if self.s.get(self.i) != Some(&b'"') {
195 return Err(invalid("string expected", self.i));
196 }
197 self.i += 1;
198 let mut out = String::new();
199 loop {
200 let start = self.i;
201 while self.s.get(self.i).is_some_and(|&c| c != b'"' && c != b'\\') {
202 self.i += 1;
203 }
204 out.push_str(
205 std::str::from_utf8(&self.s[start..self.i])
206 .map_err(|_| invalid("invalid UTF-8", start))?,
207 );
208 match self.s.get(self.i) {
209 Some(b'"') => {
210 self.i += 1;
211 return Ok(out);
212 }
213 Some(b'\\') => {
214 let esc = *self
215 .s
216 .get(self.i + 1)
217 .ok_or_else(|| invalid("bad escape", self.i))?;
218 self.i += 2;
219 match esc {
220 b'"' => out.push('"'),
221 b'\\' => out.push('\\'),
222 b'/' => out.push('/'),
223 b'b' => out.push('\u{8}'),
224 b'f' => out.push('\u{c}'),
225 b'n' => out.push('\n'),
226 b'r' => out.push('\r'),
227 b't' => out.push('\t'),
228 b'u' => {
229 let mut code = self.hex4()?;
230 if (0xD800..0xDC00).contains(&code)
231 && self.s[self.i..].starts_with(b"\\u")
232 {
233 self.i += 2;
234 let low = self.hex4()?;
235 code = 0x10000
236 + ((code - 0xD800) << 10)
237 + (low.wrapping_sub(0xDC00) & 0x3FF);
238 }
239 out.push(char::from_u32(code).unwrap_or('\u{FFFD}'));
240 }
241 _ => return Err(invalid("bad escape", self.i - 1)),
242 }
243 }
244 _ => return Err(invalid("unterminated string", start)),
245 }
246 }
247 }
248
249 fn hex4(&mut self) -> Result<u32> {
250 let digits = self
251 .s
252 .get(self.i..self.i + 4)
253 .ok_or_else(|| invalid("bad \\u escape", self.i))?;
254 self.i += 4;
255 std::str::from_utf8(digits)
256 .ok()
257 .and_then(|d| u32::from_str_radix(d, 16).ok())
258 .ok_or_else(|| invalid("bad \\u escape", self.i - 4))
259 }
260}
261
262pub fn parse(bytes: &[u8]) -> Result<Json> {
264 let mut p = Parser { s: bytes, i: 0 };
265 let v = p.value()?;
266 p.ws();
267 if p.i != bytes.len() {
268 return Err(invalid("trailing data", p.i));
269 }
270 Ok(v)
271}
272
273pub(crate) fn quote(s: &str) -> String {
275 let mut out = String::with_capacity(s.len() + 2);
276 out.push('"');
277 for c in s.chars() {
278 match c {
279 '"' => out.push_str("\\\""),
280 '\\' => out.push_str("\\\\"),
281 '\n' => out.push_str("\\n"),
282 '\r' => out.push_str("\\r"),
283 '\t' => out.push_str("\\t"),
284 c if (c as u32) < 0x20 => out.push_str(&format!("\\u{:04x}", c as u32)),
285 c => out.push(c),
286 }
287 }
288 out.push('"');
289 out
290}
291
292pub(crate) fn object(map: &BTreeMap<String, String>) -> String {
294 let items: Vec<String> = map
295 .iter()
296 .map(|(k, v)| format!("{}:{}", quote(k), quote(v)))
297 .collect();
298 format!("{{{}}}", items.join(","))
299}