use std::collections::HashMap;
use std::fmt;
use std::str::Chars;
use std::iter::Peekable;
#[derive(Debug, Clone, PartialEq)]
pub enum Json {
Null,
Bool(bool),
Int(i64),
Float(f64),
Str(String),
Array(Vec<Json>),
Object(HashMap<String, Json>),
}
impl Json {
pub fn null() -> Self { Json::Null }
pub fn bool(v: bool) -> Self { Json::Bool(v) }
pub fn int(v: i64) -> Self { Json::Int(v) }
pub fn float(v: f64) -> Self { Json::Float(v) }
pub fn str(v: impl Into<String>) -> Self { Json::Str(v.into()) }
pub fn array(v: Vec<Json>) -> Self { Json::Array(v) }
pub fn object(v: HashMap<String, Json>) -> Self { Json::Object(v) }
pub fn new_object() -> Self { Json::Object(HashMap::new()) }
pub fn new_array() -> Self { Json::Array(Vec::new()) }
}
impl Json {
pub fn is_null(&self) -> bool { matches!(self, Json::Null) }
pub fn is_bool(&self) -> bool { matches!(self, Json::Bool(_)) }
pub fn is_int(&self) -> bool { matches!(self, Json::Int(_)) }
pub fn is_float(&self) -> bool { matches!(self, Json::Float(_)) }
pub fn is_number(&self) -> bool { self.is_int() || self.is_float() }
pub fn is_str(&self) -> bool { matches!(self, Json::Str(_)) }
pub fn is_array(&self) -> bool { matches!(self, Json::Array(_)) }
pub fn is_object(&self) -> bool { matches!(self, Json::Object(_)) }
}
impl Json {
pub fn as_bool(&self) -> Option<bool> {
if let Json::Bool(v) = self { Some(*v) } else { None }
}
pub fn as_i64(&self) -> Option<i64> {
match self {
Json::Int(v) => Some(*v),
Json::Float(v) => Some(*v as i64),
_ => None,
}
}
pub fn as_f64(&self) -> Option<f64> {
match self {
Json::Float(v) => Some(*v),
Json::Int(v) => Some(*v as f64),
_ => None,
}
}
pub fn as_str(&self) -> Option<&str> {
if let Json::Str(v) = self { Some(v) } else { None }
}
pub fn as_array(&self) -> Option<&Vec<Json>> {
if let Json::Array(v) = self { Some(v) } else { None }
}
pub fn as_array_mut(&mut self) -> Option<&mut Vec<Json>> {
if let Json::Array(v) = self { Some(v) } else { None }
}
pub fn as_object(&self) -> Option<&HashMap<String, Json>> {
if let Json::Object(v) = self { Some(v) } else { None }
}
pub fn as_object_mut(&mut self) -> Option<&mut HashMap<String, Json>> {
if let Json::Object(v) = self { Some(v) } else { None }
}
pub fn get(&self, key: &str) -> &Json {
match self {
Json::Object(map) => map.get(key).unwrap_or(&Json::Null),
_ => &Json::Null,
}
}
pub fn get_path(&self, path: &str) -> Option<&Json> {
let mut current = self;
for key in path.split('.') {
match current {
Json::Object(map) => {
current = map.get(key)?;
}
Json::Array(arr) => {
let idx: usize = key.parse().ok()?;
current = arr.get(idx)?;
}
_ => return None,
}
}
Some(current)
}
pub fn len(&self) -> usize {
match self {
Json::Object(m) => m.len(),
Json::Array(a) => a.len(),
Json::Str(s) => s.len(),
_ => 0,
}
}
pub fn is_empty(&self) -> bool { self.len() == 0 }
}
impl Json {
pub fn set(&mut self, key: impl Into<String>, value: impl Into<Json>) -> &mut Self {
if let Json::Object(map) = self {
map.insert(key.into(), value.into());
} else {
panic!("Json::set called on non-Object");
}
self
}
pub fn remove(&mut self, key: &str) -> Option<Json> {
if let Json::Object(map) = self {
map.remove(key)
} else {
None
}
}
pub fn push(&mut self, value: impl Into<Json>) -> &mut Self {
if let Json::Array(arr) = self {
arr.push(value.into());
} else {
panic!("Json::push called on non-Array");
}
self
}
pub fn merge(&mut self, other: &Json) -> &mut Self {
if let (Json::Object(dst), Json::Object(src)) = (&mut *self, other) {
for (k, v) in src {
dst.insert(k.clone(), v.clone());
}
}
self
}
pub fn contains_key(&self, key: &str) -> bool {
matches!(self, Json::Object(m) if m.contains_key(key))
}
}
impl Json {
pub fn iter_array(&self) -> impl Iterator<Item = &Json> {
match self {
Json::Array(v) => v.iter(),
_ => [].iter(),
}
}
pub fn iter_object(&self) -> impl Iterator<Item = (&String, &Json)> {
static EMPTY: std::sync::OnceLock<HashMap<String, Json>> = std::sync::OnceLock::new();
match self {
Json::Object(m) => m.iter(),
_ => EMPTY.get_or_init(HashMap::new).iter(),
}
}
pub fn keys(&self) -> Vec<&String> {
match self {
Json::Object(m) => m.keys().collect(),
_ => vec![],
}
}
pub fn values(&self) -> Vec<&Json> {
match self {
Json::Object(m) => m.values().collect(),
_ => vec![],
}
}
}
impl Json {
pub fn to_json(&self) -> String {
let mut buf = String::new();
self.write_json(&mut buf, None, 0);
buf
}
pub fn to_pretty(&self, indent: &str) -> String {
let mut buf = String::new();
self.write_json(&mut buf, Some(indent), 0);
buf
}
fn write_json(&self, buf: &mut String, indent: Option<&str>, depth: usize) {
match self {
Json::Null => buf.push_str("null"),
Json::Bool(b) => buf.push_str(if *b { "true" } else { "false" }),
Json::Int(n) => buf.push_str(&n.to_string()),
Json::Float(f) => {
if f.fract() == 0.0 && f.is_finite() {
buf.push_str(&format!("{:.1}", f));
} else {
buf.push_str(&f.to_string());
}
}
Json::Str(s) => {
buf.push('"');
for c in s.chars() {
match c {
'"' => buf.push_str("\\\""),
'\\' => buf.push_str("\\\\"),
'\n' => buf.push_str("\\n"),
'\r' => buf.push_str("\\r"),
'\t' => buf.push_str("\\t"),
c if (c as u32) < 0x20 => {
buf.push_str(&format!("\\u{:04x}", c as u32));
}
c => buf.push(c),
}
}
buf.push('"');
}
Json::Array(arr) => {
if arr.is_empty() { buf.push_str("[]"); return; }
buf.push('[');
for (i, v) in arr.iter().enumerate() {
if let Some(ind) = indent {
buf.push('\n');
for _ in 0..=depth { buf.push_str(ind); }
}
v.write_json(buf, indent, depth + 1);
if i + 1 < arr.len() { buf.push(','); }
}
if let Some(ind) = indent {
buf.push('\n');
for _ in 0..depth { buf.push_str(ind); }
}
buf.push(']');
}
Json::Object(map) => {
if map.is_empty() { buf.push_str("{}"); return; }
buf.push('{');
let mut keys: Vec<&String> = map.keys().collect();
keys.sort(); for (i, k) in keys.iter().enumerate() {
if let Some(ind) = indent {
buf.push('\n');
for _ in 0..=depth { buf.push_str(ind); }
}
let key_json = Json::Str(k.to_string());
key_json.write_json(buf, indent, depth + 1);
buf.push(':');
if indent.is_some() { buf.push(' '); }
map[*k].write_json(buf, indent, depth + 1);
if i + 1 < keys.len() { buf.push(','); }
}
if let Some(ind) = indent {
buf.push('\n');
for _ in 0..depth { buf.push_str(ind); }
}
buf.push('}');
}
}
}
}
impl fmt::Display for Json {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.to_json())
}
}
impl From<bool> for Json { fn from(v: bool) -> Self { Json::Bool(v) } }
impl From<i64> for Json { fn from(v: i64) -> Self { Json::Int(v) } }
impl From<i32> for Json { fn from(v: i32) -> Self { Json::Int(v as i64) } }
impl From<u64> for Json { fn from(v: u64) -> Self { Json::Int(v as i64) } }
impl From<usize> for Json { fn from(v: usize) -> Self { Json::Int(v as i64) } }
impl From<f64> for Json { fn from(v: f64) -> Self { Json::Float(v) } }
impl From<f32> for Json { fn from(v: f32) -> Self { Json::Float(v as f64) } }
impl From<String> for Json { fn from(v: String) -> Self { Json::Str(v) } }
impl From<&str> for Json { fn from(v: &str) -> Self { Json::Str(v.to_owned()) } }
impl<T: Into<Json>> From<Vec<T>> for Json {
fn from(v: Vec<T>) -> Self { Json::Array(v.into_iter().map(Into::into).collect()) }
}
impl<T: Into<Json>> From<Option<T>> for Json {
fn from(v: Option<T>) -> Self { v.map(Into::into).unwrap_or(Json::Null) }
}
impl std::ops::Index<&str> for Json {
type Output = Json;
fn index(&self, key: &str) -> &Json { self.get(key) }
}
impl std::ops::Index<usize> for Json {
type Output = Json;
fn index(&self, idx: usize) -> &Json {
match self {
Json::Array(arr) => arr.get(idx).unwrap_or(&Json::Null),
_ => &Json::Null,
}
}
}
#[derive(Debug)]
pub struct ParseError(pub String);
impl fmt::Display for ParseError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "ParseError: {}", self.0) } }
impl std::error::Error for ParseError {}
impl Json {
pub fn parse(src: &str) -> Result<Json, ParseError> {
let mut chars = src.chars().peekable();
let val = parse_value(&mut chars)?;
skip_ws(&mut chars);
if chars.peek().is_some() {
return Err(ParseError("trailing characters after JSON value".into()));
}
Ok(val)
}
}
type Iter<'a> = Peekable<Chars<'a>>;
fn skip_ws(it: &mut Iter) {
while matches!(it.peek(), Some(' ' | '\t' | '\n' | '\r')) { it.next(); }
}
fn parse_value(it: &mut Iter) -> Result<Json, ParseError> {
skip_ws(it);
match it.peek().copied() {
Some('"') => parse_string(it).map(Json::Str),
Some('{') => parse_object(it),
Some('[') => parse_array(it),
Some('t') => { expect_str(it, "true")?; Ok(Json::Bool(true)) }
Some('f') => { expect_str(it, "false")?; Ok(Json::Bool(false)) }
Some('n') => { expect_str(it, "null")?; Ok(Json::Null) }
Some(c) if c == '-' || c.is_ascii_digit() => parse_number(it),
Some(c) => Err(ParseError(format!("unexpected character '{}'", c))),
None => Err(ParseError("unexpected end of input".into())),
}
}
fn expect_str(it: &mut Iter, s: &str) -> Result<(), ParseError> {
for c in s.chars() {
match it.next() {
Some(x) if x == c => {}
other => return Err(ParseError(format!("expected '{}', got {:?}", c, other))),
}
}
Ok(())
}
fn parse_string(it: &mut Iter) -> Result<String, ParseError> {
it.next(); let mut out = String::new();
loop {
match it.next() {
Some('"') => return Ok(out),
Some('\\') => {
match it.next() {
Some('"') => out.push('"'),
Some('\\') => out.push('\\'),
Some('/') => out.push('/'),
Some('n') => out.push('\n'),
Some('r') => out.push('\r'),
Some('t') => out.push('\t'),
Some('b') => out.push('\x08'),
Some('f') => out.push('\x0C'),
Some('u') => {
let hex: String = (0..4).filter_map(|_| it.next()).collect();
if hex.len() != 4 {
return Err(ParseError("invalid \\u escape".into()));
}
let codepoint = u32::from_str_radix(&hex, 16)
.map_err(|_| ParseError(format!("invalid hex '{}'", hex)))?;
let ch = char::from_u32(codepoint)
.ok_or_else(|| ParseError(format!("invalid codepoint U+{:04X}", codepoint)))?;
out.push(ch);
}
Some(c) => return Err(ParseError(format!("invalid escape '\\{}'", c))),
None => return Err(ParseError("unterminated string".into())),
}
}
Some(c) => out.push(c),
None => return Err(ParseError("unterminated string".into())),
}
}
}
fn parse_number(it: &mut Iter) -> Result<Json, ParseError> {
let mut s = String::new();
if it.peek() == Some(&'-') { s.push(it.next().unwrap()); }
while matches!(it.peek(), Some('0'..='9')) { s.push(it.next().unwrap()); }
let is_float = matches!(it.peek(), Some('.') | Some('e') | Some('E'));
if is_float {
if it.peek() == Some(&'.') {
s.push(it.next().unwrap());
while matches!(it.peek(), Some('0'..='9')) { s.push(it.next().unwrap()); }
}
if matches!(it.peek(), Some('e') | Some('E')) {
s.push(it.next().unwrap());
if matches!(it.peek(), Some('+') | Some('-')) { s.push(it.next().unwrap()); }
while matches!(it.peek(), Some('0'..='9')) { s.push(it.next().unwrap()); }
}
s.parse::<f64>().map(Json::Float).map_err(|_| ParseError(format!("invalid float '{}'", s)))
} else {
s.parse::<i64>().map(Json::Int).map_err(|_| ParseError(format!("invalid int '{}'", s)))
}
}
fn parse_array(it: &mut Iter) -> Result<Json, ParseError> {
it.next(); let mut arr = Vec::new();
skip_ws(it);
if it.peek() == Some(&']') { it.next(); return Ok(Json::Array(arr)); }
loop {
arr.push(parse_value(it)?);
skip_ws(it);
match it.peek() {
Some(',') => { it.next(); }
Some(']') => { it.next(); return Ok(Json::Array(arr)); }
other => return Err(ParseError(format!("expected ',' or ']', got {:?}", other))),
}
}
}
fn parse_object(it: &mut Iter) -> Result<Json, ParseError> {
it.next(); let mut map = HashMap::new();
skip_ws(it);
if it.peek() == Some(&'}') { it.next(); return Ok(Json::Object(map)); }
loop {
skip_ws(it);
if it.peek() != Some(&'"') {
return Err(ParseError(format!("expected key string, got {:?}", it.peek())));
}
let key = parse_string(it)?;
skip_ws(it);
if it.next() != Some(':') {
return Err(ParseError("expected ':' after object key".into()));
}
let val = parse_value(it)?;
map.insert(key, val);
skip_ws(it);
match it.peek() {
Some(',') => { it.next(); }
Some('}') => { it.next(); return Ok(Json::Object(map)); }
other => return Err(ParseError(format!("expected ',' or '}}', got {:?}", other))),
}
}
}
#[macro_export]
macro_rules! json {
(null) => { $crate::Json::Null };
(true) => { $crate::Json::Bool(true) };
(false) => { $crate::Json::Bool(false) };
([ $($elem:tt),* $(,)? ]) => {
$crate::Json::Array(vec![ $( json!($elem) ),* ])
};
({ $($key:tt : $val:tt),* $(,)? }) => {{
let mut _map = ::std::collections::HashMap::new();
$( _map.insert(String::from($key), json!($val)); )*
$crate::Json::Object(_map)
}};
($other:expr) => { $crate::Json::from($other) };
}
pub struct ObjectBuilder(HashMap<String, Json>);
impl ObjectBuilder {
pub fn new() -> Self { ObjectBuilder(HashMap::new()) }
pub fn set(mut self, key: impl Into<String>, value: impl Into<Json>) -> Self {
self.0.insert(key.into(), value.into());
self
}
pub fn set_if(self, cond: bool, key: impl Into<String>, value: impl Into<Json>) -> Self {
if cond { self.set(key, value) } else { self }
}
pub fn merge(mut self, other: &Json) -> Self {
if let Json::Object(m) = other {
for (k, v) in m { self.0.insert(k.clone(), v.clone()); }
}
self
}
pub fn build(self) -> Json { Json::Object(self.0) }
}
impl Default for ObjectBuilder {
fn default() -> Self { Self::new() }
}
pub struct ArrayBuilder(Vec<Json>);
impl ArrayBuilder {
pub fn new() -> Self { ArrayBuilder(Vec::new()) }
pub fn push(mut self, value: impl Into<Json>) -> Self {
self.0.push(value.into());
self
}
pub fn push_if(self, cond: bool, value: impl Into<Json>) -> Self {
if cond { self.push(value) } else { self }
}
pub fn extend(mut self, iter: impl IntoIterator<Item = impl Into<Json>>) -> Self {
self.0.extend(iter.into_iter().map(Into::into));
self
}
pub fn build(self) -> Json { Json::Array(self.0) }
}
impl Default for ArrayBuilder {
fn default() -> Self { Self::new() }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_macro_basic() {
let v = json!({
"name": "Rebon402",
"age": 30i64,
"active": true,
"score": 9.5f64,
"nothing": null,
"tags": ["rust", "json"]
});
assert_eq!(v["name"].as_str(), Some("Rebon402"));
assert_eq!(v["age"].as_i64(), Some(30));
assert_eq!(v["active"].as_bool(), Some(true));
assert_eq!(v["score"].as_f64(), Some(9.5));
assert!(v["nothing"].is_null());
assert_eq!(v["tags"][0].as_str(), Some("rust"));
}
#[test]
fn test_parse_roundtrip() {
let src = r#"{"a":1,"b":true,"c":null,"d":[1,2,3],"e":{"x":9}}"#;
let parsed = Json::parse(src).unwrap();
let reserialized = parsed.to_json();
let reparsed = Json::parse(&reserialized).unwrap();
assert_eq!(parsed, reparsed);
}
#[test]
fn test_get_path() {
let v = json!({ "user": { "address": { "city": "Bangkok" } } });
assert_eq!(v.get_path("user.address.city").unwrap().as_str(), Some("Bangkok"));
}
#[test]
fn test_builder() {
let obj = ObjectBuilder::new()
.set("x", 42i64)
.set("y", "hello")
.build();
assert_eq!(obj["x"].as_i64(), Some(42));
let arr = ArrayBuilder::new()
.push(1i64).push(2i64).push(3i64)
.build();
assert_eq!(arr[1].as_i64(), Some(2));
}
#[test]
fn test_mutation() {
let mut obj = Json::new_object();
obj.set("key", "value");
obj.set("num", 99i64);
assert_eq!(obj["num"].as_i64(), Some(99));
obj.remove("key");
assert!(obj["key"].is_null());
}
#[test]
fn test_pretty_print() {
let v = json!({"a": 1i64, "b": [1i64, 2i64]});
let pretty = v.to_pretty(" ");
assert!(pretty.contains('\n'));
}
#[test]
fn test_unicode_escape() {
let src = r#"{"emoji": "\u0041BC"}"#; let v = Json::parse(src).unwrap();
assert_eq!(v["emoji"].as_str(), Some("ABC"));
}
#[test]
fn test_contains_key() {
let v = json!({"x": 1i64});
assert!(v.contains_key("x"));
assert!(!v.contains_key("y"));
}
#[test]
fn test_merge() {
let mut a = json!({"x": 1i64});
let b = json!({"y": 2i64});
a.merge(&b);
assert_eq!(a["y"].as_i64(), Some(2));
}
}