#![deny(missing_docs)]
use serde_json::Value;
pub fn pluck<'a>(root: &'a Value, path: &str) -> Option<&'a Value> {
if let Some(name) = path.strip_prefix("**.") {
return bfs_first(root, name);
}
let mut cur = root;
for seg in split_path(path) {
cur = step(cur, &seg)?;
}
Some(cur)
}
pub fn pluck_mut<'a>(root: &'a mut Value, path: &str) -> Option<&'a mut Value> {
if path.starts_with("**.") {
return None;
}
let mut cur = root;
for seg in split_path(path) {
cur = step_mut(cur, &seg)?;
}
Some(cur)
}
#[derive(Debug, Clone, PartialEq)]
enum Seg {
Key(String),
Index(i64),
}
fn split_path(path: &str) -> Vec<Seg> {
let mut out = Vec::new();
let mut buf = String::new();
let mut chars = path.chars().peekable();
while let Some(c) = chars.next() {
match c {
'.' => {
if !buf.is_empty() {
out.push(Seg::Key(std::mem::take(&mut buf)));
}
}
'[' => {
if !buf.is_empty() {
out.push(Seg::Key(std::mem::take(&mut buf)));
}
let mut num = String::new();
while let Some(&next) = chars.peek() {
if next == ']' {
chars.next();
break;
}
num.push(next);
chars.next();
}
if let Ok(n) = num.parse::<i64>() {
out.push(Seg::Index(n));
}
}
_ => buf.push(c),
}
}
if !buf.is_empty() {
out.push(Seg::Key(buf));
}
out
}
fn step<'a>(v: &'a Value, seg: &Seg) -> Option<&'a Value> {
match seg {
Seg::Key(k) => v.as_object()?.get(k),
Seg::Index(i) => {
let arr = v.as_array()?;
let idx = if *i < 0 {
(arr.len() as i64 + *i) as usize
} else {
*i as usize
};
arr.get(idx)
}
}
}
fn step_mut<'a>(v: &'a mut Value, seg: &Seg) -> Option<&'a mut Value> {
match seg {
Seg::Key(k) => v.as_object_mut()?.get_mut(k),
Seg::Index(i) => {
let arr = v.as_array_mut()?;
let idx = if *i < 0 {
(arr.len() as i64 + *i) as usize
} else {
*i as usize
};
arr.get_mut(idx)
}
}
}
fn bfs_first<'a>(root: &'a Value, name: &str) -> Option<&'a Value> {
let mut queue: std::collections::VecDeque<&Value> = std::collections::VecDeque::new();
queue.push_back(root);
while let Some(v) = queue.pop_front() {
match v {
Value::Object(map) => {
if let Some(hit) = map.get(name) {
return Some(hit);
}
for (_, child) in map {
queue.push_back(child);
}
}
Value::Array(arr) => {
for child in arr {
queue.push_back(child);
}
}
_ => {}
}
}
None
}