use crate::json_path_parser::{JsonPathParser, Segment};
use anyhow::anyhow;
use serde_json::Value;
#[derive(Debug, Clone)]
pub struct JsonValueLocator {
data: Value,
}
impl JsonValueLocator {
pub fn new(data: Value) -> Self {
Self { data }
}
pub fn get(&self, path: &str) -> anyhow::Result<Value> {
let segment = JsonPathParser::parse(path)?;
let mut data: Value = self.data.clone();
for item in segment.iter() {
match item {
Segment::Key(key) => {
if let Value::Object(obj) = &data {
data = if obj.contains_key(key)
&& let Some(value) = obj.get(key)
{
value.to_owned()
} else {
return Err(anyhow!(format!("{} key does not exists", key)));
};
}
}
Segment::Index(key, index) => {
if let Value::Object(obj) = &data {
let values = if obj.contains_key(key)
&& let Some(Value::Array(values)) = obj.get(key)
{
values.to_owned()
} else {
return Err(anyhow!(format!(
"{} key does not exists or must be an array",
key
)));
};
if let Some(value) = values.get(index.to_owned()) {
data = value.to_owned()
}
}
}
}
}
Ok(data)
}
}
#[test]
pub fn test_json_value_locator() {
use crate::file_handler::FileHandler;
use serde_json::{Number, Value};
let data = FileHandler::load("storage/test-data/data.json").unwrap();
let locator = JsonValueLocator::new(data);
let test_cases: Vec<(&str, Value)> = vec![
("result.item[0].value", Value::String("apple".to_string())),
(
"result.item[1].details.color",
Value::String("yellow".to_string()),
),
("result.meta.count", Value::Number(Number::from(2))),
("status", Value::String("success".to_string())),
(
"result.item[0].details.price",
Value::Number(Number::from_f64(1.2).unwrap()),
),
(
"timestamp",
Value::String("2025-10-12T10:00:00Z".to_string()),
),
];
for test_case in test_cases {
let result = locator.get(test_case.0);
assert!(result.is_ok(), "{:#?}", result.err());
assert_eq!(test_case.1, result.unwrap())
}
}