use thiserror::Error;
#[derive(Debug, Error, PartialEq, Eq)]
pub enum JsonError {
#[error("not valid JSON")]
Invalid,
#[error("not a JSON object")]
NotAnObject,
#[error("path does not resolve to a scalar value")]
PathNotScalar {
path: String,
},
}
#[derive(Debug)]
pub struct TopLevel {
pub insert_at: usize,
pub empty: bool,
pub keys: Vec<String>,
}
pub fn object_top_level(body: &[u8]) -> Result<TopLevel, JsonError> {
let mut p = Parser::new(body);
p.skip_ws();
if p.peek() != Some(b'{') {
return Err(match validate(body) {
Ok(()) => JsonError::NotAnObject,
Err(e) => e,
});
}
let top = p.object_members()?;
p.skip_ws();
if p.peek().is_some() {
return Err(JsonError::Invalid);
}
Ok(top)
}
pub fn scalar_at_path<'a, I>(body: &[u8], segments: I) -> Result<String, JsonError>
where
I: IntoIterator<Item = &'a str>,
{
let mut p = Parser::new(body);
let mut walked: Vec<&str> = Vec::new();
for segment in segments {
walked.push(segment);
p.enter_field(segment)
.ok_or_else(|| JsonError::PathNotScalar {
path: walked.join("."),
})?;
}
p.skip_ws();
p.scalar_string().ok_or_else(|| JsonError::PathNotScalar {
path: walked.join("."),
})
}
pub fn validate(body: &[u8]) -> Result<(), JsonError> {
let mut p = Parser::new(body);
p.skip_value()?;
p.skip_ws();
if p.peek().is_some() {
return Err(JsonError::Invalid);
}
Ok(())
}
struct Parser<'a> {
b: &'a [u8],
i: usize,
}
impl<'a> Parser<'a> {
fn new(b: &'a [u8]) -> Self {
Self { b, i: 0 }
}
fn peek(&self) -> Option<u8> {
self.b.get(self.i).copied()
}
fn skip_ws(&mut self) {
while matches!(self.peek(), Some(b' ' | b'\t' | b'\n' | b'\r')) {
self.i += 1;
}
}
fn object_members(&mut self) -> Result<TopLevel, JsonError> {
debug_assert_eq!(self.peek(), Some(b'{'));
self.i += 1; let insert_at = self.i;
let mut keys = Vec::new();
self.skip_ws();
if self.peek() == Some(b'}') {
self.i += 1;
return Ok(TopLevel {
insert_at,
empty: true,
keys,
});
}
loop {
self.skip_ws();
keys.push(self.string_decode()?);
self.skip_ws();
self.expect(b':')?;
self.skip_value()?;
self.skip_ws();
match self.peek() {
Some(b',') => self.i += 1,
Some(b'}') => {
self.i += 1;
break;
}
_ => return Err(JsonError::Invalid),
}
}
Ok(TopLevel {
insert_at,
empty: false,
keys,
})
}
fn enter_field(&mut self, key: &str) -> Option<()> {
self.skip_ws();
if self.peek() != Some(b'{') {
return None;
}
self.i += 1;
self.skip_ws();
if self.peek() == Some(b'}') {
return None;
}
loop {
self.skip_ws();
let k = self.string_decode().ok()?;
self.skip_ws();
self.expect(b':').ok()?;
self.skip_ws();
if k == key {
return Some(());
}
self.skip_value().ok()?;
self.skip_ws();
match self.peek() {
Some(b',') => self.i += 1,
_ => return None,
}
}
}
fn scalar_string(&mut self) -> Option<String> {
match self.peek()? {
b'"' => self.string_decode().ok(),
b't' => self.literal(b"true").ok().map(|()| "true".to_owned()),
b'f' => self.literal(b"false").ok().map(|()| "false".to_owned()),
c if c == b'-' || c.is_ascii_digit() => {
let start = self.i;
self.number().ok()?;
std::str::from_utf8(&self.b[start..self.i])
.ok()
.map(str::to_owned)
}
_ => None,
}
}
fn expect(&mut self, byte: u8) -> Result<(), JsonError> {
if self.peek() == Some(byte) {
self.i += 1;
Ok(())
} else {
Err(JsonError::Invalid)
}
}
fn skip_value(&mut self) -> Result<(), JsonError> {
self.skip_ws();
match self.peek().ok_or(JsonError::Invalid)? {
b'{' => self.skip_object(),
b'[' => self.skip_array(),
b'"' => self.skip_string(),
b't' => self.literal(b"true"),
b'f' => self.literal(b"false"),
b'n' => self.literal(b"null"),
c if c == b'-' || c.is_ascii_digit() => self.number(),
_ => Err(JsonError::Invalid),
}
}
fn skip_object(&mut self) -> Result<(), JsonError> {
self.i += 1; self.skip_ws();
if self.peek() == Some(b'}') {
self.i += 1;
return Ok(());
}
loop {
self.skip_ws();
self.skip_string()?;
self.skip_ws();
self.expect(b':')?;
self.skip_value()?;
self.skip_ws();
match self.peek() {
Some(b',') => self.i += 1,
Some(b'}') => {
self.i += 1;
return Ok(());
}
_ => return Err(JsonError::Invalid),
}
}
}
fn skip_array(&mut self) -> Result<(), JsonError> {
self.i += 1; self.skip_ws();
if self.peek() == Some(b']') {
self.i += 1;
return Ok(());
}
loop {
self.skip_value()?;
self.skip_ws();
match self.peek() {
Some(b',') => self.i += 1,
Some(b']') => {
self.i += 1;
return Ok(());
}
_ => return Err(JsonError::Invalid),
}
}
}
fn skip_string(&mut self) -> Result<(), JsonError> {
self.expect(b'"')?;
loop {
match self.peek().ok_or(JsonError::Invalid)? {
b'"' => {
self.i += 1;
return Ok(());
}
b'\\' => {
self.i += 1;
let esc = self.peek().ok_or(JsonError::Invalid)?;
self.i += 1;
if esc == b'u' {
for _ in 0..4 {
self.hex_digit()?;
}
}
}
c if c < 0x20 => return Err(JsonError::Invalid),
_ => self.i += 1,
}
}
}
fn string_decode(&mut self) -> Result<String, JsonError> {
self.expect(b'"')?;
let mut out: Vec<u8> = Vec::new();
loop {
match self.peek().ok_or(JsonError::Invalid)? {
b'"' => {
self.i += 1;
return String::from_utf8(out).map_err(|_| JsonError::Invalid);
}
b'\\' => {
self.i += 1;
self.decode_escape(&mut out)?;
}
c if c < 0x20 => return Err(JsonError::Invalid),
_ => {
out.push(self.b[self.i]);
self.i += 1;
}
}
}
}
fn decode_escape(&mut self, out: &mut Vec<u8>) -> Result<(), JsonError> {
let esc = self.peek().ok_or(JsonError::Invalid)?;
self.i += 1;
let ch = match esc {
b'"' => '"',
b'\\' => '\\',
b'/' => '/',
b'b' => '\u{0008}',
b'f' => '\u{000C}',
b'n' => '\n',
b'r' => '\r',
b't' => '\t',
b'u' => return self.decode_unicode_escape(out),
_ => return Err(JsonError::Invalid),
};
push_char(out, ch);
Ok(())
}
fn decode_unicode_escape(&mut self, out: &mut Vec<u8>) -> Result<(), JsonError> {
let hi = self.hex4()?;
let code = if (0xD800..=0xDBFF).contains(&hi) {
self.expect(b'\\')?;
self.expect(b'u')?;
let lo = self.hex4()?;
if !(0xDC00..=0xDFFF).contains(&lo) {
return Err(JsonError::Invalid);
}
0x1_0000 + ((u32::from(hi) - 0xD800) << 10) + (u32::from(lo) - 0xDC00)
} else if (0xDC00..=0xDFFF).contains(&hi) {
return Err(JsonError::Invalid); } else {
u32::from(hi)
};
push_char(out, char::from_u32(code).ok_or(JsonError::Invalid)?);
Ok(())
}
fn hex4(&mut self) -> Result<u16, JsonError> {
let mut v: u16 = 0;
for _ in 0..4 {
let d = self.hex_digit()?;
v = v * 16 + u16::from(d);
}
Ok(v)
}
fn hex_digit(&mut self) -> Result<u8, JsonError> {
let c = self.peek().ok_or(JsonError::Invalid)?;
let v = match c {
b'0'..=b'9' => c - b'0',
b'a'..=b'f' => c - b'a' + 10,
b'A'..=b'F' => c - b'A' + 10,
_ => return Err(JsonError::Invalid),
};
self.i += 1;
Ok(v)
}
fn number(&mut self) -> Result<(), JsonError> {
if self.peek() == Some(b'-') {
self.i += 1;
}
match self.peek() {
Some(b'0') => self.i += 1,
Some(c) if c.is_ascii_digit() => self.digits(),
_ => return Err(JsonError::Invalid),
}
if self.peek() == Some(b'.') {
self.i += 1;
self.one_or_more_digits()?;
}
if matches!(self.peek(), Some(b'e' | b'E')) {
self.i += 1;
if matches!(self.peek(), Some(b'+' | b'-')) {
self.i += 1;
}
self.one_or_more_digits()?;
}
Ok(())
}
fn digits(&mut self) {
while matches!(self.peek(), Some(c) if c.is_ascii_digit()) {
self.i += 1;
}
}
fn one_or_more_digits(&mut self) -> Result<(), JsonError> {
if !matches!(self.peek(), Some(c) if c.is_ascii_digit()) {
return Err(JsonError::Invalid);
}
self.digits();
Ok(())
}
fn literal(&mut self, lit: &[u8]) -> Result<(), JsonError> {
if self.b[self.i..].starts_with(lit) {
self.i += lit.len();
Ok(())
} else {
Err(JsonError::Invalid)
}
}
}
fn push_char(out: &mut Vec<u8>, ch: char) {
let mut buf = [0u8; 4];
out.extend_from_slice(ch.encode_utf8(&mut buf).as_bytes());
}
#[cfg(test)]
#[path = "json_tests.rs"]
mod tests;