use serde_json::Value;
pub struct JValue(pub Value);
impl JValue {
pub fn path(&self, dotted: &str) -> JValue {
let mut current = &self.0;
for part in dotted.split('.') {
match current.get(part) {
Some(v) => current = v,
None => return JValue(Value::Null),
}
}
JValue(current.clone())
}
pub fn get(&self, key: &str) -> JValue {
match self.0.get(key) {
Some(v) => JValue(v.clone()),
None => JValue(Value::Null),
}
}
pub fn idx(&self, i: usize) -> JValue {
match self.0.get(i) {
Some(v) => JValue(v.clone()),
None => JValue(Value::Null),
}
}
pub fn str(&self) -> Option<&str> {
self.0.as_str()
}
pub fn str_or<'a>(&'a self, default: &'a str) -> &'a str {
self.0.as_str().unwrap_or(default)
}
pub fn string(&self) -> Option<String> {
self.0.as_str().map(|s| s.to_string())
}
pub fn i64(&self) -> Option<i64> {
self.0.as_i64()
}
pub fn f64(&self) -> Option<f64> {
self.0.as_f64()
}
pub fn bool(&self) -> Option<bool> {
self.0.as_bool()
}
pub fn array(&self) -> Option<&Vec<Value>> {
self.0.as_array()
}
pub fn is_null(&self) -> bool {
self.0.is_null()
}
pub fn into_inner(self) -> Value {
self.0
}
pub fn as_value(&self) -> &Value {
&self.0
}
}
impl From<Value> for JValue {
fn from(v: Value) -> Self {
JValue(v)
}
}
impl From<JValue> for Value {
fn from(j: JValue) -> Self {
j.0
}
}
impl From<Option<Value>> for JValue {
fn from(opt: Option<Value>) -> Self {
JValue(opt.unwrap_or(Value::Null))
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_path_nested() {
let v = JValue::from(json!({"a": {"b": {"c": 42}}}));
assert_eq!(v.path("a.b.c").i64(), Some(42));
}
#[test]
fn test_path_missing_returns_null() {
let v = JValue::from(json!({"a": 1}));
assert!(v.path("x.y.z").is_null());
}
#[test]
fn test_str_and_str_or() {
let v = JValue::from(json!({"name": "Alice"}));
assert_eq!(v.get("name").str(), Some("Alice"));
assert_eq!(v.get("name").str_or("?"), "Alice");
assert_eq!(v.get("missing").str(), None);
assert_eq!(v.get("missing").str_or("default"), "default");
}
#[test]
fn test_string_owned() {
let v = JValue::from(json!({"k": "hello"}));
let s: Option<String> = v.get("k").string();
assert_eq!(s, Some("hello".to_string()));
}
#[test]
fn test_numeric() {
let v = JValue::from(json!({"i": 10, "f": 3.14}));
assert_eq!(v.get("i").i64(), Some(10));
assert_eq!(v.get("f").f64(), Some(3.14));
}
#[test]
fn test_bool() {
let v = JValue::from(json!({"flag": true}));
assert_eq!(v.get("flag").bool(), Some(true));
assert_eq!(v.get("missing").bool(), None);
}
#[test]
fn test_array_and_idx() {
let v = JValue::from(json!({"items": [10, 20, 30]}));
assert_eq!(v.get("items").array().map(|a| a.len()), Some(3));
assert_eq!(v.get("items").idx(1).i64(), Some(20));
assert!(v.get("items").idx(99).is_null());
}
#[test]
fn test_from_none() {
let v = JValue::from(None::<Value>);
assert!(v.is_null());
assert_eq!(v.str_or("fallback"), "fallback");
}
#[test]
fn test_into_inner() {
let original = json!({"x": 1});
let j = JValue::from(original.clone());
assert_eq!(j.into_inner(), original);
}
}