use serde_json::Value;
pub fn parse(s: &str) -> Result<Value, String> {
serde_json::from_str(s).map_err(|e| e.to_string())
}
pub fn stringify(value: &Value) -> Result<String, String> {
serde_json::to_string(value).map_err(|e| e.to_string())
}
pub fn stringify_pretty(value: &Value) -> Result<String, String> {
serde_json::to_string_pretty(value).map_err(|e| e.to_string())
}
pub fn object() -> Value {
Value::Object(serde_json::Map::new())
}
pub fn array() -> Value {
Value::Array(Vec::new())
}
pub fn null() -> Value {
Value::Null
}
pub fn boolean(b: bool) -> Value {
Value::Bool(b)
}
pub fn number_i64(n: i64) -> Value {
Value::Number(n.into())
}
pub fn number_f64(n: f64) -> Result<Value, String> {
serde_json::Number::from_f64(n)
.map(Value::Number)
.ok_or_else(|| "Invalid number".to_string())
}
pub fn string(s: &str) -> Value {
Value::String(s.to_string())
}
pub fn get<'a>(value: &'a Value, key: &str) -> Option<&'a Value> {
value.get(key)
}
pub fn is_object(value: &Value) -> bool {
value.is_object()
}
pub fn is_array(value: &Value) -> bool {
value.is_array()
}
pub fn is_string(value: &Value) -> bool {
value.is_string()
}
pub fn is_number(value: &Value) -> bool {
value.is_number()
}
pub fn is_bool(value: &Value) -> bool {
value.is_boolean()
}
pub fn is_null(value: &Value) -> bool {
value.is_null()
}
pub fn as_str(value: &Value) -> Option<String> {
value.as_str().map(|s| s.to_string())
}
pub fn as_i64(value: &Value) -> Option<i64> {
value.as_i64()
}
pub fn as_f64(value: &Value) -> Option<f64> {
value.as_f64()
}
pub fn as_bool(value: &Value) -> Option<bool> {
value.as_bool()
}
pub fn get_string(value: &Value, key: &str) -> Option<String> {
value
.get(key)
.and_then(|v| v.as_str().map(|s| s.to_string()))
}
pub fn get_number(value: &Value, key: &str) -> Option<f64> {
value.get(key).and_then(|v| v.as_f64())
}
pub fn get_bool(value: &Value, key: &str) -> Option<bool> {
value.get(key).and_then(|v| v.as_bool())
}
pub fn set(value: &mut Value, key: &str, new_value: Value) -> Result<(), String> {
if let Some(obj) = value.as_object_mut() {
obj.insert(key.to_string(), new_value);
Ok(())
} else {
Err("Value is not an object".to_string())
}
}
pub fn len(value: &Value) -> usize {
match value {
Value::Array(arr) => arr.len(),
Value::Object(obj) => obj.len(),
_ => 0,
}
}
pub fn is_empty(value: &Value) -> bool {
len(value) == 0
}
pub fn get_index(value: &Value, index: usize) -> Option<&Value> {
value.get(index)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_stringify() {
let json_str = r#"{"name":"Alice","age":30}"#;
let value = parse(json_str).unwrap();
let result = stringify(&value).unwrap();
let original: serde_json::Value = serde_json::from_str(json_str).unwrap();
let parsed: serde_json::Value = serde_json::from_str(&result).unwrap();
assert_eq!(original, parsed);
}
#[test]
fn test_constructors() {
assert_eq!(null(), Value::Null);
assert_eq!(boolean(true), Value::Bool(true));
assert_eq!(string("test"), Value::String("test".to_string()));
assert!(matches!(number_i64(42), Value::Number(_)));
}
#[test]
fn test_pretty_print() {
let value = parse(r#"{"a":1,"b":2}"#).unwrap();
let pretty = stringify_pretty(&value).unwrap();
assert!(pretty.contains('\n'));
assert!(pretty.contains(" "));
}
#[test]
fn test_get() {
let json = r#"{"name": "Alice", "age": 30, "active": true}"#;
let value = parse(json).unwrap();
assert!(get(&value, "name").is_some());
assert_eq!(get_string(&value, "name"), Some("Alice".to_string()));
assert_eq!(get_number(&value, "age"), Some(30.0));
assert_eq!(get_bool(&value, "active"), Some(true));
}
#[test]
fn test_set() {
let json = r#"{"name": "Alice"}"#;
let mut value = parse(json).unwrap();
let new_name = string("Bob");
let result = set(&mut value, "name", new_name);
assert!(result.is_ok());
assert_eq!(get_string(&value, "name"), Some("Bob".to_string()));
}
#[test]
fn test_len_is_empty() {
let json_array = r#"[1, 2, 3, 4, 5]"#;
let value_array = parse(json_array).unwrap();
assert_eq!(len(&value_array), 5);
assert!(!is_empty(&value_array));
let json_object = r#"{"a": 1, "b": 2}"#;
let value_object = parse(json_object).unwrap();
assert_eq!(len(&value_object), 2);
assert!(!is_empty(&value_object));
let empty_array = array();
assert_eq!(len(&empty_array), 0);
assert!(is_empty(&empty_array));
}
#[test]
fn test_type_predicates_and_coercions() {
let value = parse(r#"{"name":"Alice","age":30,"active":true,"nil":null}"#).unwrap();
assert!(is_object(&value));
assert!(!is_array(&value));
let name = get(&value, "name").unwrap();
assert!(is_string(name));
assert_eq!(as_str(name), Some("Alice".to_string()));
let age = get(&value, "age").unwrap();
assert!(is_number(age));
assert_eq!(as_i64(age), Some(30));
let active = get(&value, "active").unwrap();
assert!(is_bool(active));
assert_eq!(as_bool(active), Some(true));
let nil = get(&value, "nil").unwrap();
assert!(is_null(nil));
}
}