use std::collections::HashMap;
use std::ops::Index;
#[derive(Debug, Clone)]
pub enum Json {
Object(HashMap<String, Json>),
Array(Vec<Json>),
String(String),
Number(f64),
Bool(bool),
Null,
}
impl Json {
pub fn is_null(&self) -> bool { matches!(self, Json::Null) }
pub fn len(&self) -> usize {
match self {
Json::Array(v) => v.len(),
Json::Object(m) => m.len(),
_ => 0,
}
}
pub fn unwrap_or(&self, default: &str) -> String {
if self.is_null() { default.to_string() } else { self.to_string_raw() }
}
pub fn find(&self, key: &str) -> &Json {
static NULL: Json = Json::Null;
match self {
Json::Object(m) => {
if let Some(v) = m.get(key) { return v; }
for v in m.values() {
let found = v.find(key);
if !found.is_null() { return found; }
}
&NULL
}
Json::Array(a) => {
for v in a {
let found = v.find(key);
if !found.is_null() { return found; }
}
&NULL
}
_ => &NULL,
}
}
pub fn from_str(input: &str) -> Self {
let mut chars = input.chars().peekable();
Self::parse(&mut chars)
}
pub fn as_str(&self) -> Option<&str> {
if let Json::String(s) = self { Some(s) } else { None }
}
pub fn as_f64(&self) -> Option<f64> {
if let Json::Number(n) = self { Some(*n) } else { None }
}
fn parse(chars: &mut std::iter::Peekable<std::str::Chars>) -> Self {
while let Some(&c) = chars.peek() {
if c.is_whitespace() || c == ',' || c == ':' { chars.next(); } else { break; }
}
match chars.next() {
Some('{') => {
let mut map = HashMap::new();
while let Some(&c) = chars.peek() {
if c == '}' { chars.next(); break; }
let key = match Self::parse(chars) { Json::String(s) => s, _ => String::new() };
map.insert(key, Self::parse(chars));
}
Json::Object(map)
}
Some('[') => {
let mut vec = Vec::new();
while let Some(&c) = chars.peek() {
if c == ']' { chars.next(); break; }
vec.push(Self::parse(chars));
}
Json::Array(vec)
}
Some('"') => {
let mut s = String::new();
while let Some(c) = chars.next() {
if c == '\\' { if let Some(esc) = chars.next() { s.push(esc); } }
else if c == '"' { break; }
else { s.push(c); }
}
Json::String(s)
}
Some('t') => { for _ in 0..3 { chars.next(); } Json::Bool(true) }
Some('f') => { for _ in 0..4 { chars.next(); } Json::Bool(false) }
Some('n') => { for _ in 0..3 { chars.next(); } Json::Null }
Some(c) if c.is_digit(10) || c == '-' => {
let mut s = c.to_string();
while let Some(&next) = chars.peek() {
if next.is_digit(10) || next == '.' || next == 'e' || next == 'E' {
s.push(chars.next().unwrap());
} else { break; }
}
Json::Number(s.parse().unwrap_or(0.0))
}
_ => Json::Null,
}
}
pub fn pretty_print(&self, indent: usize) -> String {
let space = " ".repeat(indent);
let inner_space = " ".repeat(indent + 4);
match self {
Json::Object(obj) => {
if obj.is_empty() { return "{}".to_string(); }
let mut items: Vec<String> = obj.iter()
.map(|(k, v)| format!("{}\"{}\": {}", inner_space, k, v.pretty_print(indent + 4)))
.collect();
items.sort();
format!("{{\n{}\n{}}}", items.join(",\n"), space)
}
Json::Array(arr) => {
if arr.is_empty() { return "[]".to_string(); }
let items: Vec<String> = arr.iter()
.map(|v| format!("{}{}", inner_space, v.pretty_print(indent + 4)))
.collect();
format!("[\n{}\n{}]", items.join(",\n"), space)
}
Json::String(s) => format!("\"{}\"", s),
_ => self.to_string_raw(),
}
}
pub fn to_string_raw(&self) -> String {
match self {
Json::String(s) => s.clone(),
Json::Number(n) => if n.fract() == 0.0 { (*n as i64).to_string() } else { n.to_string() },
Json::Bool(b) => b.to_string(),
Json::Array(arr) => {
let items: Vec<String> = arr.iter().map(|v| v.to_string_raw_json()).collect();
format!("[{}]", items.join(","))
},
Json::Object(obj) => {
let mut pairs: Vec<String> = obj.iter()
.map(|(k, v)| format!("\"{}\":{}", k, v.to_string_raw_json()))
.collect();
pairs.sort();
format!("{{{}}}", pairs.join(","))
},
Json::Null => "null".to_string(),
}
}
fn to_string_raw_json(&self) -> String {
match self {
Json::String(s) => format!("\"{}\"", s),
_ => self.to_string_raw(),
}
}
}
impl Index<&str> for Json {
type Output = Json;
fn index(&self, key: &str) -> &Self::Output {
static NULL: Json = Json::Null;
if let Json::Object(m) = self { m.get(key).unwrap_or(&NULL) } else { &NULL }
}
}
impl Index<usize> for Json {
type Output = Json;
fn index(&self, index: usize) -> &Self::Output {
static NULL: Json = Json::Null;
if let Json::Array(a) = self { a.get(index).unwrap_or(&NULL) } else { &NULL }
}
}