1#[derive(Clone, Debug, PartialEq, Eq)]
5pub enum Value {
6 Object(Vec<(String, Value)>),
8 Array(Vec<Value>),
9 String(String),
10 Number(String),
12 Bool(bool),
13 Null,
14}
15
16#[derive(Clone, Copy, Debug, PartialEq, Eq)]
18pub enum Invalid {
19 Malformed,
20 Empty,
21 Trailing,
22}
23
24impl Invalid {
25 pub fn code(self) -> i64 {
26 match self {
27 Invalid::Malformed => 100,
28 Invalid::Empty => 101,
29 Invalid::Trailing => 102,
30 }
31 }
32}
33
34struct Reader<'t> {
35 text: &'t [char],
36 at: usize,
37}
38
39type Read<T> = Result<T, Invalid>;
40
41fn is_space(c: char) -> bool {
42 matches!(c, ' ' | '\t' | '\n' | '\r')
43}
44
45impl Reader<'_> {
46 fn skip_space(&mut self) {
47 while self.text.get(self.at).is_some_and(|&c| is_space(c)) {
48 self.at += 1;
49 }
50 }
51
52 fn next(&mut self) -> Read<char> {
53 let c = *self.text.get(self.at).ok_or(Invalid::Malformed)?;
54 self.at += 1;
55 Ok(c)
56 }
57
58 fn expect(&mut self, word: &str) -> Read<()> {
59 for w in word.chars() {
60 if self.next()? != w {
61 return Err(Invalid::Malformed);
62 }
63 }
64 Ok(())
65 }
66
67 fn value(&mut self) -> Read<Value> {
68 self.skip_space();
69 let value = match self.text.get(self.at).ok_or(Invalid::Malformed)? {
70 '{' => self.object()?,
71 '[' => self.array()?,
72 '"' => Value::String(self.string()?),
73 't' => {
74 self.expect("true")?;
75 Value::Bool(true)
76 }
77 'f' => {
78 self.expect("false")?;
79 Value::Bool(false)
80 }
81 'n' => {
82 self.expect("null")?;
83 Value::Null
84 }
85 _ => Value::Number(self.number()?),
86 };
87 self.skip_space();
88 Ok(value)
89 }
90
91 fn object(&mut self) -> Read<Value> {
92 self.at += 1;
93 let mut pairs = Vec::new();
94 self.skip_space();
95 if self.text.get(self.at) == Some(&'}') {
96 self.at += 1;
97 return Ok(Value::Object(pairs));
98 }
99 loop {
100 self.skip_space();
101 if self.text.get(self.at) != Some(&'"') {
102 return Err(Invalid::Malformed);
103 }
104 let name = self.string()?;
105 self.skip_space();
106 self.expect(":")?;
107 pairs.push((name, self.value()?));
108 match self.next()? {
109 ',' => {}
110 '}' => return Ok(Value::Object(pairs)),
111 _ => return Err(Invalid::Malformed),
112 }
113 }
114 }
115
116 fn array(&mut self) -> Read<Value> {
117 self.at += 1;
118 let mut elements = Vec::new();
119 self.skip_space();
120 if self.text.get(self.at) == Some(&']') {
121 self.at += 1;
122 return Ok(Value::Array(elements));
123 }
124 loop {
125 elements.push(self.value()?);
126 match self.next()? {
127 ',' => {}
128 ']' => return Ok(Value::Array(elements)),
129 _ => return Err(Invalid::Malformed),
130 }
131 }
132 }
133
134 fn hex4(&mut self) -> Read<u32> {
135 let mut unit = 0;
136 for _ in 0..4 {
137 unit = unit * 16 + self.next()?.to_digit(16).ok_or(Invalid::Malformed)?;
138 }
139 Ok(unit)
140 }
141
142 fn string(&mut self) -> Read<String> {
143 self.at += 1;
144 let mut out = String::new();
145 loop {
146 match self.next()? {
147 '"' => return Ok(out),
148 '\\' => out.push(match self.next()? {
149 '"' => '"',
150 '\\' => '\\',
151 '/' => '/',
152 'b' => '\u{8}',
153 'f' => '\u{c}',
154 'n' => '\n',
155 'r' => '\r',
156 't' => '\t',
157 'x' => '\u{85}',
158 'u' => {
159 let unit = self.hex4()?;
160 let scalar = if (0xD800..0xDC00).contains(&unit) {
161 self.expect("\\u")?;
162 let low = self.hex4()?;
163 if !(0xDC00..0xE000).contains(&low) {
164 return Err(Invalid::Malformed);
165 }
166 0x10000 + ((unit - 0xD800) << 10) + (low - 0xDC00)
167 } else {
168 unit
169 };
170 char::from_u32(scalar).ok_or(Invalid::Malformed)?
171 }
172 _ => return Err(Invalid::Malformed),
173 }),
174 c if (c as u32) < 0x20 => return Err(Invalid::Malformed),
175 c => out.push(c),
176 }
177 }
178 }
179
180 fn digits(&mut self) -> usize {
181 let start = self.at;
182 while self.text.get(self.at).is_some_and(char::is_ascii_digit) {
183 self.at += 1;
184 }
185 self.at - start
186 }
187
188 fn number(&mut self) -> Read<String> {
189 let start = self.at;
190 if self.text.get(self.at) == Some(&'-') {
191 self.at += 1;
192 }
193 let first = self.text.get(self.at).copied();
194 let whole = self.digits();
195 if whole == 0 || (first == Some('0') && whole > 1) {
196 return Err(Invalid::Malformed);
197 }
198 if self.text.get(self.at) == Some(&'.') {
199 self.at += 1;
200 if self.digits() == 0 {
201 return Err(Invalid::Malformed);
202 }
203 }
204 if matches!(self.text.get(self.at), Some('e' | 'E')) {
205 self.at += 1;
206 if matches!(self.text.get(self.at), Some('+' | '-')) {
207 self.at += 1;
208 }
209 if self.digits() == 0 {
210 return Err(Invalid::Malformed);
211 }
212 }
213 Ok(self.text[start..self.at].iter().collect())
214 }
215}
216
217pub fn parse(text: &str) -> Result<Value, Invalid> {
219 let chars: Vec<char> = text.chars().collect();
220 if chars.iter().all(|&c| is_space(c)) {
221 return Err(Invalid::Empty);
222 }
223 let mut reader = Reader { text: &chars, at: 0 };
224 let value = reader.value()?;
225 if reader.at < chars.len() {
226 return Err(Invalid::Trailing);
227 }
228 Ok(value)
229}
230
231pub fn decimal(number: &str) -> (bool, String, String) {
233 let (negative, body) = match number.strip_prefix('-') {
234 Some(rest) => (true, rest),
235 None => (false, number),
236 };
237 let (mantissa, exponent) = match body.find(['e', 'E']) {
238 Some(e) => (&body[..e], body[e + 1..].parse::<i64>().unwrap_or(0)),
239 None => (body, 0),
240 };
241 let (int, frac) = mantissa.split_once('.').unwrap_or((mantissa, ""));
242 let digits = format!("{int}{frac}");
243 let point = int.len() as i64 + exponent;
244 let (int, frac) = if point <= 0 {
245 (String::new(), format!("{}{digits}", "0".repeat(point.unsigned_abs() as usize)))
246 } else if point as usize >= digits.len() {
247 (format!("{digits}{}", "0".repeat(point as usize - digits.len())), String::new())
248 } else {
249 (digits[..point as usize].to_owned(), digits[point as usize..].to_owned())
250 };
251 (negative, int.trim_start_matches('0').to_owned(), frac)
252}
253
254pub fn numeric_string(text: &str, point: char) -> Option<(bool, String, String)> {
257 let body = text.trim_matches(' ');
258 let (negative, body) = match body.as_bytes().first() {
259 Some(b'-') => (true, &body[1..]),
260 Some(b'+') => (false, &body[1..]),
261 _ => (false, body),
262 };
263 let (int, frac) = body.split_once(point).unwrap_or((body, ""));
264 let all_digits = |s: &str| s.bytes().all(|b| b.is_ascii_digit());
265 (!(int.is_empty() && frac.is_empty()) && all_digits(int) && all_digits(frac)).then(|| (negative, int.trim_start_matches('0').to_owned(), frac.to_owned()))
266}
267
268#[cfg(test)]
269mod tests {
270 use super::*;
271
272 fn s(t: &str) -> Value {
273 Value::String(t.into())
274 }
275
276 #[test]
277 fn objects_arrays_and_scalars_parse_in_order() {
278 let v = parse(" {\"g\": {\"A\": \"Eh?\", \"3_\": 5, \"t\": [true, false, null, -1.5e2]}} ").unwrap();
279 let inner = Value::Object(vec![
280 ("A".into(), s("Eh?")),
281 ("3_".into(), Value::Number("5".into())),
282 ("t".into(), Value::Array(vec![Value::Bool(true), Value::Bool(false), Value::Null, Value::Number("-1.5e2".into())])),
283 ]);
284 assert_eq!(v, Value::Object(vec![("g".into(), inner)]));
285 }
286
287 #[test]
288 fn escapes_include_ibms_next_line() {
289 assert_eq!(parse(r#"["a\"b\\c\/\né\x😀"]"#).unwrap(), Value::Array(vec![s("a\"b\\c/\né\u{85}😀")]));
290 }
291
292 #[test]
293 fn bad_text_gives_its_code() {
294 assert_eq!(parse(" "), Err(Invalid::Empty));
295 assert_eq!(parse("{\"a\":1} x"), Err(Invalid::Trailing));
296 for bad in ["{\"a\":01}", "{\"a\":1,}", "{a:1}", "[\"\u{1}\"]", "{\"a\":tru}", "{\"a\":.5}", "{\"a\":1."] {
297 assert_eq!(parse(bad), Err(Invalid::Malformed), "{bad}");
298 }
299 }
300
301 #[test]
302 fn numbers_become_digits_either_side_of_the_point() {
303 assert_eq!(decimal("-125.53"), (true, "125".into(), "53".into()));
304 assert_eq!(decimal("1.5E+3"), (false, "1500".into(), String::new()));
305 assert_eq!(decimal("12e-3"), (false, String::new(), "012".into()));
306 assert_eq!(decimal("0"), (false, String::new(), String::new()));
307 assert_eq!(numeric_string(" -1,234 ", ','), Some((true, "1".into(), "234".into())));
308 assert_eq!(numeric_string("0042", '.'), Some((false, "42".into(), String::new())));
309 assert_eq!(numeric_string("4x", '.'), None);
310 assert_eq!(numeric_string(" ", '.'), None);
311 }
312}