windjammer_runtime/
json.rs1use serde_json::Value;
6
7pub fn parse(s: &str) -> Result<Value, String> {
9 serde_json::from_str(s).map_err(|e| e.to_string())
10}
11
12pub fn stringify(value: &Value) -> Result<String, String> {
14 serde_json::to_string(value).map_err(|e| e.to_string())
15}
16
17pub fn stringify_pretty(value: &Value) -> Result<String, String> {
19 serde_json::to_string_pretty(value).map_err(|e| e.to_string())
20}
21
22pub fn object() -> Value {
24 Value::Object(serde_json::Map::new())
25}
26
27pub fn array() -> Value {
29 Value::Array(Vec::new())
30}
31
32pub fn null() -> Value {
34 Value::Null
35}
36
37pub fn boolean(b: bool) -> Value {
39 Value::Bool(b)
40}
41
42pub fn number_i64(n: i64) -> Value {
44 Value::Number(n.into())
45}
46
47pub 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
54pub fn string(s: &str) -> Value {
56 Value::String(s.to_string())
57}
58
59pub fn get<'a>(value: &'a Value, key: &str) -> Option<&'a Value> {
61 value.get(key)
62}
63
64pub 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
89pub 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
106pub 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
113pub fn get_number(value: &Value, key: &str) -> Option<f64> {
115 value.get(key).and_then(|v| v.as_f64())
116}
117
118pub fn get_bool(value: &Value, key: &str) -> Option<bool> {
120 value.get(key).and_then(|v| v.as_bool())
121}
122
123pub 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
133pub 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
142pub fn is_empty(value: &Value) -> bool {
144 len(value) == 0
145}
146
147pub 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 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}