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
90const MAX_DEPTH: usize = 128;
93
94struct Parser<'a> {
95 s: &'a [u8],
96 i: usize,
97 depth: usize,
99}
100
101impl Parser<'_> {
102 fn ws(&mut self) {
103 while self.s.get(self.i).is_some_and(u8::is_ascii_whitespace) {
104 self.i += 1;
105 }
106 }
107
108 fn eat(&mut self, c: u8) -> Result<()> {
109 self.ws();
110 if self.s.get(self.i) == Some(&c) {
111 self.i += 1;
112 Ok(())
113 } else {
114 Err(invalid(&format!("expected `{}`", c as char), self.i))
115 }
116 }
117
118 fn open(&mut self) -> Result<()> {
120 if self.depth == MAX_DEPTH {
121 return Err(invalid("nesting too deep", self.i));
122 }
123 self.depth += 1;
124 self.i += 1;
125 Ok(())
126 }
127
128 fn value(&mut self) -> Result<Json> {
129 self.ws();
130 match self.s.get(self.i) {
131 Some(b'{') => {
132 self.open()?;
133 let mut items = Vec::new();
134 self.ws();
135 if self.s.get(self.i) == Some(&b'}') {
136 self.i += 1;
137 self.depth -= 1;
138 return Ok(Json::Object(items));
139 }
140 loop {
141 self.ws();
142 let key = self.string()?;
143 self.eat(b':')?;
144 items.push((key, self.value()?));
145 self.ws();
146 match self.s.get(self.i) {
147 Some(b',') => self.i += 1,
148 Some(b'}') => {
149 self.i += 1;
150 self.depth -= 1;
151 return Ok(Json::Object(items));
152 }
153 _ => return Err(invalid("expected `,` or `}`", self.i)),
154 }
155 }
156 }
157 Some(b'[') => {
158 self.open()?;
159 let mut items = Vec::new();
160 self.ws();
161 if self.s.get(self.i) == Some(&b']') {
162 self.i += 1;
163 self.depth -= 1;
164 return Ok(Json::Array(items));
165 }
166 loop {
167 items.push(self.value()?);
168 self.ws();
169 match self.s.get(self.i) {
170 Some(b',') => self.i += 1,
171 Some(b']') => {
172 self.i += 1;
173 self.depth -= 1;
174 return Ok(Json::Array(items));
175 }
176 _ => return Err(invalid("expected `,` or `]`", self.i)),
177 }
178 }
179 }
180 Some(b'"') => Ok(Json::Str(self.string()?)),
181 Some(b't') if self.s[self.i..].starts_with(b"true") => {
182 self.i += 4;
183 Ok(Json::Bool(true))
184 }
185 Some(b'f') if self.s[self.i..].starts_with(b"false") => {
186 self.i += 5;
187 Ok(Json::Bool(false))
188 }
189 Some(b'n') if self.s[self.i..].starts_with(b"null") => {
190 self.i += 4;
191 Ok(Json::Null)
192 }
193 Some(c) if *c == b'-' || c.is_ascii_digit() => self.number(),
194 _ => Err(invalid("value expected", self.i)),
195 }
196 }
197
198 fn digits(&mut self, start: usize) -> Result<usize> {
200 let from = self.i;
201 while self.s.get(self.i).is_some_and(u8::is_ascii_digit) {
202 self.i += 1;
203 }
204 match self.i - from {
205 0 => Err(invalid("number expected", start)),
206 n => Ok(n),
207 }
208 }
209
210 fn number(&mut self) -> Result<Json> {
212 let start = self.i;
213 if self.s.get(self.i) == Some(&b'-') {
214 self.i += 1;
215 }
216 let first = self.i;
217 if self.digits(start)? > 1 && self.s[first] == b'0' {
218 return Err(invalid("leading zero", start));
219 }
220 let mut integer = true;
221 if self.s.get(self.i) == Some(&b'.') {
222 self.i += 1;
223 self.digits(start)?;
224 integer = false;
225 }
226 if matches!(self.s.get(self.i), Some(b'e' | b'E')) {
227 self.i += 1;
228 if matches!(self.s.get(self.i), Some(b'+' | b'-')) {
229 self.i += 1;
230 }
231 self.digits(start)?;
232 integer = false;
233 }
234 let text = std::str::from_utf8(&self.s[start..self.i]).unwrap_or("");
236 match text.parse() {
237 Ok(n) if integer => Ok(Json::Int(n)),
238 _ => text
239 .parse()
240 .map(Json::Float)
241 .map_err(|_| invalid("number expected", start)),
242 }
243 }
244
245 fn string(&mut self) -> Result<String> {
246 if self.s.get(self.i) != Some(&b'"') {
247 return Err(invalid("string expected", self.i));
248 }
249 self.i += 1;
250 let mut out = String::new();
251 loop {
252 let start = self.i;
253 while self.s.get(self.i).is_some_and(|&c| c != b'"' && c != b'\\') {
254 self.i += 1;
255 }
256 out.push_str(
257 std::str::from_utf8(&self.s[start..self.i])
258 .map_err(|_| invalid("invalid UTF-8", start))?,
259 );
260 match self.s.get(self.i) {
261 Some(b'"') => {
262 self.i += 1;
263 return Ok(out);
264 }
265 Some(b'\\') => {
266 let esc = *self
267 .s
268 .get(self.i + 1)
269 .ok_or_else(|| invalid("bad escape", self.i))?;
270 self.i += 2;
271 match esc {
272 b'"' => out.push('"'),
273 b'\\' => out.push('\\'),
274 b'/' => out.push('/'),
275 b'b' => out.push('\u{8}'),
276 b'f' => out.push('\u{c}'),
277 b'n' => out.push('\n'),
278 b'r' => out.push('\r'),
279 b't' => out.push('\t'),
280 b'u' => {
281 let at = self.i - 2;
282 let mut code = self.hex4()?;
283 if (0xD800..0xDC00).contains(&code) {
286 if !self.s[self.i..].starts_with(b"\\u") {
287 return Err(invalid("lone surrogate", at));
288 }
289 self.i += 2;
290 let low = self.hex4()?;
291 if !(0xDC00..0xE000).contains(&low) {
292 return Err(invalid("invalid surrogate pair", at));
293 }
294 code = 0x10000 + ((code - 0xD800) << 10) + (low - 0xDC00);
295 }
296 out.push(
297 char::from_u32(code)
298 .ok_or_else(|| invalid("lone surrogate", at))?,
299 );
300 }
301 _ => return Err(invalid("bad escape", self.i - 1)),
302 }
303 }
304 _ => return Err(invalid("unterminated string", start)),
305 }
306 }
307 }
308
309 fn hex4(&mut self) -> Result<u32> {
310 let digits = self
311 .s
312 .get(self.i..self.i + 4)
313 .ok_or_else(|| invalid("bad \\u escape", self.i))?;
314 if !digits.iter().all(u8::is_ascii_hexdigit) {
316 return Err(invalid("bad \\u escape", self.i));
317 }
318 self.i += 4;
319 std::str::from_utf8(digits)
320 .ok()
321 .and_then(|d| u32::from_str_radix(d, 16).ok())
322 .ok_or_else(|| invalid("bad \\u escape", self.i - 4))
323 }
324}
325
326pub fn parse(bytes: &[u8]) -> Result<Json> {
332 let mut p = Parser {
333 s: bytes,
334 i: 0,
335 depth: 0,
336 };
337 let v = p.value()?;
338 p.ws();
339 if p.i != bytes.len() {
340 return Err(invalid("trailing data", p.i));
341 }
342 Ok(v)
343}
344
345pub(crate) fn quote(s: &str) -> String {
347 let mut out = String::with_capacity(s.len() + 2);
348 out.push('"');
349 for c in s.chars() {
350 match c {
351 '"' => out.push_str("\\\""),
352 '\\' => out.push_str("\\\\"),
353 '\n' => out.push_str("\\n"),
354 '\r' => out.push_str("\\r"),
355 '\t' => out.push_str("\\t"),
356 c if (c as u32) < 0x20 => out.push_str(&format!("\\u{:04x}", c as u32)),
357 c => out.push(c),
358 }
359 }
360 out.push('"');
361 out
362}
363
364pub(crate) fn object(map: &BTreeMap<String, String>) -> String {
366 let items: Vec<String> = map
367 .iter()
368 .map(|(k, v)| format!("{}:{}", quote(k), quote(v)))
369 .collect();
370 format!("{{{}}}", items.join(","))
371}