use core::slice::Iter;
use std::collections::HashMap;
#[derive(Clone, PartialEq, Debug)]
pub enum JsonValue {
String(String),
Number(String),
Boolean(bool),
Object(JsonObject),
Array(JsonArray),
Null,
}
#[derive(Clone, PartialEq, Debug)]
pub struct JsonObject(HashMap<String, JsonValue>);
impl IntoIterator for JsonObject {
type Item = (String, JsonValue);
type IntoIter = std::collections::hash_map::IntoIter<String, JsonValue>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl From<HashMap<String, JsonValue>> for JsonObject {
fn from(properties: HashMap<String, JsonValue>) -> JsonObject {
JsonObject::new(properties)
}
}
macro_rules! generate_take {
($self:ident, $name:ident, $value_type:ident) => {
match $self.0.remove_entry($name) {
Some((_, JsonValue::$value_type(value))) => Some(value),
Some((key, value)) => {
$self.0.insert(key, value);
None
}
_ => None,
}
};
}
macro_rules! generate_get {
($self:ident, $name:ident, $value_type:ident) => {
match $self.0.get($name) {
Some(JsonValue::$value_type(value)) => Some(value),
_ => None,
}
};
}
impl JsonObject {
pub fn new(inner: HashMap<String, JsonValue>) -> JsonObject {
JsonObject(inner)
}
pub fn take_inner(self) -> HashMap<String, JsonValue> {
self.0
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn get(&self, name: &str) -> Option<&JsonValue> {
self.0.get(name)
}
pub fn get_string(&self, name: &str) -> Option<&String> {
generate_get!(self, name, String)
}
pub fn get_number(&self, name: &str) -> Option<&String> {
generate_get!(self, name, Number)
}
pub fn get_boolean(&self, name: &str) -> Option<bool> {
let result = generate_get!(self, name, Boolean);
result.cloned()
}
pub fn get_object(&self, name: &str) -> Option<&JsonObject> {
generate_get!(self, name, Object)
}
pub fn get_array(&self, name: &str) -> Option<&JsonArray> {
generate_get!(self, name, Array)
}
pub fn take(&mut self, name: &str) -> Option<JsonValue> {
self.0.remove(name)
}
pub fn take_string(&mut self, name: &str) -> Option<String> {
generate_take!(self, name, String)
}
pub fn take_number(&mut self, name: &str) -> Option<String> {
generate_take!(self, name, Number)
}
pub fn take_boolean(&mut self, name: &str) -> Option<bool> {
generate_take!(self, name, Boolean)
}
pub fn take_object(&mut self, name: &str) -> Option<JsonObject> {
generate_take!(self, name, Object)
}
pub fn take_array(&mut self, name: &str) -> Option<JsonArray> {
generate_take!(self, name, Array)
}
}
#[derive(Clone, PartialEq, Debug)]
pub struct JsonArray(Vec<JsonValue>);
impl IntoIterator for JsonArray {
type Item = JsonValue;
type IntoIter = std::vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl From<Vec<JsonValue>> for JsonArray {
fn from(elements: Vec<JsonValue>) -> JsonArray {
JsonArray::new(elements)
}
}
impl JsonArray {
pub fn new(inner: Vec<JsonValue>) -> JsonArray {
JsonArray(inner)
}
pub fn take_inner(self) -> Vec<JsonValue> {
self.0
}
pub fn iter(&self) -> Iter<JsonValue> {
self.0.iter()
}
pub fn get(&self, index: usize) -> Option<&JsonValue> {
self.0.get(index)
}
pub fn len(&self) -> usize {
self.0.len()
}
}
#[cfg(test)]
mod test {
use super::*;
use std::collections::HashMap;
#[test]
fn it_should_take() {
let mut inner = HashMap::new();
inner.insert(String::from("prop"), JsonValue::String(String::from("asdf")));
inner.insert(String::from("other"), JsonValue::String(String::from("text")));
let mut obj = JsonObject::new(inner);
assert_eq!(obj.len(), 2);
assert_eq!(obj.take_string("asdf"), None);
assert_eq!(obj.len(), 2);
assert_eq!(obj.take_number("prop"), None);
assert_eq!(obj.len(), 2);
assert_eq!(obj.take_string("prop"), Some(String::from("asdf")));
assert_eq!(obj.len(), 1);
assert_eq!(obj.take("something"), None);
assert_eq!(obj.len(), 1);
assert_eq!(obj.take("other"), Some(JsonValue::String(String::from("text"))));
assert_eq!(obj.len(), 0);
}
#[test]
fn it_should_get() {
let mut inner = HashMap::new();
inner.insert(String::from("prop"), JsonValue::String(String::from("asdf")));
let obj = JsonObject::new(inner);
assert_eq!(obj.len(), 1);
assert_eq!(obj.get_string("asdf"), None);
assert_eq!(obj.get_string("prop"), Some(&String::from("asdf")));
assert_eq!(obj.get("prop"), Some(&JsonValue::String(String::from("asdf"))));
assert_eq!(obj.get("asdf"), None);
assert_eq!(obj.len(), 1);
}
}