use std::fmt;
use std::string::ToString;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ParseError {
pub offset: usize,
pub message: String,
}
impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "json parse error at offset {}: {}", self.offset, self.message)
}
}
impl std::error::Error for ParseError {}
impl ParseError {
fn at(offset: usize, message: &str) -> Self {
ParseError {
offset,
message: message.to_string(),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Json {
Null,
Bool(bool),
Number(String),
String(String),
Array(Vec<Json>),
Object(Vec<(String, Json)>),
}
impl Json {
pub fn as_str(&self) -> Option<&str> {
match self {
Json::String(s) => Some(s.as_str()),
_ => None,
}
}
pub fn as_i64(&self) -> Option<i64> {
match self {
Json::Number(s) => s.parse::<i64>().ok(),
_ => None,
}
}
pub fn as_bool(&self) -> Option<bool> {
match self {
Json::Bool(b) => Some(*b),
_ => None,
}
}
pub fn as_array(&self) -> Option<&[Json]> {
match self {
Json::Array(a) => Some(a.as_slice()),
_ => None,
}
}
pub fn as_object(&self) -> Option<&[(String, Json)]> {
match self {
Json::Object(o) => Some(o.as_slice()),
_ => None,
}
}
pub fn get(&self, key: &str) -> Option<&Json> {
match self {
Json::Object(entries) => entries
.iter()
.find_map(|(k, v)| if k == key { Some(v) } else { None }),
_ => None,
}
}
}
struct Parser<'a> {
bytes: &'a [u8],
pos: usize,
}
impl<'a> Parser<'a> {
fn new(input: &'a str) -> Self {
Parser {
bytes: input.as_bytes(),
pos: 0,
}
}
fn peek(&self) -> Option<u8> {
self.bytes.get(self.pos).copied()
}
fn bump(&mut self) -> Option<u8> {
let c = self.peek()?;
self.pos += 1;
Some(c)
}
fn err(&self, msg: &str) -> ParseError {
ParseError::at(self.pos, msg)
}
fn skip_ws(&mut self) {
while let Some(c) = self.peek() {
match c {
b' ' | b'\t' | b'\n' | b'\r' => {
self.pos += 1;
}
_ => break,
}
}
}
fn parse_value(&mut self) -> Result<Json, ParseError> {
self.skip_ws();
let c = self
.peek()
.ok_or_else(|| self.err("unexpected end of input"))?;
let value = match c {
b'{' => self.parse_object()?,
b'[' => self.parse_array()?,
b'"' => Json::String(self.parse_string()?),
b't' | b'f' => self.parse_bool()?,
b'n' => self.parse_null()?,
b'-' | b'0'..=b'9' => self.parse_number()?,
other => return Err(self.err(&format!("unexpected character {:#x}", other))),
};
Ok(value)
}
fn parse_object(&mut self) -> Result<Json, ParseError> {
self.bump(); let mut entries: Vec<(String, Json)> = Vec::new();
self.skip_ws();
if self.peek() == Some(b'}') {
self.bump();
return Ok(Json::Object(entries));
}
loop {
self.skip_ws();
if self.peek() != Some(b'"') {
return Err(self.err("expected string key in object"));
}
let key = self.parse_string()?;
self.skip_ws();
if self.peek() != Some(b':') {
return Err(self.err("expected ':' after object key"));
}
self.bump(); let val = self.parse_value()?;
entries.push((key, val));
self.skip_ws();
match self.peek() {
Some(b',') => {
self.bump();
continue;
}
Some(b'}') => {
self.bump();
break;
}
_ => return Err(self.err("expected ',' or '}' in object")),
}
}
Ok(Json::Object(entries))
}
fn parse_array(&mut self) -> Result<Json, ParseError> {
self.bump(); let mut items: Vec<Json> = Vec::new();
self.skip_ws();
if self.peek() == Some(b']') {
self.bump();
return Ok(Json::Array(items));
}
loop {
let val = self.parse_value()?;
items.push(val);
self.skip_ws();
match self.peek() {
Some(b',') => {
self.bump();
continue;
}
Some(b']') => {
self.bump();
break;
}
_ => return Err(self.err("expected ',' or ']' in array")),
}
}
Ok(Json::Array(items))
}
fn parse_string(&mut self) -> Result<String, ParseError> {
if self.bump() != Some(b'"') {
return Err(self.err("expected opening '\"'"));
}
let mut out = String::new();
loop {
let c = self
.bump()
.ok_or_else(|| self.err("unterminated string"))?;
match c {
b'"' => break,
b'\\' => {
let esc = self
.bump()
.ok_or_else(|| self.err("trailing escape"))?;
match esc {
b'"' => out.push('"'),
b'\\' => out.push('\\'),
b'/' => out.push('/'),
b'b' => out.push('\u{0008}'),
b'f' => out.push('\u{000C}'),
b'n' => out.push('\n'),
b'r' => out.push('\r'),
b't' => out.push('\t'),
b'u' => {
let cp = self.parse_unicode_escape()?;
out.push(cp);
}
other => {
return Err(self.err(&format!("invalid escape \\{}", other as char)))
}
}
}
0x00..=0x1F => return Err(self.err("unescaped control character in string")),
0x80..=0xFF => {
let cp = self.continue_utf8(c)?;
out.push(cp);
}
_ => out.push(c as char),
}
}
Ok(out)
}
fn parse_unicode_escape(&mut self) -> Result<char, ParseError> {
let hi = self.parse_hex4()?;
if (0xD800..=0xDBFF).contains(&hi) {
if self.bump() != Some(b'\\') || self.bump() != Some(b'u') {
return Err(self.err("expected '\\u' low surrogate after high surrogate"));
}
let lo = self.parse_hex4()?;
if !(0xDC00..=0xDFFF).contains(&lo) {
return Err(self.err("invalid low surrogate"));
}
let codepoint = 0x10000 + (((hi - 0xD800) << 10) | (lo - 0xDC00));
char::from_u32(codepoint).ok_or_else(|| self.err("invalid surrogate pair codepoint"))
} else if (0xDC00..=0xDFFF).contains(&hi) {
Err(self.err("unexpected low surrogate without preceding high surrogate"))
} else {
char::from_u32(hi).ok_or_else(|| self.err("invalid codepoint"))
}
}
fn parse_hex4(&mut self) -> Result<u32, ParseError> {
let mut acc: u32 = 0;
for _ in 0..4 {
let c = self
.bump()
.ok_or_else(|| self.err("truncated \\u escape"))?;
let d = match c {
b'0'..=b'9' => (c - b'0') as u32,
b'a'..=b'f' => (c - b'a' + 10) as u32,
b'A'..=b'F' => (c - b'A' + 10) as u32,
_ => return Err(self.err("invalid hex digit in \\u escape")),
};
acc = acc * 16 + d;
}
Ok(acc)
}
fn continue_utf8(&mut self, first: u8) -> Result<char, ParseError> {
let (len, mut codepoint) = if first & 0xE0 == 0xC0 {
(2, (first & 0x1F) as u32)
} else if first & 0xF0 == 0xE0 {
(3, (first & 0x0F) as u32)
} else if first & 0xF8 == 0xF0 {
(4, (first & 0x07) as u32)
} else {
return Err(self.err("invalid UTF-8 lead byte"));
};
for _ in 1..len {
let c = self
.bump()
.ok_or_else(|| self.err("truncated UTF-8 sequence"))?;
if c & 0xC0 != 0x80 {
return Err(self.err("invalid UTF-8 continuation byte"));
}
codepoint = (codepoint << 6) | ((c & 0x3F) as u32);
}
char::from_u32(codepoint).ok_or_else(|| self.err("invalid UTF-8 codepoint"))
}
fn parse_bool(&mut self) -> Result<Json, ParseError> {
if self.bytes[self.pos..].starts_with(b"true") {
self.pos += 4;
Ok(Json::Bool(true))
} else if self.bytes[self.pos..].starts_with(b"false") {
self.pos += 5;
Ok(Json::Bool(false))
} else {
Err(self.err("expected 'true' or 'false'"))
}
}
fn parse_null(&mut self) -> Result<Json, ParseError> {
if self.bytes[self.pos..].starts_with(b"null") {
self.pos += 4;
Ok(Json::Null)
} else {
Err(self.err("expected 'null'"))
}
}
fn parse_number(&mut self) -> Result<Json, ParseError> {
let start = self.pos;
if self.peek() == Some(b'-') {
self.bump();
}
match self.peek() {
Some(b'0') => {
self.bump();
}
Some(c) if c.is_ascii_digit() => {
while self.peek().map_or(false, |c| c.is_ascii_digit()) {
self.bump();
}
}
_ => return Err(self.err("expected digit in number")),
}
if self.peek() == Some(b'.') {
self.bump();
if !self.peek().map_or(false, |c| c.is_ascii_digit()) {
return Err(self.err("expected digit after decimal point"));
}
while self.peek().map_or(false, |c| c.is_ascii_digit()) {
self.bump();
}
}
if matches!(self.peek(), Some(b'e') | Some(b'E')) {
self.bump();
if matches!(self.peek(), Some(b'+') | Some(b'-')) {
self.bump();
}
if !self.peek().map_or(false, |c| c.is_ascii_digit()) {
return Err(self.err("expected digit in exponent"));
}
while self.peek().map_or(false, |c| c.is_ascii_digit()) {
self.bump();
}
}
let text = core::str::from_utf8(&self.bytes[start..self.pos])
.map_err(|_| self.err("non-utf8 number literal"))?;
Ok(Json::Number(text.to_string()))
}
}
pub fn parse(input: &str) -> Result<Json, ParseError> {
let mut p = Parser::new(input);
let value = p.parse_value()?;
p.skip_ws();
if p.pos != p.bytes.len() {
return Err(ParseError::at(p.pos, "trailing characters after JSON value"));
}
Ok(value)
}