1#![forbid(unsafe_code)]
40#![deny(missing_docs)]
41
42use std::collections::BTreeMap;
43use std::fmt::Write as _;
44
45#[derive(Debug, Clone, PartialEq)]
47pub enum Json {
48 Null,
50 Bool(bool),
52 Number(f64),
54 String(String),
56 Array(Vec<Json>),
58 Object(BTreeMap<String, Json>),
60}
61
62impl Json {
63 #[must_use]
65 pub fn object(pairs: Vec<(&str, Self)>) -> Self {
66 Self::Object(
67 pairs.into_iter().map(|(k, v)| (k.to_owned(), v)).collect(),
68 )
69 }
70
71 #[must_use]
73 pub fn str(s: impl Into<String>) -> Self {
74 Self::String(s.into())
75 }
76
77 #[must_use]
79 pub fn get(&self, key: &str) -> Option<&Self> {
80 match self {
81 Self::Object(m) => m.get(key),
82 _ => None,
83 }
84 }
85
86 #[must_use]
88 pub fn as_str(&self) -> Option<&str> {
89 match self {
90 Self::String(s) => Some(s),
91 _ => None,
92 }
93 }
94
95 #[must_use]
97 pub fn to_json(&self) -> String {
98 let mut out = String::new();
99 self.write(&mut out);
100 out
101 }
102
103 fn write(&self, out: &mut String) {
104 match self {
105 Self::Null => out.push_str("null"),
106 Self::Bool(b) => {
107 out.push_str(if *b { "true" } else { "false" });
108 }
109 Self::Number(n) => {
110 #[allow(clippy::cast_possible_truncation)]
114 if n.fract() == 0.0 && n.abs() < 1e15 {
115 let _ = write!(out, "{}", *n as i64);
116 } else {
117 let _ = write!(out, "{n}");
118 }
119 }
120 Self::String(s) => write_string(s, out),
121 Self::Array(items) => {
122 out.push('[');
123 for (i, item) in items.iter().enumerate() {
124 if i > 0 {
125 out.push(',');
126 }
127 item.write(out);
128 }
129 out.push(']');
130 }
131 Self::Object(map) => {
132 out.push('{');
133 for (i, (k, v)) in map.iter().enumerate() {
134 if i > 0 {
135 out.push(',');
136 }
137 write_string(k, out);
138 out.push(':');
139 v.write(out);
140 }
141 out.push('}');
142 }
143 }
144 }
145}
146
147fn write_string(s: &str, out: &mut String) {
148 out.push('"');
149 for c in s.chars() {
150 match c {
151 '"' => out.push_str("\\\""),
152 '\\' => out.push_str("\\\\"),
153 '\n' => out.push_str("\\n"),
154 '\r' => out.push_str("\\r"),
155 '\t' => out.push_str("\\t"),
156 c if (c as u32) < 0x20 => {
159 let _ = write!(out, "\\u{:04x}", c as u32);
160 }
161 c => out.push(c),
162 }
163 }
164 out.push('"');
165}
166
167pub fn parse(input: &str) -> Result<Json, String> {
174 if input.len() > 16 * 1024 * 1024 {
175 return Err("JSON source limit exceeded (16 MiB)".to_owned());
176 }
177 let chars: Vec<char> = input.chars().collect();
178 let mut p = Parser {
179 chars: &chars,
180 pos: 0,
181 depth: 0,
182 };
183 p.ws();
184 let v = p.value()?;
185 p.ws();
186 if p.pos < chars.len() {
187 return Err("trailing input after JSON value".to_owned());
188 }
189 Ok(v)
190}
191
192struct Parser<'a> {
193 chars: &'a [char],
194 pos: usize,
195 depth: usize,
196}
197
198impl Parser<'_> {
199 fn ws(&mut self) {
200 while matches!(self.chars.get(self.pos), Some(' ' | '\t' | '\n' | '\r'))
201 {
202 self.pos += 1;
203 }
204 }
205
206 fn value(&mut self) -> Result<Json, String> {
207 self.ws();
208 let container = matches!(self.chars.get(self.pos), Some('{' | '['));
209 if container && self.depth >= 256 {
210 return Err("JSON nesting limit exceeded (256)".to_owned());
211 }
212 self.depth += usize::from(container);
213 let result = self.value_inner();
214 self.depth -= usize::from(container);
215 result
216 }
217
218 fn value_inner(&mut self) -> Result<Json, String> {
219 match self.chars.get(self.pos) {
220 Some('{') => self.object(),
221 Some('[') => self.array(),
222 Some('"') => Ok(Json::String(self.string()?)),
223 Some('t') => self.literal("true", Json::Bool(true)),
224 Some('f') => self.literal("false", Json::Bool(false)),
225 Some('n') => self.literal("null", Json::Null),
226 Some(_) => self.number(),
227 None => Err("unexpected end of JSON".to_owned()),
228 }
229 }
230
231 fn literal(&mut self, word: &str, v: Json) -> Result<Json, String> {
232 if self.chars[self.pos..]
233 .starts_with(word.chars().collect::<Vec<_>>().as_slice())
234 {
235 self.pos += word.len();
236 Ok(v)
237 } else {
238 Err(format!("expected `{word}`"))
239 }
240 }
241
242 fn object(&mut self) -> Result<Json, String> {
243 self.pos += 1;
244 let mut map = BTreeMap::new();
245 self.ws();
246 if self.chars.get(self.pos) == Some(&'}') {
247 self.pos += 1;
248 return Ok(Json::Object(map));
249 }
250 loop {
251 self.ws();
252 let key = self.string()?;
253 self.ws();
254 if self.chars.get(self.pos) != Some(&':') {
255 return Err("expected `:` in object".to_owned());
256 }
257 self.pos += 1;
258 let value = self.value()?;
259 let _ = map.insert(key, value);
260 self.ws();
261 match self.chars.get(self.pos) {
262 Some(',') => self.pos += 1,
263 Some('}') => {
264 self.pos += 1;
265 return Ok(Json::Object(map));
266 }
267 _ => return Err("expected `,` or `}`".to_owned()),
268 }
269 }
270 }
271
272 fn array(&mut self) -> Result<Json, String> {
273 self.pos += 1;
274 let mut items = Vec::new();
275 self.ws();
276 if self.chars.get(self.pos) == Some(&']') {
277 self.pos += 1;
278 return Ok(Json::Array(items));
279 }
280 loop {
281 items.push(self.value()?);
282 self.ws();
283 match self.chars.get(self.pos) {
284 Some(',') => self.pos += 1,
285 Some(']') => {
286 self.pos += 1;
287 return Ok(Json::Array(items));
288 }
289 _ => return Err("expected `,` or `]`".to_owned()),
290 }
291 }
292 }
293
294 fn string(&mut self) -> Result<String, String> {
295 if self.chars.get(self.pos) != Some(&'"') {
296 return Err("expected a string".to_owned());
297 }
298 self.pos += 1;
299 let mut out = String::new();
300 loop {
301 match self.chars.get(self.pos) {
302 None => return Err("unterminated string".to_owned()),
303 Some('"') => {
304 self.pos += 1;
305 return Ok(out);
306 }
307 Some('\\') => {
308 self.pos += 1;
309 out.push(self.escape()?);
310 }
311 Some(c) if *c < '\u{20}' => {
312 return Err("unescaped control in string".to_owned());
313 }
314 Some(c) => {
315 out.push(*c);
316 self.pos += 1;
317 }
318 }
319 }
320 }
321
322 fn escape(&mut self) -> Result<char, String> {
323 let esc = *self.chars.get(self.pos).ok_or("trailing backslash")?;
324 self.pos += 1;
325 match esc {
326 'u' => self.unicode_escape(),
327 _ => simple_escape(esc)
328 .ok_or_else(|| "invalid string escape".to_owned()),
329 }
330 }
331
332 fn unicode_escape(&mut self) -> Result<char, String> {
333 let cp = self.hex4()?;
334 let cp = if (0xD800..0xDC00).contains(&cp) {
339 if !matches!(
340 self.chars.get(self.pos..self.pos + 2),
341 Some(['\\', 'u'])
342 ) {
343 return Err("lone high surrogate".to_owned());
344 }
345 self.pos += 2;
346 let lo = self.hex4()?;
347 if !(0xDC00..0xE000).contains(&lo) {
348 return Err(
349 "high surrogate not followed by a low surrogate".to_owned()
350 );
351 }
352 0x1_0000 + ((cp - 0xD800) << 10) + (lo - 0xDC00)
353 } else if (0xDC00..0xE000).contains(&cp) {
354 return Err("lone low surrogate".to_owned());
355 } else {
356 cp
357 };
358 char::from_u32(cp).ok_or_else(|| "bad code point".to_owned())
359 }
360
361 fn hex4(&mut self) -> Result<u32, String> {
363 let hex: String = self
364 .chars
365 .get(self.pos..self.pos + 4)
366 .ok_or("truncated \\u escape")?
367 .iter()
368 .collect();
369 self.pos += 4;
370 u32::from_str_radix(&hex, 16).map_err(|_| "bad \\u escape".to_owned())
371 }
372
373 fn number(&mut self) -> Result<Json, String> {
374 let start = self.pos;
375 while matches!(
376 self.chars.get(self.pos),
377 Some(c) if c.is_ascii_digit()
378 || matches!(c, '-' | '+' | '.' | 'e' | 'E')
379 ) {
380 self.pos += 1;
381 }
382 let text: String = self.chars[start..self.pos].iter().collect();
383 if !valid_number(&text) {
384 return Err(format!("invalid number `{text}`"));
385 }
386 let value: f64 = text
387 .parse()
388 .map_err(|_| format!("invalid number `{text}`"))?;
389 if !value.is_finite() {
399 return Err(format!("number `{text}` is out of range for f64"));
400 }
401 Ok(Json::Number(value))
402 }
403}
404
405fn simple_escape(escaped: char) -> Option<char> {
406 match escaped {
407 'n' => Some('\n'),
408 't' => Some('\t'),
409 'r' => Some('\r'),
410 'b' => Some('\u{8}'),
411 'f' => Some('\u{c}'),
412 c if ['"', '\\', '/'].contains(&c) => Some(c),
413 _ => None,
414 }
415}
416
417fn digits(text: &str) -> bool {
418 !text.is_empty() && text.bytes().all(|byte| byte.is_ascii_digit())
419}
420
421fn valid_mantissa(text: &str) -> bool {
422 let (integer, fraction) = text
423 .split_once('.')
424 .map_or((text, None), |(integer, fraction)| {
425 (integer, Some(fraction))
426 });
427 digits(integer)
428 && !(integer.len() > 1 && integer.starts_with('0'))
429 && fraction.is_none_or(digits)
430}
431
432fn valid_number(text: &str) -> bool {
433 let unsigned = text.strip_prefix('-').unwrap_or(text);
434 let mut parts = unsigned.split(['e', 'E']);
435 let mantissa = parts.next().unwrap_or_default();
436 let exponent = parts.next();
437 parts.next().is_none()
438 && valid_mantissa(mantissa)
439 && exponent.is_none_or(|value| {
440 let unsigned = value
441 .strip_prefix('+')
442 .or_else(|| value.strip_prefix('-'))
443 .unwrap_or(value);
444 digits(unsigned)
445 })
446}
447
448#[cfg(test)]
449#[path = "tests/basic.rs"]
450mod tests;
451
452#[cfg(test)]
453#[path = "tests/escapes.rs"]
454mod escape_tests;