cjson_bindings/
cjson_utils.rs1extern crate alloc;
7
8use alloc::ffi::CString;
9use alloc::string::String;
10use core::ffi::{CStr, c_char};
11
12use crate::cjson::{CJson, CJsonError, CJsonResult};
13use crate::cjson_ffi::cJSON;
14use crate::cjson_utils_ffi::*;
15
16pub struct JsonPointer;
18
19impl JsonPointer {
20 pub fn get(object: &CJson, pointer: &str) -> CJsonResult<CJsonRef> {
29 let c_pointer = CString::new(pointer).map_err(|_| CJsonError::InvalidUtf8)?;
30 let ptr = unsafe {
31 cJSONUtils_GetPointer(object.as_ptr() as *mut cJSON, c_pointer.as_ptr() as *const i8)
32 };
33 unsafe { CJsonRef::from_ptr(ptr) }.map_err(|_| CJsonError::NotFound)
34 }
35
36 pub fn get_case_sensitive(object: &CJson, pointer: &str) -> CJsonResult<CJsonRef> {
45 let c_pointer = CString::new(pointer).map_err(|_| CJsonError::InvalidUtf8)?;
46 let ptr = unsafe {
47 cJSONUtils_GetPointerCaseSensitive(
48 object.as_ptr() as *mut cJSON,
49 c_pointer.as_ptr() as *const i8,
50 )
51 };
52 unsafe { CJsonRef::from_ptr(ptr) }.map_err(|_| CJsonError::NotFound)
53 }
54
55 pub fn find_from_object_to(object: &CJson, target: &CJson) -> CJsonResult<String> {
64 let ptr = unsafe {
65 cJSONUtils_FindPointerFromObjectTo(object.as_ptr(), target.as_ptr())
66 };
67 if ptr.is_null() {
68 return Err(CJsonError::NotFound);
69 }
70 let path = unsafe { CStr::from_ptr(ptr as *const c_char).to_string_lossy().into_owned() };
71 unsafe { crate::cjson_ffi::cJSON_free(ptr as *mut core::ffi::c_void) };
72 Ok(path)
73 }
74}
75
76pub struct JsonPatch;
78
79impl JsonPatch {
80 pub fn generate(from: &mut CJson, to: &mut CJson) -> CJsonResult<CJson> {
91 let ptr = unsafe {
92 cJSONUtils_GeneratePatches(from.as_mut_ptr(), to.as_mut_ptr())
93 };
94 unsafe { CJson::from_ptr(ptr) }
95 }
96
97 pub fn generate_case_sensitive(from: &mut CJson, to: &mut CJson) -> CJsonResult<CJson> {
108 let ptr = unsafe {
109 cJSONUtils_GeneratePatchesCaseSensitive(from.as_mut_ptr(), to.as_mut_ptr())
110 };
111 unsafe { CJson::from_ptr(ptr) }
112 }
113
114 pub fn apply(object: &mut CJson, patches: &CJson) -> CJsonResult<()> {
123 let result = unsafe {
124 cJSONUtils_ApplyPatches(object.as_mut_ptr(), patches.as_ptr())
125 };
126 if result == 0 {
127 Ok(())
128 } else {
129 Err(CJsonError::InvalidOperation)
130 }
131 }
132
133 pub fn apply_case_sensitive(object: &mut CJson, patches: &CJson) -> CJsonResult<()> {
142 let result = unsafe {
143 cJSONUtils_ApplyPatchesCaseSensitive(object.as_mut_ptr(), patches.as_ptr())
144 };
145 if result == 0 {
146 Ok(())
147 } else {
148 Err(CJsonError::InvalidOperation)
149 }
150 }
151
152 pub fn add_to_array(
160 array: &mut CJson,
161 operation: &str,
162 path: &str,
163 value: Option<&CJson>,
164 ) -> CJsonResult<()> {
165 if !array.is_array() {
166 return Err(CJsonError::TypeError);
167 }
168
169 let c_operation = CString::new(operation).map_err(|_| CJsonError::InvalidUtf8)?;
170 let c_path = CString::new(path).map_err(|_| CJsonError::InvalidUtf8)?;
171
172 let value_ptr = value.map(|v| v.as_ptr()).unwrap_or(core::ptr::null());
173
174 unsafe {
175 cJSONUtils_AddPatchToArray(
176 array.as_mut_ptr(),
177 c_operation.as_ptr() as *const i8,
178 c_path.as_ptr() as *const i8,
179 value_ptr,
180 );
181 }
182 Ok(())
183 }
184}
185
186pub struct JsonMergePatch;
188
189impl JsonMergePatch {
190 pub fn apply(target: &mut CJson, patch: &CJson) -> CJsonResult<CJson> {
199 let ptr = unsafe {
200 cJSONUtils_MergePatch(target.as_mut_ptr(), patch.as_ptr())
201 };
202 unsafe { CJson::from_ptr(ptr) }
203 }
204
205 pub fn apply_case_sensitive(target: &mut CJson, patch: &CJson) -> CJsonResult<CJson> {
214 let ptr = unsafe {
215 cJSONUtils_MergePatchCaseSensitive(target.as_mut_ptr(), patch.as_ptr())
216 };
217 unsafe { CJson::from_ptr(ptr) }
218 }
219
220 pub fn generate(from: &mut CJson, to: &mut CJson) -> CJsonResult<CJson> {
231 let ptr = unsafe {
232 cJSONUtils_GenerateMergePatch(from.as_mut_ptr(), to.as_mut_ptr())
233 };
234 unsafe { CJson::from_ptr(ptr) }
235 }
236
237 pub fn generate_case_sensitive(from: &mut CJson, to: &mut CJson) -> CJsonResult<CJson> {
248 let ptr = unsafe {
249 cJSONUtils_GenerateMergePatchCaseSensitive(from.as_mut_ptr(), to.as_mut_ptr())
250 };
251 unsafe { CJson::from_ptr(ptr) }
252 }
253}
254
255pub struct JsonUtils;
257
258impl JsonUtils {
259 pub fn sort_object(object: &mut CJson) -> CJsonResult<()> {
264 if !object.is_object() {
265 return Err(CJsonError::TypeError);
266 }
267 unsafe { cJSONUtils_SortObject(object.as_mut_ptr()) };
268 Ok(())
269 }
270
271 pub fn sort_object_case_sensitive(object: &mut CJson) -> CJsonResult<()> {
276 if !object.is_object() {
277 return Err(CJsonError::TypeError);
278 }
279 unsafe { cJSONUtils_SortObjectCaseSensitive(object.as_mut_ptr()) };
280 Ok(())
281 }
282}
283
284pub use crate::cjson::CJsonRef;
286
287#[cfg(test)]
288mod tests {
289 use super::*;
290 use crate::cjson::CJson;
291
292 #[test]
293 fn test_json_pointer_get() {
294 let json = r#"{"foo":{"bar":[1,2,3]}}"#;
295 let obj = CJson::parse(json).unwrap();
296
297 let result = JsonPointer::get(&obj, "/foo/bar/1").unwrap();
298 assert_eq!(result.get_number_value().unwrap(), 2.0);
299 }
300
301 #[test]
302 fn test_json_pointer_get_case_sensitive() {
303 let json = r#"{"Foo":{"Bar":"test"}}"#;
304 let obj = CJson::parse(json).unwrap();
305
306 let result = JsonPointer::get_case_sensitive(&obj, "/Foo/Bar").unwrap();
307 assert_eq!(result.get_string_value().unwrap(), "test");
308 }
309
310 #[test]
311 fn test_json_pointer_not_found() {
312 let json = r#"{"foo":"bar"}"#;
313 let obj = CJson::parse(json).unwrap();
314
315 assert!(JsonPointer::get(&obj, "/nonexistent").is_err());
316 }
317
318 #[test]
319 fn test_json_patch_generate_and_apply() {
320 let from_json = r#"{"name":"John","age":30}"#;
321 let to_json = r#"{"name":"John","age":31,"city":"NYC"}"#;
322
323 let mut from = CJson::parse(from_json).unwrap();
324 let mut to = CJson::parse(to_json).unwrap();
325
326 let patches = JsonPatch::generate(&mut from, &mut to).unwrap();
327 assert!(patches.is_array());
328 }
329
330 #[test]
331 fn test_json_patch_apply() {
332 let obj_json = r#"{"name":"John","age":30}"#;
333 let patch_json = r#"[{"op":"replace","path":"/age","value":31}]"#;
334
335 let mut obj = CJson::parse(obj_json).unwrap();
336 let patches = CJson::parse(patch_json).unwrap();
337
338 JsonPatch::apply(&mut obj, &patches).unwrap();
339
340 let age = obj.get_object_item("age").unwrap();
341 assert_eq!(age.get_number_value().unwrap(), 31.0);
342 }
343
344 #[test]
345 fn test_json_merge_patch_apply() {
346 let target_json = r#"{"name":"John","age":30}"#;
347 let patch_json = r#"{"age":31,"city":"NYC"}"#;
348
349 let mut target = CJson::parse(target_json).unwrap();
350 let patch = CJson::parse(patch_json).unwrap();
351
352 let result = JsonMergePatch::apply(&mut target, &patch).unwrap();
353 core::mem::forget(target); let age = result.get_object_item("age").unwrap();
357 assert_eq!(age.get_number_value().unwrap(), 31.0);
358
359 let city = result.get_object_item("city").unwrap();
360 assert_eq!(city.get_string_value().unwrap(), "NYC");
361 }
362
363 #[test]
364 fn test_json_merge_patch_generate() {
365 let from_json = r#"{"name":"John","age":30}"#;
366 let to_json = r#"{"name":"John","age":31}"#;
367
368 let mut from = CJson::parse(from_json).unwrap();
369 let mut to = CJson::parse(to_json).unwrap();
370
371 let patch = JsonMergePatch::generate(&mut from, &mut to).unwrap();
372 assert!(patch.is_object());
373 }
374
375 #[test]
376 fn test_json_utils_sort_object() {
377 let json = r#"{"z":"last","a":"first","m":"middle"}"#;
378 let mut obj = CJson::parse(json).unwrap();
379
380 JsonUtils::sort_object(&mut obj).unwrap();
381
382 assert!(obj.is_object());
384 assert!(obj.has_object_item("a"));
385 assert!(obj.has_object_item("m"));
386 assert!(obj.has_object_item("z"));
387 }
388
389 #[test]
390 fn test_json_utils_sort_object_case_sensitive() {
391 let json = r#"{"Z":"last","a":"first","M":"middle"}"#;
392 let mut obj = CJson::parse(json).unwrap();
393
394 JsonUtils::sort_object_case_sensitive(&mut obj).unwrap();
395
396 assert!(obj.is_object());
397 assert!(obj.has_object_item("a"));
398 assert!(obj.has_object_item("M"));
399 assert!(obj.has_object_item("Z"));
400 }
401
402 #[test]
403 fn test_pointer_find_from_object_to() {
404 let json = r#"{"foo":{"bar":"test"}}"#;
405 let obj = CJson::parse(json).unwrap();
406
407 let target_owned = CJson::parse(r#"{"bar":"test"}"#).unwrap();
409
410 let _ = JsonPointer::find_from_object_to(&obj, &target_owned);
413 }
414
415 #[test]
416 fn test_json_patch_add_to_array() {
417 let mut patches = CJson::create_array().unwrap();
418
419 let value = CJson::create_string("test").unwrap();
420 JsonPatch::add_to_array(&mut patches, "add", "/foo", Some(&value)).unwrap();
421
422 assert!(patches.is_array());
423 assert_eq!(patches.get_array_size().unwrap(), 1);
424 }
425
426 #[test]
427 fn test_complex_pointer_path() {
428 let json = r#"{"users":[{"name":"Alice","age":25},{"name":"Bob","age":30}]}"#;
429 let obj = CJson::parse(json).unwrap();
430
431 let result = JsonPointer::get(&obj, "/users/0/name").unwrap();
432 assert_eq!(result.get_string_value().unwrap(), "Alice");
433
434 let result = JsonPointer::get(&obj, "/users/1/age").unwrap();
435 assert_eq!(result.get_number_value().unwrap(), 30.0);
436 }
437
438 #[test]
439 fn test_merge_patch_null_removal() {
440 let target_json = r#"{"name":"John","age":30,"city":"NYC"}"#;
441 let patch_json = r#"{"city":null}"#;
442
443 let mut target = CJson::parse(target_json).unwrap();
444 let patch = CJson::parse(patch_json).unwrap();
445
446 let result = JsonMergePatch::apply(&mut target, &patch).unwrap();
447 core::mem::forget(target); assert!(result.get_object_item("city").is_err());
451 assert!(result.has_object_item("name"));
452 assert!(result.has_object_item("age"));
453 }
454}