use std::collections::HashMap;
#[derive(Debug, Clone)]
pub enum Value {
Null,
Bool(bool),
Number(f64),
String(String),
Array(Vec<Value>),
Object(HashMap<String, Value>),
}
impl Value {
pub fn as_str(&self) -> Option<&str> {
match self {
Value::String(s) => Some(s.as_str()),
_ => None,
}
}
pub fn as_f64(&self) -> Option<f64> {
match self {
Value::Number(n) => Some(*n),
_ => None,
}
}
pub fn as_array(&self) -> Option<&[Value]> {
match self {
Value::Array(a) => Some(a.as_slice()),
_ => None,
}
}
pub fn get(&self, key: &str) -> Option<&Value> {
match self {
Value::Object(o) => o.get(key),
_ => None,
}
}
}
pub fn parse(s: &str) -> Result<Value, String> {
let mut p = Parser {
bytes: s.as_bytes(),
pos: 0,
};
p.skip_whitespace();
let v = p.parse_value()?;
p.skip_whitespace();
Ok(v)
}
struct Parser<'a> {
bytes: &'a [u8],
pos: usize,
}
impl<'a> Parser<'a> {
fn peek(&self) -> Option<u8> {
self.bytes.get(self.pos).copied()
}
fn advance(&mut self) -> Option<u8> {
let b = self.peek()?;
self.pos += 1;
Some(b)
}
fn skip_whitespace(&mut self) {
while let Some(b) = self.peek() {
if b == b' ' || b == b'\t' || b == b'\n' || b == b'\r' {
self.pos += 1;
} else {
break;
}
}
}
fn expect(&mut self, b: u8) -> Result<(), String> {
if self.peek() == Some(b) {
self.pos += 1;
Ok(())
} else {
Err(format!("expected {} at {}", b as char, self.pos))
}
}
fn parse_value(&mut self) -> Result<Value, String> {
self.skip_whitespace();
match self.peek() {
Some(b'{') => self.parse_object(),
Some(b'[') => self.parse_array(),
Some(b'"') => Ok(Value::String(self.parse_string()?)),
Some(b't') | Some(b'f') => self.parse_bool(),
Some(b'n') => self.parse_null(),
Some(c) if c == b'-' || c.is_ascii_digit() => self.parse_number(),
Some(c) => Err(format!("unexpected {} at {}", c as char, self.pos)),
None => Err("unexpected EOF".to_string()),
}
}
fn parse_object(&mut self) -> Result<Value, String> {
self.expect(b'{')?;
let mut map = HashMap::new();
self.skip_whitespace();
if self.peek() == Some(b'}') {
self.advance();
return Ok(Value::Object(map));
}
loop {
self.skip_whitespace();
let key = self.parse_string()?;
self.skip_whitespace();
self.expect(b':')?;
let val = self.parse_value()?;
map.insert(key, val);
self.skip_whitespace();
match self.peek() {
Some(b',') => {
self.advance();
}
Some(b'}') => {
self.advance();
return Ok(Value::Object(map));
}
Some(c) => return Err(format!("expected , or }} at {} got {}", self.pos, c as char)),
None => return Err("EOF in object".to_string()),
}
}
}
fn parse_array(&mut self) -> Result<Value, String> {
self.expect(b'[')?;
let mut items = Vec::new();
self.skip_whitespace();
if self.peek() == Some(b']') {
self.advance();
return Ok(Value::Array(items));
}
loop {
let v = self.parse_value()?;
items.push(v);
self.skip_whitespace();
match self.peek() {
Some(b',') => {
self.advance();
}
Some(b']') => {
self.advance();
return Ok(Value::Array(items));
}
Some(c) => return Err(format!("expected , or ] at {} got {}", self.pos, c as char)),
None => return Err("EOF in array".to_string()),
}
}
}
fn parse_string(&mut self) -> Result<String, String> {
self.expect(b'"')?;
let mut out = String::new();
loop {
match self.advance() {
Some(b'"') => return Ok(out),
Some(b'\\') => match self.advance() {
Some(b'"') => out.push('"'),
Some(b'\\') => out.push('\\'),
Some(b'/') => out.push('/'),
Some(b'n') => out.push('\n'),
Some(b'r') => out.push('\r'),
Some(b't') => out.push('\t'),
Some(b'b') => out.push('\u{0008}'),
Some(b'f') => out.push('\u{000C}'),
Some(b'u') => {
let mut hex = [0u8; 4];
for h in hex.iter_mut() {
*h = self.advance().ok_or("EOF in unicode escape")?;
}
let s = std::str::from_utf8(&hex).map_err(|e| e.to_string())?;
let cp = u32::from_str_radix(s, 16).map_err(|e| e.to_string())?;
if let Some(c) = char::from_u32(cp) {
out.push(c);
}
}
Some(c) => return Err(format!("bad escape {}", c as char)),
None => return Err("EOF in string escape".to_string()),
},
Some(c) => out.push(c as char),
None => return Err("EOF in string".to_string()),
}
}
}
fn parse_bool(&mut self) -> Result<Value, String> {
if self.bytes[self.pos..].starts_with(b"true") {
self.pos += 4;
Ok(Value::Bool(true))
} else if self.bytes[self.pos..].starts_with(b"false") {
self.pos += 5;
Ok(Value::Bool(false))
} else {
Err("expected bool".to_string())
}
}
fn parse_null(&mut self) -> Result<Value, String> {
if self.bytes[self.pos..].starts_with(b"null") {
self.pos += 4;
Ok(Value::Null)
} else {
Err("expected null".to_string())
}
}
fn parse_number(&mut self) -> Result<Value, String> {
let start = self.pos;
if self.peek() == Some(b'-') {
self.pos += 1;
}
while let Some(c) = self.peek() {
if c.is_ascii_digit() {
self.pos += 1;
} else {
break;
}
}
if self.peek() == Some(b'.') {
self.pos += 1;
while let Some(c) = self.peek() {
if c.is_ascii_digit() {
self.pos += 1;
} else {
break;
}
}
}
if matches!(self.peek(), Some(b'e') | Some(b'E')) {
self.pos += 1;
if matches!(self.peek(), Some(b'+') | Some(b'-')) {
self.pos += 1;
}
while let Some(c) = self.peek() {
if c.is_ascii_digit() {
self.pos += 1;
} else {
break;
}
}
}
let s = std::str::from_utf8(&self.bytes[start..self.pos]).map_err(|e| e.to_string())?;
s.parse::<f64>().map(Value::Number).map_err(|e| e.to_string())
}
}