text_scanner/ext/
json.rs

1use crate::{Scanner, ScannerResult};
2
3/// [`Scanner`] extension for scanning JSON tokens.
4pub trait JsonScannerExt<'text>: crate::private::Sealed {
5    fn scan_json_string(&mut self) -> ScannerResult<'text, &'text str>;
6    fn scan_json_number(&mut self) -> ScannerResult<'text, &'text str>;
7}
8
9impl<'text> JsonScannerExt<'text> for Scanner<'text> {
10    // Reference: https://www.json.org/json-en.html
11    fn scan_json_string(&mut self) -> ScannerResult<'text, &'text str> {
12        self.scan_with(|scanner| {
13            let (_r, _c) = scanner.accept_char('"')?;
14
15            loop {
16                scanner.skip_until_char_any(&['"', '\\']);
17                match scanner.next() {
18                    Ok((_r, '"')) => break,
19                    Ok((_r, '\\')) => {
20                        // Skip the next character as it is escaped
21                        // Note: Technically any character is not valid
22                        _ = scanner.next();
23                    }
24                    Ok(_) => unreachable!(),
25                    Err(_) => break,
26                }
27            }
28
29            Ok(())
30        })
31    }
32
33    // Reference: https://www.json.org/json-en.html
34    fn scan_json_number(&mut self) -> ScannerResult<'text, &'text str> {
35        self.scan_with(|scanner| {
36            let (_r, c) = scanner.accept_if(|c| c.is_ascii_digit() || (c == '-'))?;
37            if c == '-' {
38                scanner.accept_if(|c| c.is_ascii_digit())?;
39            }
40            scanner.skip_while(|c| c.is_ascii_digit());
41
42            if scanner.accept_char('.').is_ok() {
43                scanner.skip_while(|c| c.is_ascii_digit());
44            }
45
46            if scanner.accept_char_any(&['E', 'e']).is_ok() {
47                _ = scanner.accept_char_any(&['+', '-']);
48                scanner.skip_while(|c| c.is_ascii_digit());
49            }
50
51            Ok(())
52        })
53    }
54}