cjson_binding/
cjson_utils.rs1extern crate alloc;
27
28use alloc::ffi::CString;
29use alloc::string::String;
30use core::ffi::{CStr, c_char};
31
32use crate::cjson::{CJson, CJsonError, CJsonResult};
33use crate::cjson_ffi::cJSON;
34use crate::cjson_utils_ffi::*;
35
36pub struct JsonPointer;
38
39impl JsonPointer {
40 pub fn get(object: &CJson, pointer: &str) -> CJsonResult<CJsonRef> {
49 let c_pointer = CString::new(pointer).map_err(|_| CJsonError::InvalidUtf8)?;
50 let ptr = unsafe {
51 cJSONUtils_GetPointer(object.as_ptr() as *mut cJSON, c_pointer.as_ptr() as *const i8)
52 };
53 unsafe { CJsonRef::from_ptr(ptr) }.map_err(|_| CJsonError::NotFound)
54 }
55
56 pub fn get_case_sensitive(object: &CJson, pointer: &str) -> CJsonResult<CJsonRef> {
65 let c_pointer = CString::new(pointer).map_err(|_| CJsonError::InvalidUtf8)?;
66 let ptr = unsafe {
67 cJSONUtils_GetPointerCaseSensitive(
68 object.as_ptr() as *mut cJSON,
69 c_pointer.as_ptr() as *const i8,
70 )
71 };
72 unsafe { CJsonRef::from_ptr(ptr) }.map_err(|_| CJsonError::NotFound)
73 }
74
75 pub fn find_from_object_to(object: &CJson, target: &CJson) -> CJsonResult<String> {
84 let ptr = unsafe {
85 cJSONUtils_FindPointerFromObjectTo(object.as_ptr(), target.as_ptr())
86 };
87 if ptr.is_null() {
88 return Err(CJsonError::NotFound);
89 }
90 let path = unsafe { CStr::from_ptr(ptr as *const c_char).to_string_lossy().into_owned() };
91 unsafe { crate::cjson_ffi::cJSON_free(ptr as *mut core::ffi::c_void) };
92 Ok(path)
93 }
94}
95
96pub struct JsonPatch;
98
99impl JsonPatch {
100 pub fn generate(from: &mut CJson, to: &mut CJson) -> CJsonResult<CJson> {
111 let ptr = unsafe {
112 cJSONUtils_GeneratePatches(from.as_mut_ptr(), to.as_mut_ptr())
113 };
114 unsafe { CJson::from_ptr(ptr) }
115 }
116
117 pub fn generate_case_sensitive(from: &mut CJson, to: &mut CJson) -> CJsonResult<CJson> {
128 let ptr = unsafe {
129 cJSONUtils_GeneratePatchesCaseSensitive(from.as_mut_ptr(), to.as_mut_ptr())
130 };
131 unsafe { CJson::from_ptr(ptr) }
132 }
133
134 pub fn apply(object: &mut CJson, patches: &CJson) -> CJsonResult<()> {
143 let result = unsafe {
144 cJSONUtils_ApplyPatches(object.as_mut_ptr(), patches.as_ptr())
145 };
146 if result == 0 {
147 Ok(())
148 } else {
149 Err(CJsonError::InvalidOperation)
150 }
151 }
152
153 pub fn apply_case_sensitive(object: &mut CJson, patches: &CJson) -> CJsonResult<()> {
162 let result = unsafe {
163 cJSONUtils_ApplyPatchesCaseSensitive(object.as_mut_ptr(), patches.as_ptr())
164 };
165 if result == 0 {
166 Ok(())
167 } else {
168 Err(CJsonError::InvalidOperation)
169 }
170 }
171
172 pub fn add_to_array(
180 array: &mut CJson,
181 operation: &str,
182 path: &str,
183 value: Option<&CJson>,
184 ) -> CJsonResult<()> {
185 if !array.is_array() {
186 return Err(CJsonError::TypeError);
187 }
188
189 let c_operation = CString::new(operation).map_err(|_| CJsonError::InvalidUtf8)?;
190 let c_path = CString::new(path).map_err(|_| CJsonError::InvalidUtf8)?;
191
192 let value_ptr = value.map(|v| v.as_ptr()).unwrap_or(core::ptr::null());
193
194 unsafe {
195 cJSONUtils_AddPatchToArray(
196 array.as_mut_ptr(),
197 c_operation.as_ptr() as *const i8,
198 c_path.as_ptr() as *const i8,
199 value_ptr,
200 );
201 }
202 Ok(())
203 }
204}
205
206pub struct JsonMergePatch;
208
209impl JsonMergePatch {
210 pub fn apply(target: &mut CJson, patch: &CJson) -> CJsonResult<CJson> {
219 let ptr = unsafe {
220 cJSONUtils_MergePatch(target.as_mut_ptr(), patch.as_ptr())
221 };
222 unsafe { CJson::from_ptr(ptr) }
223 }
224
225 pub fn apply_case_sensitive(target: &mut CJson, patch: &CJson) -> CJsonResult<CJson> {
234 let ptr = unsafe {
235 cJSONUtils_MergePatchCaseSensitive(target.as_mut_ptr(), patch.as_ptr())
236 };
237 unsafe { CJson::from_ptr(ptr) }
238 }
239
240 pub fn generate(from: &mut CJson, to: &mut CJson) -> CJsonResult<CJson> {
251 let ptr = unsafe {
252 cJSONUtils_GenerateMergePatch(from.as_mut_ptr(), to.as_mut_ptr())
253 };
254 unsafe { CJson::from_ptr(ptr) }
255 }
256
257 pub fn generate_case_sensitive(from: &mut CJson, to: &mut CJson) -> CJsonResult<CJson> {
268 let ptr = unsafe {
269 cJSONUtils_GenerateMergePatchCaseSensitive(from.as_mut_ptr(), to.as_mut_ptr())
270 };
271 unsafe { CJson::from_ptr(ptr) }
272 }
273}
274
275pub struct JsonUtils;
277
278impl JsonUtils {
279 pub fn sort_object(object: &mut CJson) -> CJsonResult<()> {
284 if !object.is_object() {
285 return Err(CJsonError::TypeError);
286 }
287 unsafe { cJSONUtils_SortObject(object.as_mut_ptr()) };
288 Ok(())
289 }
290
291 pub fn sort_object_case_sensitive(object: &mut CJson) -> CJsonResult<()> {
296 if !object.is_object() {
297 return Err(CJsonError::TypeError);
298 }
299 unsafe { cJSONUtils_SortObjectCaseSensitive(object.as_mut_ptr()) };
300 Ok(())
301 }
302}
303
304pub use crate::cjson::CJsonRef;
306
307#[cfg(test)]
308mod tests {
309 use super::*;
310 use crate::cjson::CJson;
311
312 #[test]
313 fn test_json_pointer_get() {
314 let json = r#"{"foo":{"bar":[1,2,3]}}"#;
315 let obj = CJson::parse(json).unwrap();
316
317 let result = JsonPointer::get(&obj, "/foo/bar/1").unwrap();
318 assert_eq!(result.get_number_value().unwrap(), 2.0);
319 }
320
321 #[test]
322 fn test_json_pointer_get_case_sensitive() {
323 let json = r#"{"Foo":{"Bar":"test"}}"#;
324 let obj = CJson::parse(json).unwrap();
325
326 let result = JsonPointer::get_case_sensitive(&obj, "/Foo/Bar").unwrap();
327 assert_eq!(result.get_string_value().unwrap(), "test");
328 }
329
330 #[test]
331 fn test_json_pointer_not_found() {
332 let json = r#"{"foo":"bar"}"#;
333 let obj = CJson::parse(json).unwrap();
334
335 assert!(JsonPointer::get(&obj, "/nonexistent").is_err());
336 }
337
338 #[test]
339 fn test_json_patch_generate_and_apply() {
340 let from_json = r#"{"name":"John","age":30}"#;
341 let to_json = r#"{"name":"John","age":31,"city":"NYC"}"#;
342
343 let mut from = CJson::parse(from_json).unwrap();
344 let mut to = CJson::parse(to_json).unwrap();
345
346 let patches = JsonPatch::generate(&mut from, &mut to).unwrap();
347 assert!(patches.is_array());
348 }
349
350 #[test]
351 fn test_json_patch_apply() {
352 let obj_json = r#"{"name":"John","age":30}"#;
353 let patch_json = r#"[{"op":"replace","path":"/age","value":31}]"#;
354
355 let mut obj = CJson::parse(obj_json).unwrap();
356 let patches = CJson::parse(patch_json).unwrap();
357
358 JsonPatch::apply(&mut obj, &patches).unwrap();
359
360 let age = obj.get_object_item("age").unwrap();
361 assert_eq!(age.get_number_value().unwrap(), 31.0);
362 }
363
364 #[test]
365 fn test_json_merge_patch_apply() {
366 let target_json = r#"{"name":"John","age":30}"#;
367 let patch_json = r#"{"age":31,"city":"NYC"}"#;
368
369 let mut target = CJson::parse(target_json).unwrap();
370 let patch = CJson::parse(patch_json).unwrap();
371
372 let result = JsonMergePatch::apply(&mut target, &patch).unwrap();
373 core::mem::forget(target); let age = result.get_object_item("age").unwrap();
377 assert_eq!(age.get_number_value().unwrap(), 31.0);
378
379 let city = result.get_object_item("city").unwrap();
380 assert_eq!(city.get_string_value().unwrap(), "NYC");
381 }
382
383 #[test]
384 fn test_json_merge_patch_generate() {
385 let from_json = r#"{"name":"John","age":30}"#;
386 let to_json = r#"{"name":"John","age":31}"#;
387
388 let mut from = CJson::parse(from_json).unwrap();
389 let mut to = CJson::parse(to_json).unwrap();
390
391 let patch = JsonMergePatch::generate(&mut from, &mut to).unwrap();
392 assert!(patch.is_object());
393 }
394
395 #[test]
396 fn test_json_utils_sort_object() {
397 let json = r#"{"z":"last","a":"first","m":"middle"}"#;
398 let mut obj = CJson::parse(json).unwrap();
399
400 JsonUtils::sort_object(&mut obj).unwrap();
401
402 assert!(obj.is_object());
404 assert!(obj.has_object_item("a"));
405 assert!(obj.has_object_item("m"));
406 assert!(obj.has_object_item("z"));
407 }
408
409 #[test]
410 fn test_json_utils_sort_object_case_sensitive() {
411 let json = r#"{"Z":"last","a":"first","M":"middle"}"#;
412 let mut obj = CJson::parse(json).unwrap();
413
414 JsonUtils::sort_object_case_sensitive(&mut obj).unwrap();
415
416 assert!(obj.is_object());
417 assert!(obj.has_object_item("a"));
418 assert!(obj.has_object_item("M"));
419 assert!(obj.has_object_item("Z"));
420 }
421
422 #[test]
423 fn test_pointer_find_from_object_to() {
424 let json = r#"{"foo":{"bar":"test"}}"#;
425 let obj = CJson::parse(json).unwrap();
426
427 let target_owned = CJson::parse(r#"{"bar":"test"}"#).unwrap();
429
430 let _ = JsonPointer::find_from_object_to(&obj, &target_owned);
433 }
434
435 #[test]
436 fn test_json_patch_add_to_array() {
437 let mut patches = CJson::create_array().unwrap();
438
439 let value = CJson::create_string("test").unwrap();
440 JsonPatch::add_to_array(&mut patches, "add", "/foo", Some(&value)).unwrap();
441
442 assert!(patches.is_array());
443 assert_eq!(patches.get_array_size().unwrap(), 1);
444 }
445
446 #[test]
447 fn test_complex_pointer_path() {
448 let json = r#"{"users":[{"name":"Alice","age":25},{"name":"Bob","age":30}]}"#;
449 let obj = CJson::parse(json).unwrap();
450
451 let result = JsonPointer::get(&obj, "/users/0/name").unwrap();
452 assert_eq!(result.get_string_value().unwrap(), "Alice");
453
454 let result = JsonPointer::get(&obj, "/users/1/age").unwrap();
455 assert_eq!(result.get_number_value().unwrap(), 30.0);
456 }
457
458 #[test]
459 fn test_merge_patch_null_removal() {
460 let target_json = r#"{"name":"John","age":30,"city":"NYC"}"#;
461 let patch_json = r#"{"city":null}"#;
462
463 let mut target = CJson::parse(target_json).unwrap();
464 let patch = CJson::parse(patch_json).unwrap();
465
466 let result = JsonMergePatch::apply(&mut target, &patch).unwrap();
467 core::mem::forget(target); assert!(result.get_object_item("city").is_err());
471 assert!(result.has_object_item("name"));
472 assert!(result.has_object_item("age"));
473 }
474}