use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum JsonValue {
Null,
Bool(bool),
Number(u64),
String(String),
Array(Vec<JsonValue>),
Object(Vec<(String, JsonValue)>),
}
impl JsonValue {
#[inline]
pub fn as_u64(&self) -> Option<u64> {
match self {
Self::Number(value) => Some(*value),
_ => None,
}
}
#[inline]
pub fn as_str(&self) -> Option<&str> {
match self {
Self::String(value) => Some(value),
_ => None,
}
}
#[inline]
pub fn as_array(&self) -> Option<&[JsonValue]> {
match self {
Self::Array(values) => Some(values),
_ => None,
}
}
#[inline]
pub fn as_object(&self) -> Option<&[(String, JsonValue)]> {
match self {
Self::Object(entries) => Some(entries),
_ => None,
}
}
#[inline]
pub fn get(&self, key: &str) -> Option<&JsonValue> {
self.as_object()?
.iter()
.find_map(|(entry_key, value)| (entry_key == key).then_some(value))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum JsonError {
UnexpectedEnd,
UnexpectedToken(u8),
InvalidEscape,
InvalidNumber,
TrailingData,
}
impl fmt::Display for JsonError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::UnexpectedEnd => f.write_str("unexpected end of JSON"),
Self::UnexpectedToken(token) => write!(f, "unexpected JSON token 0x{token:02x}"),
Self::InvalidEscape => f.write_str("invalid JSON escape"),
Self::InvalidNumber => f.write_str("invalid JSON number"),
Self::TrailingData => f.write_str("trailing JSON data"),
}
}
}
impl std::error::Error for JsonError {}
pub fn parse(input: &str) -> Result<JsonValue, JsonError> {
let mut parser = Parser::new(input.as_bytes());
let value = parser.parse_value()?;
parser.skip_ws();
if parser.is_eof() {
Ok(value)
} else {
Err(JsonError::TrailingData)
}
}
struct Parser<'a> {
input: &'a [u8],
index: usize,
}
impl<'a> Parser<'a> {
#[inline]
fn new(input: &'a [u8]) -> Self {
Self { input, index: 0 }
}
#[inline]
fn is_eof(&self) -> bool {
self.index >= self.input.len()
}
#[inline]
fn peek(&self) -> Option<u8> {
self.input.get(self.index).copied()
}
#[inline]
fn bump(&mut self) -> Option<u8> {
let value = self.peek()?;
self.index += 1;
Some(value)
}
fn skip_ws(&mut self) {
while matches!(self.peek(), Some(b' ' | b'\n' | b'\r' | b'\t')) {
self.index += 1;
}
}
fn parse_value(&mut self) -> Result<JsonValue, JsonError> {
self.skip_ws();
match self.bump().ok_or(JsonError::UnexpectedEnd)? {
b'n' => {
self.expect_bytes(b"ull")?;
Ok(JsonValue::Null)
}
b't' => {
self.expect_bytes(b"rue")?;
Ok(JsonValue::Bool(true))
}
b'f' => {
self.expect_bytes(b"alse")?;
Ok(JsonValue::Bool(false))
}
b'"' => Ok(JsonValue::String(self.parse_string()?)),
b'[' => self.parse_array(),
b'{' => self.parse_object(),
byte @ b'0'..=b'9' => self.parse_number(byte),
token => Err(JsonError::UnexpectedToken(token)),
}
}
fn expect_bytes(&mut self, bytes: &[u8]) -> Result<(), JsonError> {
for expected in bytes {
let got = self.bump().ok_or(JsonError::UnexpectedEnd)?;
if got != *expected {
return Err(JsonError::UnexpectedToken(got));
}
}
Ok(())
}
fn parse_string(&mut self) -> Result<String, JsonError> {
let mut output = String::new();
loop {
match self.bump().ok_or(JsonError::UnexpectedEnd)? {
b'"' => return Ok(output),
b'\\' => {
let escaped = self.bump().ok_or(JsonError::UnexpectedEnd)?;
match escaped {
b'"' => output.push('"'),
b'\\' => output.push('\\'),
b'/' => output.push('/'),
b'b' => output.push('\u{0008}'),
b'f' => output.push('\u{000c}'),
b'n' => output.push('\n'),
b'r' => output.push('\r'),
b't' => output.push('\t'),
_ => return Err(JsonError::InvalidEscape),
}
}
byte if byte.is_ascii() => output.push(byte as char),
token => return Err(JsonError::UnexpectedToken(token)),
}
}
}
fn parse_number(&mut self, first: u8) -> Result<JsonValue, JsonError> {
let mut value = (first - b'0') as u64;
while let Some(byte @ b'0'..=b'9') = self.peek() {
self.index += 1;
value = value
.checked_mul(10)
.and_then(|value| value.checked_add((byte - b'0') as u64))
.ok_or(JsonError::InvalidNumber)?;
}
Ok(JsonValue::Number(value))
}
fn parse_array(&mut self) -> Result<JsonValue, JsonError> {
let mut values = Vec::new();
loop {
self.skip_ws();
if matches!(self.peek(), Some(b']')) {
self.index += 1;
return Ok(JsonValue::Array(values));
}
values.push(self.parse_value()?);
self.skip_ws();
match self.bump().ok_or(JsonError::UnexpectedEnd)? {
b',' => {}
b']' => return Ok(JsonValue::Array(values)),
token => return Err(JsonError::UnexpectedToken(token)),
}
}
}
fn parse_object(&mut self) -> Result<JsonValue, JsonError> {
let mut entries = Vec::new();
loop {
self.skip_ws();
if matches!(self.peek(), Some(b'}')) {
self.index += 1;
return Ok(JsonValue::Object(entries));
}
if self.bump() != Some(b'"') {
return Err(JsonError::UnexpectedToken(self.peek().unwrap_or_default()));
}
let key = self.parse_string()?;
self.skip_ws();
if self.bump() != Some(b':') {
return Err(JsonError::UnexpectedToken(self.peek().unwrap_or_default()));
}
let value = self.parse_value()?;
entries.push((key, value));
self.skip_ws();
match self.bump().ok_or(JsonError::UnexpectedEnd)? {
b',' => {}
b'}' => return Ok(JsonValue::Object(entries)),
token => return Err(JsonError::UnexpectedToken(token)),
}
}
}
}