Skip to main content

windjammer_runtime/
json.rs

1//! JSON serialization and deserialization
2//!
3//! Windjammer's `std::json` module maps to these functions.
4
5use serde_json::Value;
6
7/// Parse JSON string into a Value
8pub fn parse(s: &str) -> Result<Value, String> {
9    serde_json::from_str(s).map_err(|e| e.to_string())
10}
11
12/// Convert Value to JSON string
13pub fn stringify(value: &Value) -> Result<String, String> {
14    serde_json::to_string(value).map_err(|e| e.to_string())
15}
16
17/// Convert Value to pretty-printed JSON string
18pub fn stringify_pretty(value: &Value) -> Result<String, String> {
19    serde_json::to_string_pretty(value).map_err(|e| e.to_string())
20}
21
22/// Create a JSON object (map)
23pub fn object() -> Value {
24    Value::Object(serde_json::Map::new())
25}
26
27/// Create a JSON array
28pub fn array() -> Value {
29    Value::Array(Vec::new())
30}
31
32/// Create a JSON null value
33pub fn null() -> Value {
34    Value::Null
35}
36
37/// Create a JSON boolean value
38pub fn boolean(b: bool) -> Value {
39    Value::Bool(b)
40}
41
42/// Create a JSON number value from i64
43pub fn number_i64(n: i64) -> Value {
44    Value::Number(n.into())
45}
46
47/// Create a JSON number value from f64
48pub fn number_f64(n: f64) -> Result<Value, String> {
49    serde_json::Number::from_f64(n)
50        .map(Value::Number)
51        .ok_or_else(|| "Invalid number".to_string())
52}
53
54/// Create a JSON string value
55pub fn string(s: &str) -> Value {
56    Value::String(s.to_string())
57}
58
59/// Get value from object by key
60pub fn get<'a>(value: &'a Value, key: &str) -> Option<&'a Value> {
61    value.get(key)
62}
63
64/// Type predicates (Windjammer `std::json` surface)
65pub fn is_object(value: &Value) -> bool {
66    value.is_object()
67}
68
69pub fn is_array(value: &Value) -> bool {
70    value.is_array()
71}
72
73pub fn is_string(value: &Value) -> bool {
74    value.is_string()
75}
76
77pub fn is_number(value: &Value) -> bool {
78    value.is_number()
79}
80
81pub fn is_bool(value: &Value) -> bool {
82    value.is_boolean()
83}
84
85pub fn is_null(value: &Value) -> bool {
86    value.is_null()
87}
88
89/// Value coercions
90pub fn as_str(value: &Value) -> Option<String> {
91    value.as_str().map(|s| s.to_string())
92}
93
94pub fn as_i64(value: &Value) -> Option<i64> {
95    value.as_i64()
96}
97
98pub fn as_f64(value: &Value) -> Option<f64> {
99    value.as_f64()
100}
101
102pub fn as_bool(value: &Value) -> Option<bool> {
103    value.as_bool()
104}
105
106/// Get string from object by key
107pub fn get_string(value: &Value, key: &str) -> Option<String> {
108    value
109        .get(key)
110        .and_then(|v| v.as_str().map(|s| s.to_string()))
111}
112
113/// Get number from object by key  
114pub fn get_number(value: &Value, key: &str) -> Option<f64> {
115    value.get(key).and_then(|v| v.as_f64())
116}
117
118/// Get boolean from object by key
119pub fn get_bool(value: &Value, key: &str) -> Option<bool> {
120    value.get(key).and_then(|v| v.as_bool())
121}
122
123/// Set value in object by key
124pub fn set(value: &mut Value, key: &str, new_value: Value) -> Result<(), String> {
125    if let Some(obj) = value.as_object_mut() {
126        obj.insert(key.to_string(), new_value);
127        Ok(())
128    } else {
129        Err("Value is not an object".to_string())
130    }
131}
132
133/// Get length of array or object
134pub fn len(value: &Value) -> usize {
135    match value {
136        Value::Array(arr) => arr.len(),
137        Value::Object(obj) => obj.len(),
138        _ => 0,
139    }
140}
141
142/// Check if array or object is empty
143pub fn is_empty(value: &Value) -> bool {
144    len(value) == 0
145}
146
147/// Get array element by index
148pub fn get_index(value: &Value, index: usize) -> Option<&Value> {
149    value.get(index)
150}
151
152#[cfg(test)]
153mod tests {
154    use super::*;
155
156    #[test]
157    fn test_parse_stringify() {
158        let json_str = r#"{"name":"Alice","age":30}"#;
159        let value = parse(json_str).unwrap();
160        let result = stringify(&value).unwrap();
161
162        // Parse both to compare (order might differ)
163        let original: serde_json::Value = serde_json::from_str(json_str).unwrap();
164        let parsed: serde_json::Value = serde_json::from_str(&result).unwrap();
165        assert_eq!(original, parsed);
166    }
167
168    #[test]
169    fn test_constructors() {
170        assert_eq!(null(), Value::Null);
171        assert_eq!(boolean(true), Value::Bool(true));
172        assert_eq!(string("test"), Value::String("test".to_string()));
173        assert!(matches!(number_i64(42), Value::Number(_)));
174    }
175
176    #[test]
177    fn test_pretty_print() {
178        let value = parse(r#"{"a":1,"b":2}"#).unwrap();
179        let pretty = stringify_pretty(&value).unwrap();
180        assert!(pretty.contains('\n'));
181        assert!(pretty.contains("  "));
182    }
183
184    #[test]
185    fn test_get() {
186        let json = r#"{"name": "Alice", "age": 30, "active": true}"#;
187        let value = parse(json).unwrap();
188
189        assert!(get(&value, "name").is_some());
190        assert_eq!(get_string(&value, "name"), Some("Alice".to_string()));
191        assert_eq!(get_number(&value, "age"), Some(30.0));
192        assert_eq!(get_bool(&value, "active"), Some(true));
193    }
194
195    #[test]
196    fn test_set() {
197        let json = r#"{"name": "Alice"}"#;
198        let mut value = parse(json).unwrap();
199
200        let new_name = string("Bob");
201        let result = set(&mut value, "name", new_name);
202        assert!(result.is_ok());
203        assert_eq!(get_string(&value, "name"), Some("Bob".to_string()));
204    }
205
206    #[test]
207    fn test_len_is_empty() {
208        let json_array = r#"[1, 2, 3, 4, 5]"#;
209        let value_array = parse(json_array).unwrap();
210        assert_eq!(len(&value_array), 5);
211        assert!(!is_empty(&value_array));
212
213        let json_object = r#"{"a": 1, "b": 2}"#;
214        let value_object = parse(json_object).unwrap();
215        assert_eq!(len(&value_object), 2);
216        assert!(!is_empty(&value_object));
217
218        let empty_array = array();
219        assert_eq!(len(&empty_array), 0);
220        assert!(is_empty(&empty_array));
221    }
222
223    #[test]
224    fn test_type_predicates_and_coercions() {
225        let value = parse(r#"{"name":"Alice","age":30,"active":true,"nil":null}"#).unwrap();
226        assert!(is_object(&value));
227        assert!(!is_array(&value));
228
229        let name = get(&value, "name").unwrap();
230        assert!(is_string(name));
231        assert_eq!(as_str(name), Some("Alice".to_string()));
232
233        let age = get(&value, "age").unwrap();
234        assert!(is_number(age));
235        assert_eq!(as_i64(age), Some(30));
236
237        let active = get(&value, "active").unwrap();
238        assert!(is_bool(active));
239        assert_eq!(as_bool(active), Some(true));
240
241        let nil = get(&value, "nil").unwrap();
242        assert!(is_null(nil));
243    }
244}