1#![allow(clippy::missing_errors_doc)]
2
3use std::collections::BTreeMap;
4use std::fmt::{self, Write};
5
6#[derive(Debug, Clone, PartialEq)]
7pub enum JsonValue {
8 Null,
9 Bool(bool),
10 Integer(i64),
11 Float(f64),
12 String(String),
13 Array(Vec<Self>),
14 Object(BTreeMap<String, Self>),
15}
16
17impl JsonValue {
18 pub fn field<T: FromJson>(&self, key: &str) -> Result<T, JsonError> {
19 let Self::Object(object) = self else {
20 return Err(JsonError("expected an object".into()));
21 };
22 object.get(key).map_or_else(
23 || T::from_json(&Self::Null).map_err(|_| JsonError(format!("missing field `{key}`"))),
24 T::from_json,
25 )
26 }
27}
28
29impl fmt::Display for JsonValue {
30 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
31 match self {
32 Self::Bool(value) => write!(formatter, "{value}"),
33 Self::Integer(value) => write!(formatter, "{value}"),
34 Self::Float(value) if value.is_finite() => write!(formatter, "{value:?}"),
35 Self::Null | Self::Float(_) => formatter.write_str("null"),
36 Self::String(value) => write_string(value, formatter),
37 Self::Array(values) => {
38 formatter.write_str("[")?;
39 for (index, value) in values.iter().enumerate() {
40 if index != 0 {
41 formatter.write_str(",")?;
42 }
43 write!(formatter, "{value}")?;
44 }
45 formatter.write_str("]")
46 }
47 Self::Object(values) => {
48 formatter.write_str("{")?;
49 for (index, (key, value)) in values.iter().enumerate() {
50 if index != 0 {
51 formatter.write_str(",")?;
52 }
53 write_string(key, formatter)?;
54 write!(formatter, ":{value}")?;
55 }
56 formatter.write_str("}")
57 }
58 }
59 }
60}
61
62fn write_string(text: &str, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
63 formatter.write_str("\"")?;
64 for character in text.chars() {
65 match character {
66 '"' => formatter.write_str("\\\"")?,
67 '\\' => formatter.write_str("\\\\")?,
68 '\u{08}' => formatter.write_str("\\b")?,
69 '\u{0c}' => formatter.write_str("\\f")?,
70 '\n' => formatter.write_str("\\n")?,
71 '\r' => formatter.write_str("\\r")?,
72 '\t' => formatter.write_str("\\t")?,
73 '\u{00}'..='\u{1f}' => write!(formatter, "\\u{:04x}", u32::from(character))?,
74 _ => formatter.write_char(character)?,
75 }
76 }
77 formatter.write_str("\"")
78}
79
80#[derive(Debug)]
81pub struct JsonError(String);
82
83impl fmt::Display for JsonError {
84 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
85 formatter.write_str(&self.0)
86 }
87}
88
89impl std::error::Error for JsonError {}
90
91pub trait FromJson: Sized {
92 fn from_json(value: &JsonValue) -> Result<Self, JsonError>;
93}
94
95impl FromJson for JsonValue {
96 fn from_json(value: &JsonValue) -> Result<Self, JsonError> {
97 Ok(value.clone())
98 }
99}
100
101impl FromJson for bool {
102 fn from_json(value: &JsonValue) -> Result<Self, JsonError> {
103 match value {
104 JsonValue::Bool(value) => Ok(*value),
105 _ => Err(JsonError("expected a boolean".into())),
106 }
107 }
108}
109
110macro_rules! integer_from_json {
111 ($($type:ty),+ $(,)?) => {
112 $(
113 impl FromJson for $type {
114 fn from_json(value: &JsonValue) -> Result<Self, JsonError> {
115 match value {
116 JsonValue::Integer(value) => Self::try_from(*value).map_err(|_| {
117 JsonError(concat!("integer out of range for ", stringify!($type)).into())
118 }),
119 _ => Err(JsonError(concat!("expected an integer for ", stringify!($type)).into())),
120 }
121 }
122 }
123 )+
124 };
125}
126
127integer_from_json!(i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize);
128
129impl FromJson for f64 {
130 #[allow(clippy::cast_precision_loss)]
131 fn from_json(value: &JsonValue) -> Result<Self, JsonError> {
132 match value {
133 JsonValue::Integer(value) => Ok(*value as Self),
134 JsonValue::Float(value) => Ok(*value),
135 _ => Err(JsonError("expected a number".into())),
136 }
137 }
138}
139
140impl FromJson for f32 {
141 #[allow(clippy::cast_precision_loss, clippy::cast_possible_truncation)]
142 fn from_json(value: &JsonValue) -> Result<Self, JsonError> {
143 match value {
144 JsonValue::Integer(value) => Ok(*value as Self),
145 JsonValue::Float(value) => Ok(*value as Self),
146 _ => Err(JsonError("expected a number".into())),
147 }
148 }
149}
150
151impl FromJson for String {
152 fn from_json(value: &JsonValue) -> Result<Self, JsonError> {
153 match value {
154 JsonValue::String(value) => Ok(value.clone()),
155 _ => Err(JsonError("expected a string".into())),
156 }
157 }
158}
159
160impl<T: FromJson> FromJson for Option<T> {
161 fn from_json(value: &JsonValue) -> Result<Self, JsonError> {
162 match value {
163 JsonValue::Null => Ok(None),
164 _ => T::from_json(value).map(Some),
165 }
166 }
167}
168
169impl<T: FromJson> FromJson for Vec<T> {
170 fn from_json(value: &JsonValue) -> Result<Self, JsonError> {
171 match value {
172 JsonValue::Array(values) => values.iter().map(T::from_json).collect(),
173 _ => Err(JsonError("expected an array".into())),
174 }
175 }
176}
177
178impl<T: FromJson> FromJson for BTreeMap<String, T> {
179 fn from_json(value: &JsonValue) -> Result<Self, JsonError> {
180 match value {
181 JsonValue::Object(values) => values
182 .iter()
183 .map(|(key, value)| T::from_json(value).map(|value| (key.clone(), value)))
184 .collect(),
185 _ => Err(JsonError("expected an object".into())),
186 }
187 }
188}
189
190pub fn parse(text: &str) -> Result<JsonValue, JsonError> {
191 let mut reader = Reader { text, position: 0 };
192 let value = reader.value(0)?;
193 reader.whitespace();
194 if reader.position != text.len() {
195 return Err(reader.error("unexpected trailing data"));
196 }
197 Ok(value)
198}
199
200struct Reader<'a> {
201 text: &'a str,
202 position: usize,
203}
204
205impl Reader<'_> {
206 fn error(&self, message: &str) -> JsonError {
207 JsonError(format!("{message} at byte {}", self.position))
208 }
209
210 fn peek(&self) -> Option<u8> {
211 self.text.as_bytes().get(self.position).copied()
212 }
213
214 fn consume(&mut self, byte: u8) -> bool {
215 if self.peek() == Some(byte) {
216 self.position += 1;
217 true
218 } else {
219 false
220 }
221 }
222
223 fn whitespace(&mut self) {
224 while matches!(self.peek(), Some(b' ' | b'\t' | b'\n' | b'\r')) {
225 self.position += 1;
226 }
227 }
228
229 fn value(&mut self, depth: usize) -> Result<JsonValue, JsonError> {
230 self.whitespace();
231 match self.peek() {
232 Some(b'{' | b'[') if depth >= 128 => Err(self.error("nesting limit exceeded")),
233 Some(b'{') => self.object(depth + 1),
234 Some(b'[') => self.array(depth + 1),
235 Some(b'"') => self.string().map(JsonValue::String),
236 Some(b'-' | b'0'..=b'9') => self.number(),
237 Some(b't') if self.text[self.position..].starts_with("true") => {
238 self.position += 4;
239 Ok(JsonValue::Bool(true))
240 }
241 Some(b'f') if self.text[self.position..].starts_with("false") => {
242 self.position += 5;
243 Ok(JsonValue::Bool(false))
244 }
245 Some(b'n') if self.text[self.position..].starts_with("null") => {
246 self.position += 4;
247 Ok(JsonValue::Null)
248 }
249 _ => Err(self.error("expected a JSON value")),
250 }
251 }
252
253 fn object(&mut self, depth: usize) -> Result<JsonValue, JsonError> {
254 self.position += 1;
255 let mut values = BTreeMap::new();
256 self.whitespace();
257 if self.consume(b'}') {
258 return Ok(JsonValue::Object(values));
259 }
260 loop {
261 if self.peek() != Some(b'"') {
262 return Err(self.error("expected an object key"));
263 }
264 let key = self.string()?;
265 self.whitespace();
266 if !self.consume(b':') {
267 return Err(self.error("expected `:`"));
268 }
269 let value = self.value(depth)?;
270 values.insert(key, value);
271 self.whitespace();
272 if self.consume(b'}') {
273 return Ok(JsonValue::Object(values));
274 }
275 if !self.consume(b',') {
276 return Err(self.error("expected `,` or `}`"));
277 }
278 self.whitespace();
279 }
280 }
281
282 fn array(&mut self, depth: usize) -> Result<JsonValue, JsonError> {
283 self.position += 1;
284 let mut values = Vec::new();
285 self.whitespace();
286 if self.consume(b']') {
287 return Ok(JsonValue::Array(values));
288 }
289 loop {
290 values.push(self.value(depth)?);
291 self.whitespace();
292 if self.consume(b']') {
293 return Ok(JsonValue::Array(values));
294 }
295 if !self.consume(b',') {
296 return Err(self.error("expected `,` or `]`"));
297 }
298 }
299 }
300
301 fn string(&mut self) -> Result<String, JsonError> {
302 self.position += 1;
303 let mut value = String::new();
304 let mut start = self.position;
305 loop {
306 match self.peek() {
307 Some(b'"') => {
308 value.push_str(&self.text[start..self.position]);
309 self.position += 1;
310 return Ok(value);
311 }
312 Some(b'\\') => {
313 value.push_str(&self.text[start..self.position]);
314 self.position += 1;
315 let escaped = match self.peek() {
316 Some(b'"') => '"',
317 Some(b'\\') => '\\',
318 Some(b'/') => '/',
319 Some(b'b') => '\u{08}',
320 Some(b'f') => '\u{0c}',
321 Some(b'n') => '\n',
322 Some(b'r') => '\r',
323 Some(b't') => '\t',
324 Some(b'u') => {
325 self.position += 1;
326 let first = self.hex_quad()?;
327 let code = if (0xd800..=0xdbff).contains(&first) {
328 if !self.consume(b'\\') || !self.consume(b'u') {
329 return Err(self.error("expected a low surrogate"));
330 }
331 let second = self.hex_quad()?;
332 if !(0xdc00..=0xdfff).contains(&second) {
333 return Err(self.error("invalid low surrogate"));
334 }
335 0x10000 + ((first - 0xd800) << 10) + second - 0xdc00
336 } else {
337 first
338 };
339 let character = char::from_u32(code)
340 .ok_or_else(|| self.error("invalid Unicode escape"))?;
341 value.push(character);
342 start = self.position;
343 continue;
344 }
345 _ => return Err(self.error("invalid string escape")),
346 };
347 value.push(escaped);
348 self.position += 1;
349 start = self.position;
350 }
351 Some(0..=0x1f) => return Err(self.error("unescaped control character")),
352 Some(_) => self.position += 1,
353 None => return Err(self.error("unterminated string")),
354 }
355 }
356 }
357
358 fn hex_quad(&mut self) -> Result<u32, JsonError> {
359 let mut value = 0;
360 for _ in 0..4 {
361 let digit = match self.peek() {
362 Some(byte @ b'0'..=b'9') => byte - b'0',
363 Some(byte @ b'a'..=b'f') => byte - b'a' + 10,
364 Some(byte @ b'A'..=b'F') => byte - b'A' + 10,
365 _ => return Err(self.error("invalid Unicode escape")),
366 };
367 value = value * 16 + u32::from(digit);
368 self.position += 1;
369 }
370 Ok(value)
371 }
372
373 fn number(&mut self) -> Result<JsonValue, JsonError> {
374 let start = self.position;
375 self.consume(b'-');
376 match self.peek() {
377 Some(b'0') => self.position += 1,
378 Some(b'1'..=b'9') => {
379 while matches!(self.peek(), Some(b'0'..=b'9')) {
380 self.position += 1;
381 }
382 }
383 _ => return Err(self.error("expected a digit")),
384 }
385 let mut float = false;
386 if self.consume(b'.') {
387 float = true;
388 let fraction_start = self.position;
389 while matches!(self.peek(), Some(b'0'..=b'9')) {
390 self.position += 1;
391 }
392 if self.position == fraction_start {
393 return Err(self.error("expected a fraction digit"));
394 }
395 }
396 if self.consume(b'e') || self.consume(b'E') {
397 float = true;
398 if !self.consume(b'+') {
399 self.consume(b'-');
400 }
401 let exponent_start = self.position;
402 while matches!(self.peek(), Some(b'0'..=b'9')) {
403 self.position += 1;
404 }
405 if self.position == exponent_start {
406 return Err(self.error("expected an exponent digit"));
407 }
408 }
409 let token = &self.text[start..self.position];
410 if !float && token != "-0" {
411 if let Ok(value) = token.parse::<i64>() {
412 return Ok(JsonValue::Integer(value));
413 }
414 }
415 let value = token
416 .parse::<f64>()
417 .map_err(|_| self.error("invalid number"))?;
418 if !value.is_finite() {
419 return Err(self.error("number out of range"));
420 }
421 Ok(JsonValue::Float(value))
422 }
423}