1use std::fmt;
8
9pub const MAX_DEPTH: usize = 512;
12
13#[derive(Debug, Clone, PartialEq, Eq)]
14pub struct JsonError {
15 pub position: usize,
16 pub message: &'static str,
17}
18
19impl fmt::Display for JsonError {
20 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21 write!(
22 f,
23 "invalid JSON at position {}: {}",
24 self.position, self.message
25 )
26 }
27}
28
29impl std::error::Error for JsonError {}
30
31type Result<T> = std::result::Result<T, JsonError>;
32
33fn err<T>(position: usize, message: &'static str) -> Result<T> {
34 Err(JsonError { position, message })
35}
36
37fn ws(b: &[u8], mut i: usize) -> usize {
38 while matches!(b.get(i), Some(b' ' | b'\t' | b'\n' | b'\r')) {
39 i += 1;
40 }
41 i
42}
43
44pub fn validate(s: &str) -> Result<()> {
46 let end = skip(s.as_bytes(), 0, 0)?;
47 finish(s.as_bytes(), end, Vec::new()).map(|_| ())
48}
49
50pub fn skip_value(s: &str, start: usize) -> Result<usize> {
52 skip(s.as_bytes(), start, 0)
53}
54
55fn skip(b: &[u8], i: usize, depth: usize) -> Result<usize> {
56 if depth > MAX_DEPTH {
57 return err(i, "nesting too deep");
58 }
59 let i = ws(b, i);
60 match b.get(i).copied() {
61 None => err(i, "unexpected end"),
62 Some(b'{') => {
63 let mut i = ws(b, i + 1);
64 if b.get(i) == Some(&b'}') {
65 return Ok(i + 1);
66 }
67 loop {
68 i = skip_string(b, i)?;
69 i = ws(b, i);
70 if b.get(i) != Some(&b':') {
71 return err(i, "esperava ':'");
72 }
73 i = skip(b, i + 1, depth + 1)?;
74 i = ws(b, i);
75 match b.get(i) {
76 Some(b',') => i = ws(b, i + 1),
77 Some(b'}') => return Ok(i + 1),
78 _ => return err(i, "expected ',' or '}'"),
79 }
80 }
81 }
82 Some(b'[') => {
83 let mut i = ws(b, i + 1);
84 if b.get(i) == Some(&b']') {
85 return Ok(i + 1);
86 }
87 loop {
88 i = skip(b, i, depth + 1)?;
89 i = ws(b, i);
90 match b.get(i) {
91 Some(b',') => i += 1,
92 Some(b']') => return Ok(i + 1),
93 _ => return err(i, "expected ',' or ']'"),
94 }
95 }
96 }
97 Some(b'"') => skip_string(b, i),
98 Some(b't') => literal(b, i, b"true"),
99 Some(b'f') => literal(b, i, b"false"),
100 Some(b'n') => literal(b, i, b"null"),
101 Some(b'-' | b'0'..=b'9') => skip_number(b, i),
102 Some(_) => err(i, "unexpected value"),
103 }
104}
105
106fn literal(b: &[u8], i: usize, word: &[u8]) -> Result<usize> {
107 if b[i..].starts_with(word) {
108 Ok(i + word.len())
109 } else {
110 err(i, "invalid literal")
111 }
112}
113
114fn skip_string(b: &[u8], i: usize) -> Result<usize> {
115 if b.get(i) != Some(&b'"') {
116 return err(i, "expected a string");
117 }
118 let mut i = i + 1;
119 loop {
120 match b.get(i).copied() {
121 None => return err(i, "unterminated string"),
122 Some(b'"') => return Ok(i + 1),
123 Some(b'\\') => match b.get(i + 1).copied() {
124 Some(b'"' | b'\\' | b'/' | b'b' | b'f' | b'n' | b'r' | b't') => i += 2,
125 Some(b'u') => {
126 let hex = b.get(i + 2..i + 6);
127 if hex.is_some_and(|h| h.iter().all(u8::is_ascii_hexdigit)) {
128 i += 6;
129 } else {
130 return err(i, "invalid \\u escape");
131 }
132 }
133 _ => return err(i, "invalid escape"),
134 },
135 Some(c) if c < 0x20 => return err(i, "control character in string"),
136 Some(_) => i += 1,
137 }
138 }
139}
140
141fn skip_number(b: &[u8], mut i: usize) -> Result<usize> {
142 let digits = |b: &[u8], mut i: usize| -> (usize, usize) {
143 let start = i;
144 while matches!(b.get(i), Some(b'0'..=b'9')) {
145 i += 1;
146 }
147 (i, i - start)
148 };
149 if b.get(i) == Some(&b'-') {
150 i += 1;
151 }
152 match b.get(i) {
153 Some(b'0') => i += 1,
154 Some(b'1'..=b'9') => i = digits(b, i).0,
155 _ => return err(i, "invalid number"),
156 }
157 if b.get(i) == Some(&b'.') {
158 let (next, count) = digits(b, i + 1);
159 if count == 0 {
160 return err(i, "invalid number");
161 }
162 i = next;
163 }
164 if matches!(b.get(i), Some(b'e' | b'E')) {
165 i += 1;
166 if matches!(b.get(i), Some(b'+' | b'-')) {
167 i += 1;
168 }
169 let (next, count) = digits(b, i);
170 if count == 0 {
171 return err(i, "invalid number");
172 }
173 i = next;
174 }
175 Ok(i)
176}
177
178fn raw_fields(s: &str) -> Result<Vec<(&str, &str)>> {
180 let b = s.as_bytes();
181 let mut i = ws(b, 0);
182 if b.get(i) != Some(&b'{') {
183 return err(i, "expected an object");
184 }
185 i = ws(b, i + 1);
186 let mut out = Vec::new();
187 if b.get(i) == Some(&b'}') {
188 return finish(b, i + 1, out);
189 }
190 loop {
191 let key_end = skip_string(b, i)?;
192 let key = &s[i..key_end];
193 i = ws(b, key_end);
194 if b.get(i) != Some(&b':') {
195 return err(i, "esperava ':'");
196 }
197 let value_start = ws(b, i + 1);
198 let value_end = skip(b, value_start, 1)?;
199 out.push((key, &s[value_start..value_end]));
200 i = ws(b, value_end);
201 match b.get(i) {
202 Some(b',') => i = ws(b, i + 1),
203 Some(b'}') => return finish(b, i + 1, out),
204 _ => return err(i, "expected ',' or '}'"),
205 }
206 }
207}
208
209fn finish<'a>(b: &[u8], i: usize, out: Vec<(&'a str, &'a str)>) -> Result<Vec<(&'a str, &'a str)>> {
210 let i = ws(b, i);
211 if i != b.len() {
212 return err(i, "text after the document");
213 }
214 Ok(out)
215}
216
217pub fn object_fields(s: &str) -> Result<Vec<(String, &str)>> {
219 raw_fields(s)?
220 .into_iter()
221 .map(|(k, v)| Ok((decode_string(k)?, v)))
222 .collect()
223}
224
225pub fn array_items(s: &str) -> Result<Vec<&str>> {
227 let b = s.as_bytes();
228 let mut i = ws(b, 0);
229 if b.get(i) != Some(&b'[') {
230 return err(i, "expected a list");
231 }
232 i = ws(b, i + 1);
233 let mut out = Vec::new();
234 if b.get(i) == Some(&b']') {
235 finish(b, i + 1, Vec::new())?;
236 return Ok(out);
237 }
238 loop {
239 let end = skip(b, i, 1)?;
240 out.push(s[i..end].trim());
241 i = ws(b, end);
242 match b.get(i) {
243 Some(b',') => i = ws(b, i + 1),
244 Some(b']') => {
245 finish(b, i + 1, Vec::new())?;
246 return Ok(out);
247 }
248 _ => return err(i, "expected ',' or ']'"),
249 }
250 }
251}
252
253pub fn decode_string(raw: &str) -> Result<String> {
255 let raw = raw.trim();
256 let b = raw.as_bytes();
257 if b.len() < 2 || b[0] != b'"' || b[b.len() - 1] != b'"' {
258 return err(0, "expected a string");
259 }
260 let inner = &raw[1..raw.len() - 1];
261 let mut out = String::with_capacity(inner.len());
262 let mut chars = inner.char_indices();
263 while let Some((at, c)) = chars.next() {
264 if c != '\\' {
265 out.push(c);
266 continue;
267 }
268 let Some((_, e)) = chars.next() else {
269 return err(at, "escape incompleto");
270 };
271 match e {
272 '"' => out.push('"'),
273 '\\' => out.push('\\'),
274 '/' => out.push('/'),
275 'b' => out.push('\u{8}'),
276 'f' => out.push('\u{c}'),
277 'n' => out.push('\n'),
278 'r' => out.push('\r'),
279 't' => out.push('\t'),
280 'u' => {
281 let first = hex4(&mut chars, at)?;
282 if (0xD800..0xDC00).contains(&first) {
283 let mut ahead = chars.clone();
285 let paired = match (ahead.next(), ahead.next()) {
286 (Some((_, '\\')), Some((_, 'u'))) => {
287 let second = hex4(&mut ahead, at)?;
288 if (0xDC00..0xE000).contains(&second) {
289 chars = ahead;
290 Some(0x10000 + ((first - 0xD800) << 10) + (second - 0xDC00))
291 } else {
292 None
293 }
294 }
295 _ => None,
296 };
297 out.push(paired.and_then(char::from_u32).unwrap_or('\u{FFFD}'));
298 } else {
299 out.push(char::from_u32(first).unwrap_or('\u{FFFD}'));
300 }
301 }
302 _ => return err(at, "invalid escape"),
303 }
304 }
305 Ok(out)
306}
307
308fn hex4(chars: &mut std::str::CharIndices<'_>, at: usize) -> Result<u32> {
309 let mut value = 0;
310 for _ in 0..4 {
311 let digit = chars.next().and_then(|(_, c)| c.to_digit(16));
312 match digit {
313 Some(d) => value = value * 16 + d,
314 None => return err(at, "invalid \\u escape"),
315 }
316 }
317 Ok(value)
318}
319
320pub fn quote(s: &str) -> String {
322 let mut out = String::with_capacity(s.len() + 2);
323 out.push('"');
324 for c in s.chars() {
325 match c {
326 '"' => out.push_str("\\\""),
327 '\\' => out.push_str("\\\\"),
328 '\n' => out.push_str("\\n"),
329 '\r' => out.push_str("\\r"),
330 '\t' => out.push_str("\\t"),
331 c if (c as u32) < 0x20 => out.push_str(&format!("\\u{:04x}", c as u32)),
332 c => out.push(c),
333 }
334 }
335 out.push('"');
336 out
337}
338
339pub struct Fields<'a>(Vec<(String, &'a str)>);
341
342impl<'a> Fields<'a> {
343 pub fn parse(s: &'a str) -> Result<Self> {
344 object_fields(s).map(Fields)
345 }
346
347 pub fn raw(&self, name: &str) -> Option<&'a str> {
349 self.0.iter().find(|(k, _)| k == name).map(|(_, v)| *v)
350 }
351
352 pub fn string(&self, name: &str) -> Option<String> {
354 let raw = self.raw(name)?;
355 if raw.starts_with('"') {
356 decode_string(raw).ok()
357 } else {
358 None
359 }
360 }
361
362 pub fn unsigned(&self, name: &str) -> Option<u64> {
364 self.raw(name)?.parse().ok()
365 }
366
367 pub fn boolean(&self, name: &str) -> Option<bool> {
369 match self.raw(name)? {
370 "true" => Some(true),
371 "false" => Some(false),
372 _ => None,
373 }
374 }
375
376 pub fn names(&self) -> impl Iterator<Item = &str> {
377 self.0.iter().map(|(k, _)| k.as_str())
378 }
379}
380
381pub fn pretty(s: &str) -> Result<String> {
383 let mut out = String::with_capacity(s.len() * 2);
384 pretty_into(s.trim(), &mut out, 0, 0)?;
385 Ok(out)
386}
387
388fn pretty_into(s: &str, out: &mut String, indent: usize, depth: usize) -> Result<()> {
389 if depth > MAX_DEPTH {
390 return err(0, "nesting too deep");
391 }
392 match s.as_bytes().first() {
393 Some(b'{') => {
394 let fields = raw_fields(s)?;
395 if fields.is_empty() {
396 out.push_str("{}");
397 return Ok(());
398 }
399 out.push_str("{\n");
400 for (n, (key, value)) in fields.iter().enumerate() {
401 out.push_str(&" ".repeat(indent + 2));
402 out.push_str(key);
403 out.push_str(": ");
404 pretty_into(value, out, indent + 2, depth + 1)?;
405 out.push_str(if n + 1 < fields.len() { ",\n" } else { "\n" });
406 }
407 out.push_str(&" ".repeat(indent));
408 out.push('}');
409 }
410 Some(b'[') => {
411 let items = array_items(s)?;
412 if items.is_empty() {
413 out.push_str("[]");
414 return Ok(());
415 }
416 out.push_str("[\n");
417 for (n, item) in items.iter().enumerate() {
418 out.push_str(&" ".repeat(indent + 2));
419 pretty_into(item, out, indent + 2, depth + 1)?;
420 out.push_str(if n + 1 < items.len() { ",\n" } else { "\n" });
421 }
422 out.push_str(&" ".repeat(indent));
423 out.push(']');
424 }
425 _ => {
426 let end = skip_value(s, 0)?;
427 if ws(s.as_bytes(), end) != s.len() {
428 return err(end, "text after the document");
429 }
430 out.push_str(s.trim());
431 }
432 }
433 Ok(())
434}
435
436#[cfg(test)]
437mod tests;