use std::mem;
use crate::{
error::{JsonError, Result},
operation::Operator,
path::Path,
};
use serde_json::Value;
pub trait Routable {
fn route_get(&self, paths: &Path) -> Result<Option<&Value>>;
fn route_get_mut(&mut self, paths: &Path) -> Result<Option<&mut Value>>;
}
pub trait Appliable {
fn apply(&mut self, paths: Path, operator: Operator) -> Result<()>;
}
impl Routable for Value {
fn route_get(&self, paths: &Path) -> Result<Option<&Value>> {
match self {
Value::Array(array) => array.route_get(paths),
Value::Object(obj) => obj.route_get(paths),
Value::Null => Ok(None),
_ => {
if paths.is_empty() {
Ok(Some(self))
} else {
Err(JsonError::BadPath)
}
}
}
}
fn route_get_mut(&mut self, paths: &Path) -> Result<Option<&mut Value>> {
match self {
Value::Array(array) => array.route_get_mut(paths),
Value::Object(obj) => obj.route_get_mut(paths),
_ => {
if paths.is_empty() {
Ok(Some(self))
} else {
Err(JsonError::BadPath)
}
}
}
}
}
impl Routable for serde_json::Map<String, serde_json::Value> {
fn route_get(&self, paths: &Path) -> Result<Option<&Value>> {
let k = paths.first_key_path().ok_or(JsonError::BadPath)?;
if let Some(v) = self.get(k) {
let next_level = paths.next_level();
if next_level.is_empty() {
Ok(Some(v))
} else {
v.route_get(&next_level)
}
} else {
Ok(None)
}
}
fn route_get_mut(&mut self, paths: &Path) -> Result<Option<&mut Value>> {
let k = paths.first_key_path().ok_or(JsonError::BadPath)?;
if let Some(v) = self.get_mut(k) {
let next_level = paths.next_level();
if next_level.is_empty() {
Ok(Some(v))
} else {
v.route_get_mut(&next_level)
}
} else {
Ok(None)
}
}
}
impl Routable for Vec<serde_json::Value> {
fn route_get(&self, paths: &Path) -> Result<Option<&Value>> {
let i = paths.first_index_path().ok_or(JsonError::BadPath)?;
if let Some(v) = self.get(*i) {
let next_level = paths.next_level();
if next_level.is_empty() {
Ok(Some(v))
} else {
v.route_get(&next_level)
}
} else {
Ok(None)
}
}
fn route_get_mut(&mut self, paths: &Path) -> Result<Option<&mut Value>> {
let i = paths.first_index_path().ok_or(JsonError::BadPath)?;
if let Some(v) = self.get_mut(*i) {
let next_level = paths.next_level();
if next_level.is_empty() {
Ok(Some(v))
} else {
v.route_get_mut(&next_level)
}
} else {
Ok(None)
}
}
}
impl Appliable for Value {
fn apply(&mut self, paths: Path, op: Operator) -> Result<()> {
if paths.len() > 1 {
let (left, right) = paths.split_at(paths.len() - 1);
return self
.route_get_mut(&left)?
.ok_or(JsonError::BadPath)?
.apply(right, op);
}
match self {
Value::Array(array) => array.apply(paths, op),
Value::Object(obj) => obj.apply(paths, op),
_ => match op {
Operator::SubType(_, op, f) => {
if let Some(v) = f.apply(Some(self), &op)? {
_ = mem::replace(self, v);
}
Ok(())
}
_ => Err(JsonError::InvalidOperation(
"Operation can only apply on array or object".into(),
)),
},
}
}
}
impl Appliable for serde_json::Map<String, serde_json::Value> {
fn apply(&mut self, paths: Path, op: Operator) -> Result<()> {
assert!(paths.len() == 1);
let k = paths.first_key_path().ok_or(JsonError::BadPath)?;
let target_value = self.get(k);
match &op {
Operator::SubType(_, op, f) => {
if let Some(v) = f.apply(target_value, op)? {
self.insert(k.clone(), v);
}
Ok(())
}
Operator::ObjectInsert(v) => {
self.insert(k.clone(), v.clone());
Ok(())
}
Operator::ObjectDelete(_) => {
if target_value.is_some() {
self.remove(k);
}
Ok(())
}
Operator::ObjectReplace(new_v, _) => {
if target_value.is_some() {
self.insert(k.clone(), new_v.clone());
}
Ok(())
}
_ => Err(JsonError::BadPath),
}
}
}
impl Appliable for Vec<serde_json::Value> {
fn apply(&mut self, paths: Path, op: Operator) -> Result<()> {
assert!(paths.len() == 1);
let index = paths.first_index_path().ok_or(JsonError::BadPath)?;
let target_value = self.get(*index);
match op {
Operator::SubType(_, op, f) => {
if let Some(v) = f.apply(target_value, &op)? {
self[*index] = v;
}
Ok(())
}
Operator::ListInsert(v) => {
if *index > self.len() {
self.push(v.clone())
} else {
self.insert(*index, v.clone());
}
Ok(())
}
Operator::ListDelete(_) => {
if target_value.is_some() {
self.remove(*index);
}
Ok(())
}
Operator::ListReplace(new_v, _) => {
if target_value.is_some() {
self[*index] = new_v.clone();
}
Ok(())
}
Operator::ListMove(new_index) => {
if let Some(target_v) = target_value {
if *index != new_index {
let new_v = target_v.clone();
self.remove(*index);
self.insert(new_index, new_v);
}
}
Ok(())
}
_ => Err(JsonError::BadPath),
}
}
}
#[cfg(test)]
mod tests {
use crate::path::Path;
use super::*;
use test_log::test;
#[test]
fn test_route_get_by_path_only_has_object() {
let json: Value =
serde_json::from_str(r#"{"level1":"world", "level12":{"level2":"world2"}}"#).unwrap();
let paths = Path::try_from(r#"["level1"]"#).unwrap();
assert_eq!(
json.route_get(&paths).unwrap().unwrap().to_string(),
r#""world""#
);
let paths = Path::try_from(r#"["level12", "level2"]"#).unwrap();
assert_eq!(
json.route_get(&paths).unwrap().unwrap().to_string(),
r#""world2""#
);
let paths = Path::try_from(r#"["level3"]"#).unwrap();
assert!(json.route_get(&paths).unwrap().is_none());
let json: Value =
serde_json::from_str(r#"{"level1":[1,{"hello":[1,[7,8]]}], "level12":"world"}"#)
.unwrap();
let paths = Path::try_from(r#"["level1", 1, "hello"]"#).unwrap();
assert_eq!(
json.route_get(&paths).unwrap().unwrap().to_string(),
r#"[1,[7,8]]"#
);
}
#[test]
fn test_route_get_by_path_has_array() {
let json: Value =
serde_json::from_str(r#"{"level1":["a","b"], "level12":[123, {"level2":["c","d"]}]}"#)
.unwrap();
let paths = Path::try_from(r#"["level1", 1]"#).unwrap();
assert_eq!(
json.route_get(&paths).unwrap().unwrap().to_string(),
r#""b""#
);
let paths = Path::try_from(r#"["level12", 0]"#).unwrap();
assert_eq!(
json.route_get(&paths).unwrap().unwrap().to_string(),
r#"123"#
);
let paths = Path::try_from(r#"["level12", 1, "level2"]"#).unwrap();
assert_eq!(
json.route_get(&paths).unwrap().unwrap().to_string(),
r#"["c","d"]"#
);
let json: Value =
serde_json::from_str(r#"{"level1":[1,{"hello":[1,[7,8]]}], "level12":"world"}"#)
.unwrap();
let paths = Path::try_from(r#"["level1", 1, "hello", 1]"#).unwrap();
assert_eq!(
json.route_get(&paths).unwrap().unwrap().to_string(),
r#"[7,8]"#
);
}
}