use cipherstash_config::column::ArrayIndexMode;
use serde_json::Value;
#[derive(Debug, PartialEq, Eq)]
pub(super) struct PathValue<'a>(pub(super) Vec<PathSegment<'a>>, pub(super) &'a Value);
pub fn flatmap_json<'a, F>(value: &'a Value, mode: ArrayIndexMode, mapper: F) -> Vec<PathValue<'a>>
where
F: Fn(&[PathSegment<'a>], &'a Value) -> PathValue<'a>,
{
let mut out: Vec<PathValue<'a>> = vec![];
flatmap_json_internal(value, &mut vec![PathSegment::Root], &mapper, &mut out, mode);
out
}
fn flatmap_json_internal<'a, F>(
value: &'a Value,
path: &mut Vec<PathSegment<'a>>,
mapper: &F,
out: &mut Vec<PathValue<'a>>,
mode: ArrayIndexMode,
) where
F: Fn(&[PathSegment<'a>], &'a Value) -> PathValue<'a>,
{
match value {
Value::Null | Value::Bool(_) | Value::Number(_) | Value::String(_) => {
out.push(mapper(&*path, value))
}
Value::Array(values) => {
out.push(mapper(&*path, value));
for (idx, value) in values.iter().enumerate() {
if mode.has_item() {
path.push(PathSegment::ArrayItem);
flatmap_json_internal(value, path, mapper, out, mode);
path.pop();
}
if mode.has_position() {
path.push(PathSegment::ArrayPositionItem(idx));
flatmap_json_internal(value, path, mapper, out, mode);
path.pop();
}
if mode.has_wildcard() {
path.push(PathSegment::ArrayWildcardItem);
flatmap_json_internal(value, path, mapper, out, mode);
path.pop();
}
}
}
Value::Object(object) => {
out.push(mapper(&*path, value));
for (key, value) in object.iter() {
path.push(PathSegment::ObjectItem(key));
flatmap_json_internal(value, path, mapper, out, mode);
path.pop();
}
}
};
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) enum PathSegment<'a> {
Root,
ArrayWildcardItem,
ArrayPositionItem(usize),
ArrayItem,
ObjectItem(&'a str),
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_flatmap_with_mode_none() {
let json = json!({"ary": ["a", "b", "c"]});
let mapped = flatmap_json(&json, ArrayIndexMode::NONE, |path, value| {
PathValue(path.to_vec(), value)
});
assert_eq!(mapped.len(), 2);
}
#[test]
fn test_flatmap_with_mode_wildcard_only() {
let json = json!({"ary": ["a", "b", "c"]});
let mapped = flatmap_json(&json, ArrayIndexMode::WILDCARD, |path, value| {
PathValue(path.to_vec(), value)
});
assert_eq!(mapped.len(), 5);
}
#[test]
fn test_flatmap_with_mode_all() {
let json = json!({"ary": ["a", "b", "c"]});
let mapped = flatmap_json(&json, ArrayIndexMode::ALL, |path, value| {
PathValue(path.to_vec(), value)
});
assert_eq!(mapped.len(), 11);
}
#[test]
fn test_flatmap_with_mode_item_and_wildcard() {
let json = json!({"ary": ["a", "b", "c"]});
let mode = ArrayIndexMode::ITEM | ArrayIndexMode::WILDCARD;
let mapped = flatmap_json(&json, mode, |path, value| PathValue(path.to_vec(), value));
assert_eq!(mapped.len(), 8);
}
#[test]
fn test_flatmap_with_mode_item_only() {
let json = json!({"ary": ["a", "b", "c"]});
let mapped = flatmap_json(&json, ArrayIndexMode::ITEM, |path, value| {
PathValue(path.to_vec(), value)
});
assert_eq!(mapped.len(), 5);
}
#[test]
fn test_flatmap_with_mode_position_only() {
let json = json!({"ary": ["a", "b", "c"]});
let mapped = flatmap_json(&json, ArrayIndexMode::POSITION, |path, value| {
PathValue(path.to_vec(), value)
});
assert_eq!(mapped.len(), 5);
}
#[test]
#[ignore] fn map_json() {
let json = json!({
"ary": [
"a", "b", "c"
]
});
let mapped = super::flatmap_json(&json, ArrayIndexMode::ALL, |path, value| {
PathValue(path.to_vec(), value)
});
println!("mapped: {mapped:?}")
}
}