use std::collections::BTreeMap;
use serde_json::Value;
use crate::JsonScalar;
pub trait JsonPaths {
#[doc(alias = "PathsAndValuesDictionary")]
#[must_use]
fn paths_and_values(&self) -> BTreeMap<String, JsonScalar>;
}
impl JsonPaths for Value {
fn paths_and_values(&self) -> BTreeMap<String, JsonScalar> {
let mut paths = BTreeMap::new();
walk(self, &mut String::with_capacity(64), &mut paths);
paths
}
}
fn walk(value: &Value, path: &mut String, paths: &mut BTreeMap<String, JsonScalar>) {
match value {
Value::Object(properties) => {
for (key, child) in properties {
let length = path.len();
push_segment(path, key);
walk(child, path, paths);
path.truncate(length);
}
}
Value::Array(items) => {
for (index, child) in items.iter().enumerate() {
let length = path.len();
push_index(path, index);
walk(child, path, paths);
path.truncate(length);
}
}
Value::Null => {
paths.insert(path.clone(), JsonScalar::Null);
}
Value::Bool(value) => {
paths.insert(path.clone(), JsonScalar::Bool(*value));
}
Value::Number(value) => {
paths.insert(path.clone(), JsonScalar::Number(value.clone()));
}
Value::String(value) => {
paths.insert(path.clone(), JsonScalar::String(value.clone()));
}
}
}
fn push_segment(path: &mut String, segment: &str) {
if !path.is_empty() {
path.push('.');
}
path.push_str(segment);
}
fn push_index(path: &mut String, index: usize) {
if !path.is_empty() {
path.push('.');
}
let mut digits = [0u8; 20];
let mut cursor = digits.len();
let mut value = index;
loop {
cursor -= 1;
digits[cursor] = b'0' + (value % 10) as u8;
value /= 10;
if value == 0 {
break;
}
}
path.push_str(std::str::from_utf8(&digits[cursor..]).unwrap());
}