use std::collections::HashMap; use std::fmt; use std::ops::Range; /// UTF-8 byte offsets into the original source. pub type Span = Range; #[derive(Debug, Clone, PartialEq)] pub struct Value { pub kind: ValueKind, pub span: Span, } #[derive(Debug, Clone, PartialEq)] pub enum ValueKind { Null, Bool(bool), // Preserve precision until the config schema chooses a numeric type. Number(String), String(String), Array(Vec), Object(Vec), } #[derive(Debug, Clone, PartialEq)] pub struct Member { pub key: String, pub key_span: Span, pub value: Value, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ErrorKind { Syntax, Unicode, Number, Nesting, } #[derive(Debug, Clone, PartialEq, Eq)] pub struct Error { pub kind: ErrorKind, pub message: String, pub span: Span, pub previous: Option, } impl Error { /// Render against the same source that was parsed. pub fn render(&self, name: &str, source: &str) -> String { let location = |offset: usize| { let mut line = 1; let mut column = 1; let mut previous_cr = false; for ch in source[..offset].chars() { match ch { '\r' => { line += 1; column = 1; } '\n' if previous_cr => {} '\n' => { line += 1; column = 1; } _ => column += 1, } previous_cr = ch == '\r'; } format!("{name}:{line}:{column}") }; let mut message = format!("{}: {}", location(self.span.start), self.message); if let Some(previous) = &self.previous { message.push_str(&format!( "\n{}: First defined here", location(previous.start) )); } message } } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(&self.message) } } impl std::error::Error for Error {} pub fn parse_object(source: &str) -> Result { parse_object_with(source, &crate::Options::default()) } pub fn parse_object_with(source: &str, options: &crate::Options) -> Result { let mut parser = Parser { source, offset: 0, max_depth: options.max_depth, }; parser.trivia()?; let value = parser.object(parser.peek() == Some('{'), 0)?; parser.trivia()?; if parser.peek().is_some() { return Err(parser.error("Unexpected content after the root object")); } Ok(value) } pub(crate) fn nesting_limit(span: Span, limit: usize) -> Error { Error { kind: ErrorKind::Nesting, span, message: format!("fJSON nesting exceeds {limit} levels below the root"), previous: None, } } pub(crate) fn invalid(span: Span, message: impl Into) -> Error { Error { kind: ErrorKind::Syntax, span, message: message.into(), previous: None, } } pub fn parse(source: &str) -> Result { parse_with(source, &crate::Options::default()) } pub fn parse_with(source: &str, options: &crate::Options) -> Result { let mut parser = Parser { source, offset: 0, max_depth: options.max_depth, }; parser.trivia()?; let start = parser.offset; let implicit = match parser.peek() { None => true, Some('"' | '\'') => { parser.string()?; parser.trivia()?; parser.peek() == Some(':') } Some(ch) if ch.is_ascii_alphabetic() || ch == '_' || ch == '$' => { parser.identifier(); parser.trivia()?; parser.peek() == Some(':') } _ => false, }; parser.offset = start; let value = if implicit { parser.object(false, 0)? } else { parser.value(0)? }; parser.trivia()?; if parser.peek().is_some() { return Err(parser.error("Unexpected content after the root value")); } Ok(value) } struct Parser<'a> { source: &'a str, offset: usize, max_depth: usize, } impl Parser<'_> { fn peek(&self) -> Option { self.source[self.offset..].chars().next() } fn bump(&mut self) -> Option { let ch = self.peek()?; self.offset += ch.len_utf8(); Some(ch) } fn error(&self, message: impl Into) -> Error { Error { kind: ErrorKind::Syntax, message: message.into(), span: self.offset..self.offset + self.peek().map_or(0, char::len_utf8), previous: None, } } fn expect(&mut self, expected: char) -> Result<(), Error> { if self.peek() != Some(expected) { return Err(self.error(format!("Expected `{expected}`"))); } self.bump(); Ok(()) } fn trivia(&mut self) -> Result { let mut newline = false; loop { match self.peek() { Some(' ' | '\t') => { self.bump(); } Some('\r' | '\n') => { newline = true; self.bump(); } Some('/') if self.source[self.offset..].starts_with("//") => { while self.peek().is_some_and(|ch| ch != '\r' && ch != '\n') { self.bump(); } } Some('/') if self.source[self.offset..].starts_with("/*") => { let start = self.offset; self.offset += 2; let Some(end) = self.source[self.offset..].find("*/") else { return Err(Error { kind: ErrorKind::Syntax, message: "Unterminated block comment".into(), span: start..start + 2, previous: None, }); }; self.offset += end + 2; } _ => return Ok(newline), } } } fn end(&mut self, closing: Option) -> Result { match (closing, self.peek()) { (Some(expected), Some(ch)) if ch == expected => { self.bump(); Ok(true) } (Some(expected), None) => Err(self.error(format!("Expected `{expected}`"))), (None, None) => Ok(true), _ => Ok(false), } } fn more(&mut self, closing: Option) -> Result { let newline = self.trivia()?; if self.end(closing)? { return Ok(false); } if self.peek() == Some(',') { self.bump(); self.trivia()?; } else if !newline { return Err(self.error("Expected `,` or newline")); } Ok(!self.end(closing)?) } fn object(&mut self, braced: bool, depth: usize) -> Result { let start = self.offset; if braced { self.expect('{')?; } let closing = braced.then_some('}'); let mut members = Vec::new(); let mut end = start; let mut keys = HashMap::new(); self.trivia()?; if !self.end(closing)? { loop { let key_start = self.offset; let key = match self.peek() { Some('"' | '\'') => self.string()?, Some(ch) if ch.is_ascii_alphabetic() || ch == '_' || ch == '$' => { self.identifier().to_owned() } _ => return Err(self.error("Expected an object key")), }; let key_span = key_start..self.offset; if let Some(previous) = keys.insert(key.clone(), key_span.clone()) { return Err(Error { kind: ErrorKind::Syntax, message: format!("Duplicate key {key:?}"), span: key_span, previous: Some(previous), }); } self.trivia()?; self.expect(':')?; self.trivia()?; let value = self.value(depth + 1)?; end = self.offset; members.push(Member { key, key_span, value }); if !self.more(closing)? { break; } } } Ok(Value { kind: ValueKind::Object(members), span: start..if braced { self.offset } else { end }, }) } fn array(&mut self, depth: usize) -> Result { let start = self.offset; self.expect('[')?; self.trivia()?; let mut values = Vec::new(); if !self.end(Some(']'))? { loop { values.push(self.value(depth + 1)?); if !self.more(Some(']'))? { break; } } } Ok(Value { kind: ValueKind::Array(values), span: start..self.offset, }) } fn value(&mut self, depth: usize) -> Result { let start = self.offset; let kind = match self.peek() { Some('{' | '[') if depth > self.max_depth => { return Err(nesting_limit(start..start + 1, self.max_depth)); } Some('{') => return self.object(true, depth), Some('[') => return self.array(depth), Some('"' | '\'') => ValueKind::String(self.string()?), Some('-' | '0'..='9') => ValueKind::Number(self.number()?), Some(ch) if ch.is_ascii_alphabetic() || ch == '_' || ch == '$' => { let word = self.identifier(); match word { "true" => ValueKind::Bool(true), "false" => ValueKind::Bool(false), "null" => ValueKind::Null, _ => { return Err(Error { kind: ErrorKind::Syntax, message: format!("Unknown value {word:?}; strings must be quoted"), span: start..self.offset, previous: None, }); } } } _ => return Err(self.error("Expected a value")), }; Ok(Value { kind, span: start..self.offset }) } fn identifier(&mut self) -> &str { let start = self.offset; while self .peek() .is_some_and(|ch| ch.is_ascii_alphanumeric() || ch == '_' || ch == '$') { self.bump(); } &self.source[start..self.offset] } fn number(&mut self) -> Result { let start = self.offset; if self.peek() == Some('-') { self.bump(); } match self.peek() { Some('0') => { self.bump(); if self.peek().is_some_and(|ch| ch.is_ascii_digit()) { return Err(self.error("Leading zeros are not allowed")); } } Some('1'..='9') => self.digits()?, _ => return Err(self.error("Expected a digit")), } if self.peek() == Some('.') { self.bump(); self.digits()?; } if matches!(self.peek(), Some('e' | 'E')) { self.bump(); if matches!(self.peek(), Some('+' | '-')) { self.bump(); } self.digits()?; } if self .peek() .is_some_and(|ch| ch.is_ascii_alphanumeric() || matches!(ch, '.' | '_' | '$')) { return Err(self.error("Invalid decimal number")); } Ok(self.source[start..self.offset].to_owned()) } fn digits(&mut self) -> Result<(), Error> { if !self.peek().is_some_and(|ch| ch.is_ascii_digit()) { return Err(self.error("Expected a digit")); } while self.peek().is_some_and(|ch| ch.is_ascii_digit()) { self.bump(); } Ok(()) } fn string(&mut self) -> Result { let start = self.offset; let quote = self.bump().unwrap(); let mut value = String::new(); loop { let ch = self.peek().ok_or_else(|| Error { kind: ErrorKind::Syntax, message: "Unterminated string".into(), span: start..start + 1, previous: None, })?; match ch { ch if ch == quote => { self.bump(); return Ok(value); } '\\' => { self.bump(); let escaped = match self.peek() { Some('"' | '\'' | '\\' | '/') => self.bump().unwrap(), Some('b') => { self.bump(); '\u{0008}' } Some('f') => { self.bump(); '\u{000c}' } Some('n') => { self.bump(); '\n' } Some('r') => { self.bump(); '\r' } Some('t') => { self.bump(); '\t' } Some('u') => { self.bump(); let unicode_start = self.offset; let first = self.hex_quad()?; let scalar = match first { 0xd800..=0xdbff => { if !self.source[self.offset..].starts_with("\\u") { return Err(Error { kind: ErrorKind::Unicode, message: "Unpaired Unicode surrogate".into(), span: unicode_start..self.offset, previous: None, }); } self.offset += 2; let second = self.hex_quad()?; if !(0xdc00..=0xdfff).contains(&second) { return Err(Error { kind: ErrorKind::Unicode, message: "Expected a low Unicode surrogate".into(), span: self.offset - 4..self.offset, previous: None, }); } 0x10000 + ((first - 0xd800) << 10) + second - 0xdc00 } 0xdc00..=0xdfff => { return Err(Error { kind: ErrorKind::Unicode, message: "Unexpected low Unicode surrogate".into(), span: unicode_start..self.offset, previous: None, }); } _ => first, }; char::from_u32(scalar).unwrap() } _ => return Err(self.error("Invalid string escape")), }; value.push(escaped); } ch if ch <= '\u{001f}' => { return Err(self.error("Control characters must be escaped in strings")); } _ => { value.push(ch); self.bump(); } } } } fn hex_quad(&mut self) -> Result { let mut value = 0; for _ in 0..4 { let digit = self .peek() .and_then(|ch| ch.to_digit(16)) .ok_or_else(|| self.error("Expected four hexadecimal digits in Unicode escape"))?; self.bump(); value = value * 16 + digit; } Ok(value) } }