cjson_bindings/
cjson.rs

1//! Safe Rust wrappers for cJSON library
2//!
3//! This module provides safe, idiomatic Rust interfaces over the cJSON C library.
4
5extern crate alloc;
6
7use alloc::ffi::CString;
8use alloc::string::String;
9use alloc::vec::Vec;
10use core::ffi::{CStr, c_char, c_int};
11use core::ptr;
12
13use crate::cjson_ffi::*;
14
15/// Result type for cJSON operations
16pub type CJsonResult<T> = Result<T, CJsonError>;
17
18/// Error types for cJSON operations
19#[derive(Debug)]
20pub enum CJsonError {
21    /// Failed to parse JSON
22    ParseError,
23    /// Null pointer encountered
24    NullPointer,
25    /// Invalid UTF-8 in string
26    InvalidUtf8,
27    /// Item not found
28    NotFound,
29    /// Wrong type
30    TypeError,
31    /// Memory allocation failed
32    AllocationError,
33    /// Invalid operation
34    InvalidOperation,
35}
36
37/// Safe wrapper for cJSON pointer
38pub struct CJson {
39    ptr: *mut cJSON,
40}
41
42impl CJson {
43    /// Create a new CJson wrapper from a raw pointer
44    /// 
45    /// # Safety
46    /// The pointer must be valid and owned by this wrapper
47    pub(crate) unsafe fn from_ptr(ptr: *mut cJSON) -> CJsonResult<Self> {
48        if ptr.is_null() {
49            Err(CJsonError::NullPointer)
50        } else {
51            Ok(CJson { ptr })
52        }
53    }
54
55    /// Get the raw pointer (does not transfer ownership)
56    pub fn as_ptr(&self) -> *const cJSON {
57        self.ptr
58    }
59
60    /// Get the mutable raw pointer (does not transfer ownership)
61    pub fn as_mut_ptr(&mut self) -> *mut cJSON {
62        self.ptr
63    }
64
65    /// Consume the wrapper and return the raw pointer (transfers ownership)
66    pub fn into_raw(self) -> *mut cJSON {
67        let ptr = self.ptr;
68        core::mem::forget(self);
69        ptr
70    }
71
72    // ========================
73    // PARSING FUNCTIONS
74    // ========================
75
76    /// Parse a JSON string
77    pub fn parse(json: &str) -> CJsonResult<Self> {
78        let c_str = CString::new(json).map_err(|_| CJsonError::InvalidUtf8)?;
79        let ptr = unsafe { cJSON_Parse(c_str.as_ptr()) };
80        unsafe { Self::from_ptr(ptr) }
81    }
82
83    /// Parse a JSON string with specified length
84    pub fn parse_with_length(json: &str, length: usize) -> CJsonResult<Self> {
85        let c_str = CString::new(json).map_err(|_| CJsonError::InvalidUtf8)?;
86        let ptr = unsafe { cJSON_ParseWithLength(c_str.as_ptr(), length) };
87        unsafe { Self::from_ptr(ptr) }
88    }
89
90    /// Parse a JSON string with options
91    pub fn parse_with_opts(json: &str, require_null_terminated: bool) -> CJsonResult<Self> {
92        let c_str = CString::new(json).map_err(|_| CJsonError::InvalidUtf8)?;
93        let ptr = unsafe {
94            cJSON_ParseWithOpts(
95                c_str.as_ptr(),
96                ptr::null_mut(),
97                if require_null_terminated { 1 } else { 0 },
98            )
99        };
100        unsafe { Self::from_ptr(ptr) }
101    }
102
103    // ========================
104    // PRINTING FUNCTIONS
105    // ========================
106
107    /// Print JSON to a formatted string
108    pub fn print(&self) -> CJsonResult<String> {
109        let c_str = unsafe { cJSON_Print(self.ptr) };
110        if c_str.is_null() {
111            return Err(CJsonError::AllocationError);
112        }
113        let rust_str = unsafe { CStr::from_ptr(c_str).to_string_lossy().into_owned() };
114        unsafe { cJSON_free(c_str as *mut core::ffi::c_void) };
115        Ok(rust_str)
116    }
117
118    /// Print JSON to an unformatted string
119    pub fn print_unformatted(&self) -> CJsonResult<String> {
120        let c_str = unsafe { cJSON_PrintUnformatted(self.ptr) };
121        if c_str.is_null() {
122            return Err(CJsonError::AllocationError);
123        }
124        let rust_str = unsafe { CStr::from_ptr(c_str).to_string_lossy().into_owned() };
125        unsafe { cJSON_free(c_str as *mut core::ffi::c_void) };
126        Ok(rust_str)
127    }
128
129    // ========================
130    // TYPE CHECKING FUNCTIONS
131    // ========================
132
133    /// Check if the item is invalid
134    pub fn is_invalid(&self) -> bool {
135        unsafe { cJSON_IsInvalid(self.ptr) != 0 }
136    }
137
138    /// Check if the item is false
139    pub fn is_false(&self) -> bool {
140        unsafe { cJSON_IsFalse(self.ptr) != 0 }
141    }
142
143    /// Check if the item is true
144    pub fn is_true(&self) -> bool {
145        unsafe { cJSON_IsTrue(self.ptr) != 0 }
146    }
147
148    /// Check if the item is a boolean
149    pub fn is_bool(&self) -> bool {
150        unsafe { cJSON_IsBool(self.ptr) != 0 }
151    }
152
153    /// Check if the item is null
154    pub fn is_null(&self) -> bool {
155        unsafe { cJSON_IsNull(self.ptr) != 0 }
156    }
157
158    /// Check if the item is a number
159    pub fn is_number(&self) -> bool {
160        unsafe { cJSON_IsNumber(self.ptr) != 0 }
161    }
162
163    /// Check if the item is a string
164    pub fn is_string(&self) -> bool {
165        unsafe { cJSON_IsString(self.ptr) != 0 }
166    }
167
168    /// Check if the item is an array
169    pub fn is_array(&self) -> bool {
170        unsafe { cJSON_IsArray(self.ptr) != 0 }
171    }
172
173    /// Check if the item is an object
174    pub fn is_object(&self) -> bool {
175        unsafe { cJSON_IsObject(self.ptr) != 0 }
176    }
177
178    /// Check if the item is raw JSON
179    pub fn is_raw(&self) -> bool {
180        unsafe { cJSON_IsRaw(self.ptr) != 0 }
181    }
182
183    // ========================
184    // VALUE RETRIEVAL FUNCTIONS
185    // ========================
186
187    /// Get string value
188    pub fn get_string_value(&self) -> CJsonResult<String> {
189        if !self.is_string() {
190            return Err(CJsonError::TypeError);
191        }
192        let c_str = unsafe { cJSON_GetStringValue(self.ptr) };
193        if c_str.is_null() {
194            return Err(CJsonError::NullPointer);
195        }
196        Ok(unsafe { CStr::from_ptr(c_str).to_string_lossy().into_owned() })
197    }
198
199    /// Get number value as f64
200    pub fn get_number_value(&self) -> CJsonResult<f64> {
201        if !self.is_number() {
202            return Err(CJsonError::TypeError);
203        }
204        Ok(unsafe { cJSON_GetNumberValue(self.ptr) })
205    }
206
207    /// Get number value as i32
208    pub fn get_int_value(&self) -> CJsonResult<i32> {
209        if !self.is_number() {
210            return Err(CJsonError::TypeError);
211        }
212        Ok(unsafe { (*self.ptr).valueint })
213    }
214
215    /// Get boolean value
216    pub fn get_bool_value(&self) -> CJsonResult<bool> {
217        if !self.is_bool() {
218            return Err(CJsonError::TypeError);
219        }
220        Ok(self.is_true())
221    }
222
223    // ========================
224    // ARRAY FUNCTIONS
225    // ========================
226
227    /// Get array size
228    pub fn get_array_size(&self) -> CJsonResult<usize> {
229        if !self.is_array() {
230            return Err(CJsonError::TypeError);
231        }
232        Ok(unsafe { cJSON_GetArraySize(self.ptr) as usize })
233    }
234
235    /// Get array item by index (borrowed reference)
236    pub fn get_array_item(&self, index: usize) -> CJsonResult<CJsonRef> {
237        if !self.is_array() {
238            return Err(CJsonError::TypeError);
239        }
240        let ptr = unsafe { cJSON_GetArrayItem(self.ptr, index as c_int) };
241        unsafe { CJsonRef::from_ptr(ptr) }.map_err(|_| CJsonError::NotFound)
242    }
243
244    // ========================
245    // OBJECT FUNCTIONS
246    // ========================
247
248    /// Get object item by key (borrowed reference)
249    pub fn get_object_item(&self, key: &str) -> CJsonResult<CJsonRef> {
250        if !self.is_object() {
251            return Err(CJsonError::TypeError);
252        }
253        let c_key = CString::new(key).map_err(|_| CJsonError::InvalidUtf8)?;
254        let ptr = unsafe { cJSON_GetObjectItem(self.ptr, c_key.as_ptr()) };
255        unsafe { CJsonRef::from_ptr(ptr) }.map_err(|_| CJsonError::NotFound)
256    }
257
258    /// Get object item by key (case sensitive, borrowed reference)
259    pub fn get_object_item_case_sensitive(&self, key: &str) -> CJsonResult<CJsonRef> {
260        if !self.is_object() {
261            return Err(CJsonError::TypeError);
262        }
263        let c_key = CString::new(key).map_err(|_| CJsonError::InvalidUtf8)?;
264        let ptr = unsafe { cJSON_GetObjectItemCaseSensitive(self.ptr, c_key.as_ptr()) };
265        unsafe { CJsonRef::from_ptr(ptr) }.map_err(|_| CJsonError::NotFound)
266    }
267
268    /// Check if object has item with given key
269    pub fn has_object_item(&self, key: &str) -> bool {
270        if !self.is_object() {
271            return false;
272        }
273        let Ok(c_key) = CString::new(key) else {
274            return false;
275        };
276        unsafe { cJSON_HasObjectItem(self.ptr, c_key.as_ptr()) != 0 }
277    }
278
279    // ========================
280    // CREATION FUNCTIONS
281    // ========================
282
283    /// Create a null value
284    pub fn create_null() -> CJsonResult<Self> {
285        let ptr = unsafe { cJSON_CreateNull() };
286        unsafe { Self::from_ptr(ptr) }
287    }
288
289    /// Create a true value
290    pub fn create_true() -> CJsonResult<Self> {
291        let ptr = unsafe { cJSON_CreateTrue() };
292        unsafe { Self::from_ptr(ptr) }
293    }
294
295    /// Create a false value
296    pub fn create_false() -> CJsonResult<Self> {
297        let ptr = unsafe { cJSON_CreateFalse() };
298        unsafe { Self::from_ptr(ptr) }
299    }
300
301    /// Create a boolean value
302    pub fn create_bool(value: bool) -> CJsonResult<Self> {
303        let ptr = unsafe { cJSON_CreateBool(if value { 1 } else { 0 }) };
304        unsafe { Self::from_ptr(ptr) }
305    }
306
307    /// Create a number value
308    pub fn create_number(value: f64) -> CJsonResult<Self> {
309        let ptr = unsafe { cJSON_CreateNumber(value) };
310        unsafe { Self::from_ptr(ptr) }
311    }
312
313    /// Create a string value
314    pub fn create_string(value: &str) -> CJsonResult<Self> {
315        let c_str = CString::new(value).map_err(|_| CJsonError::InvalidUtf8)?;
316        let ptr = unsafe { cJSON_CreateString(c_str.as_ptr()) };
317        unsafe { Self::from_ptr(ptr) }
318    }
319
320    /// Create an array
321    pub fn create_array() -> CJsonResult<Self> {
322        let ptr = unsafe { cJSON_CreateArray() };
323        unsafe { Self::from_ptr(ptr) }
324    }
325
326    /// Create an object
327    pub fn create_object() -> CJsonResult<Self> {
328        let ptr = unsafe { cJSON_CreateObject() };
329        unsafe { Self::from_ptr(ptr) }
330    }
331
332    /// Create an integer array
333    pub fn create_int_array(values: &[i32]) -> CJsonResult<Self> {
334        let ptr = unsafe { cJSON_CreateIntArray(values.as_ptr(), values.len() as c_int) };
335        unsafe { Self::from_ptr(ptr) }
336    }
337
338    /// Create a double array
339    pub fn create_double_array(values: &[f64]) -> CJsonResult<Self> {
340        let ptr = unsafe { cJSON_CreateDoubleArray(values.as_ptr(), values.len() as c_int) };
341        unsafe { Self::from_ptr(ptr) }
342    }
343
344    /// Create a string array
345    pub fn create_string_array(values: &[&str]) -> CJsonResult<Self> {
346        let c_strings: Vec<CString> = values
347            .iter()
348            .map(|s| CString::new(*s))
349            .collect::<Result<_, _>>()
350            .map_err(|_| CJsonError::InvalidUtf8)?;
351        
352        let c_ptrs: Vec<*const c_char> = c_strings.iter().map(|s| s.as_ptr()).collect();
353        
354        let ptr = unsafe { cJSON_CreateStringArray(c_ptrs.as_ptr(), c_ptrs.len() as c_int) };
355        unsafe { Self::from_ptr(ptr) }
356    }
357
358    // ========================
359    // ARRAY MANIPULATION FUNCTIONS
360    // ========================
361
362    /// Add item to array
363    pub fn add_item_to_array(&mut self, item: CJson) -> CJsonResult<()> {
364        if !self.is_array() {
365            return Err(CJsonError::TypeError);
366        }
367        let result = unsafe { cJSON_AddItemToArray(self.ptr, item.into_raw()) };
368        if result != 0 {
369            Ok(())
370        } else {
371            Err(CJsonError::InvalidOperation)
372        }
373    }
374
375    /// Delete item from array by index
376    pub fn delete_item_from_array(&mut self, index: usize) -> CJsonResult<()> {
377        if !self.is_array() {
378            return Err(CJsonError::TypeError);
379        }
380        unsafe { cJSON_DeleteItemFromArray(self.ptr, index as c_int) };
381        Ok(())
382    }
383
384    /// Detach item from array by index
385    pub fn detach_item_from_array(&mut self, index: usize) -> CJsonResult<CJson> {
386        if !self.is_array() {
387            return Err(CJsonError::TypeError);
388        }
389        let ptr = unsafe { cJSON_DetachItemFromArray(self.ptr, index as c_int) };
390        unsafe { Self::from_ptr(ptr) }
391    }
392
393    // ========================
394    // OBJECT MANIPULATION FUNCTIONS
395    // ========================
396
397    /// Add item to object
398    pub fn add_item_to_object(&mut self, key: &str, item: CJson) -> CJsonResult<()> {
399        if !self.is_object() {
400            return Err(CJsonError::TypeError);
401        }
402        let c_key = CString::new(key).map_err(|_| CJsonError::InvalidUtf8)?;
403        let result = unsafe { cJSON_AddItemToObject(self.ptr, c_key.as_ptr(), item.into_raw()) };
404        if result != 0 {
405            Ok(())
406        } else {
407            Err(CJsonError::InvalidOperation)
408        }
409    }
410
411    /// Add null to object
412    pub fn add_null_to_object(&mut self, key: &str) -> CJsonResult<()> {
413        if !self.is_object() {
414            return Err(CJsonError::TypeError);
415        }
416        let c_key = CString::new(key).map_err(|_| CJsonError::InvalidUtf8)?;
417        let ptr = unsafe { cJSON_AddNullToObject(self.ptr, c_key.as_ptr()) };
418        if ptr.is_null() {
419            Err(CJsonError::AllocationError)
420        } else {
421            Ok(())
422        }
423    }
424
425    /// Add true to object
426    pub fn add_true_to_object(&mut self, key: &str) -> CJsonResult<()> {
427        if !self.is_object() {
428            return Err(CJsonError::TypeError);
429        }
430        let c_key = CString::new(key).map_err(|_| CJsonError::InvalidUtf8)?;
431        let ptr = unsafe { cJSON_AddTrueToObject(self.ptr, c_key.as_ptr()) };
432        if ptr.is_null() {
433            Err(CJsonError::AllocationError)
434        } else {
435            Ok(())
436        }
437    }
438
439    /// Add false to object
440    pub fn add_false_to_object(&mut self, key: &str) -> CJsonResult<()> {
441        if !self.is_object() {
442            return Err(CJsonError::TypeError);
443        }
444        let c_key = CString::new(key).map_err(|_| CJsonError::InvalidUtf8)?;
445        let ptr = unsafe { cJSON_AddFalseToObject(self.ptr, c_key.as_ptr()) };
446        if ptr.is_null() {
447            Err(CJsonError::AllocationError)
448        } else {
449            Ok(())
450        }
451    }
452
453    /// Add boolean to object
454    pub fn add_bool_to_object(&mut self, key: &str, value: bool) -> CJsonResult<()> {
455        if !self.is_object() {
456            return Err(CJsonError::TypeError);
457        }
458        let c_key = CString::new(key).map_err(|_| CJsonError::InvalidUtf8)?;
459        let ptr = unsafe {
460            cJSON_AddBoolToObject(self.ptr, c_key.as_ptr(), if value { 1 } else { 0 })
461        };
462        if ptr.is_null() {
463            Err(CJsonError::AllocationError)
464        } else {
465            Ok(())
466        }
467    }
468
469    /// Add number to object
470    pub fn add_number_to_object(&mut self, key: &str, value: f64) -> CJsonResult<()> {
471        if !self.is_object() {
472            return Err(CJsonError::TypeError);
473        }
474        let c_key = CString::new(key).map_err(|_| CJsonError::InvalidUtf8)?;
475        let ptr = unsafe { cJSON_AddNumberToObject(self.ptr, c_key.as_ptr(), value) };
476        if ptr.is_null() {
477            Err(CJsonError::AllocationError)
478        } else {
479            Ok(())
480        }
481    }
482
483    /// Add string to object
484    pub fn add_string_to_object(&mut self, key: &str, value: &str) -> CJsonResult<()> {
485        if !self.is_object() {
486            return Err(CJsonError::TypeError);
487        }
488        let c_key = CString::new(key).map_err(|_| CJsonError::InvalidUtf8)?;
489        let c_value = CString::new(value).map_err(|_| CJsonError::InvalidUtf8)?;
490        let ptr = unsafe { cJSON_AddStringToObject(self.ptr, c_key.as_ptr(), c_value.as_ptr()) };
491        if ptr.is_null() {
492            Err(CJsonError::AllocationError)
493        } else {
494            Ok(())
495        }
496    }
497
498    /// Delete item from object by key
499    pub fn delete_item_from_object(&mut self, key: &str) -> CJsonResult<()> {
500        if !self.is_object() {
501            return Err(CJsonError::TypeError);
502        }
503        let c_key = CString::new(key).map_err(|_| CJsonError::InvalidUtf8)?;
504        unsafe { cJSON_DeleteItemFromObject(self.ptr, c_key.as_ptr()) };
505        Ok(())
506    }
507
508    /// Detach item from object by key
509    pub fn detach_item_from_object(&mut self, key: &str) -> CJsonResult<CJson> {
510        if !self.is_object() {
511            return Err(CJsonError::TypeError);
512        }
513        let c_key = CString::new(key).map_err(|_| CJsonError::InvalidUtf8)?;
514        let ptr = unsafe { cJSON_DetachItemFromObject(self.ptr, c_key.as_ptr()) };
515        unsafe { Self::from_ptr(ptr) }
516    }
517
518    // ========================
519    // UTILITY FUNCTIONS
520    // ========================
521
522    /// Duplicate the JSON item
523    pub fn duplicate(&self, recurse: bool) -> CJsonResult<Self> {
524        let ptr = unsafe { cJSON_Duplicate(self.ptr, if recurse { 1 } else { 0 }) };
525        unsafe { Self::from_ptr(ptr) }
526    }
527
528    /// Compare two JSON items
529    pub fn compare(&self, other: &CJson, case_sensitive: bool) -> bool {
530        unsafe {
531            cJSON_Compare(self.ptr, other.ptr, if case_sensitive { 1 } else { 0 }) != 0
532        }
533    }
534}
535
536impl Drop for CJson {
537    fn drop(&mut self) {
538        if !self.ptr.is_null() {
539            unsafe { cJSON_Delete(self.ptr) };
540        }
541    }
542}
543
544/// Borrowed reference to a cJSON item (does not own the pointer)
545pub struct CJsonRef {
546    ptr: *mut cJSON,
547}
548
549impl CJsonRef {
550    /// Create a new CJsonRef from a raw pointer (does not take ownership)
551    /// 
552    /// # Safety
553    /// The pointer must be valid and must outlive this reference
554    pub(crate) unsafe fn from_ptr(ptr: *mut cJSON) -> CJsonResult<Self> {
555        if ptr.is_null() {
556            Err(CJsonError::NullPointer)
557        } else {
558            Ok(CJsonRef { ptr })
559        }
560    }
561
562    /// Get the raw pointer (does not transfer ownership)
563    pub fn as_ptr(&self) -> *const cJSON {
564        self.ptr
565    }
566
567    /// Check if the item is a string
568    pub fn is_string(&self) -> bool {
569        unsafe { cJSON_IsString(self.ptr) != 0 }
570    }
571
572    /// Check if the item is a number
573    pub fn is_number(&self) -> bool {
574        unsafe { cJSON_IsNumber(self.ptr) != 0 }
575    }
576
577    /// Check if the item is a boolean
578    pub fn is_bool(&self) -> bool {
579        unsafe { cJSON_IsBool(self.ptr) != 0 }
580    }
581
582    /// Check if the item is null
583    pub fn is_null(&self) -> bool {
584        unsafe { cJSON_IsNull(self.ptr) != 0 }
585    }
586
587    /// Check if the item is an array
588    pub fn is_array(&self) -> bool {
589        unsafe { cJSON_IsArray(self.ptr) != 0 }
590    }
591
592    /// Check if the item is an object
593    pub fn is_object(&self) -> bool {
594        unsafe { cJSON_IsObject(self.ptr) != 0 }
595    }
596
597    /// Get string value
598    pub fn get_string_value(&self) -> CJsonResult<String> {
599        if !self.is_string() {
600            return Err(CJsonError::TypeError);
601        }
602        let c_str = unsafe { cJSON_GetStringValue(self.ptr) };
603        if c_str.is_null() {
604            return Err(CJsonError::NullPointer);
605        }
606        Ok(unsafe { CStr::from_ptr(c_str).to_string_lossy().into_owned() })
607    }
608
609    /// Get number value as f64
610    pub fn get_number_value(&self) -> CJsonResult<f64> {
611        if !self.is_number() {
612            return Err(CJsonError::TypeError);
613        }
614        Ok(unsafe { cJSON_GetNumberValue(self.ptr) })
615    }
616
617    /// Get number value as i32
618    pub fn get_int_value(&self) -> CJsonResult<i32> {
619        if !self.is_number() {
620            return Err(CJsonError::TypeError);
621        }
622        Ok(unsafe { (*self.ptr).valueint })
623    }
624
625    /// Get boolean value
626    pub fn get_bool_value(&self) -> CJsonResult<bool> {
627        if !self.is_bool() {
628            return Err(CJsonError::TypeError);
629        }
630        Ok(unsafe { cJSON_IsTrue(self.ptr) != 0 })
631    }
632
633    /// Get array size
634    pub fn get_array_size(&self) -> CJsonResult<usize> {
635        if !self.is_array() {
636            return Err(CJsonError::TypeError);
637        }
638        Ok(unsafe { cJSON_GetArraySize(self.ptr) as usize })
639    }
640
641    /// Get array item by index
642    pub fn get_array_item(&self, index: usize) -> CJsonResult<CJsonRef> {
643        if !self.is_array() {
644            return Err(CJsonError::TypeError);
645        }
646        let ptr = unsafe { cJSON_GetArrayItem(self.ptr, index as c_int) };
647        unsafe { CJsonRef::from_ptr(ptr) }.map_err(|_| CJsonError::NotFound)
648    }
649
650    /// Get object item by key
651    pub fn get_object_item(&self, key: &str) -> CJsonResult<CJsonRef> {
652        if !self.is_object() {
653            return Err(CJsonError::TypeError);
654        }
655        let c_key = CString::new(key).map_err(|_| CJsonError::InvalidUtf8)?;
656        let ptr = unsafe { cJSON_GetObjectItem(self.ptr, c_key.as_ptr()) };
657        unsafe { CJsonRef::from_ptr(ptr) }.map_err(|_| CJsonError::NotFound)
658    }
659}
660
661/// Get the cJSON library version
662#[allow(dead_code)]
663pub fn version() -> String {
664    let c_str = unsafe { cJSON_Version() };
665    unsafe { CStr::from_ptr(c_str).to_string_lossy().into_owned() }
666}
667
668/// Get the last parse error pointer
669#[allow(dead_code)]
670pub fn get_error_ptr() -> Option<String> {
671    let c_str = unsafe { cJSON_GetErrorPtr() };
672    if c_str.is_null() {
673        None
674    } else {
675        Some(unsafe { CStr::from_ptr(c_str).to_string_lossy().into_owned() })
676    }
677}
678
679/// Minify a JSON string in place
680#[allow(dead_code)]
681pub fn minify(json: &mut String) {
682    let c_str = CString::new(json.as_str()).expect("CString conversion failed");
683    unsafe {
684        let ptr = c_str.as_ptr() as *mut c_char;
685        cJSON_Minify(ptr);
686        *json = CStr::from_ptr(ptr).to_string_lossy().into_owned();
687    }
688}
689
690#[cfg(test)]
691mod tests {
692    use super::*;
693
694    #[test]
695    fn test_parse_simple_object() {
696        let json = r#"{"name":"John","age":30}"#;
697        let parsed = CJson::parse(json).unwrap();
698        assert!(parsed.is_object());
699    }
700
701    #[test]
702    fn test_parse_array() {
703        let json = r#"[1,2,3,4,5]"#;
704        let parsed = CJson::parse(json).unwrap();
705        assert!(parsed.is_array());
706        assert_eq!(parsed.get_array_size().unwrap(), 5);
707    }
708
709    #[test]
710    fn test_create_and_get_string() {
711        let json = CJson::create_string("Hello, World!").unwrap();
712        assert!(json.is_string());
713        assert_eq!(json.get_string_value().unwrap(), "Hello, World!");
714    }
715
716    #[test]
717    fn test_create_and_get_number() {
718        let json = CJson::create_number(42.5).unwrap();
719        assert!(json.is_number());
720        assert_eq!(json.get_number_value().unwrap(), 42.5);
721    }
722
723    #[test]
724    fn test_create_and_get_bool() {
725        let json_true = CJson::create_true().unwrap();
726        assert!(json_true.is_true());
727        assert!(json_true.is_bool());
728        assert_eq!(json_true.get_bool_value().unwrap(), true);
729
730        let json_false = CJson::create_false().unwrap();
731        assert!(json_false.is_false());
732        assert!(json_false.is_bool());
733        assert_eq!(json_false.get_bool_value().unwrap(), false);
734    }
735
736    #[test]
737    fn test_create_null() {
738        let json = CJson::create_null().unwrap();
739        assert!(json.is_null());
740    }
741
742    #[test]
743    fn test_create_object_and_add_items() {
744        let mut obj = CJson::create_object().unwrap();
745        obj.add_string_to_object("name", "Alice").unwrap();
746        obj.add_number_to_object("age", 25.0).unwrap();
747        obj.add_bool_to_object("active", true).unwrap();
748
749        assert!(obj.is_object());
750        assert!(obj.has_object_item("name"));
751        assert!(obj.has_object_item("age"));
752        assert!(obj.has_object_item("active"));
753
754        let name = obj.get_object_item("name").unwrap();
755        assert_eq!(name.get_string_value().unwrap(), "Alice");
756
757        let age = obj.get_object_item("age").unwrap();
758        assert_eq!(age.get_number_value().unwrap(), 25.0);
759    }
760
761    #[test]
762    fn test_create_array_and_add_items() {
763        let mut arr = CJson::create_array().unwrap();
764        arr.add_item_to_array(CJson::create_number(1.0).unwrap()).unwrap();
765        arr.add_item_to_array(CJson::create_number(2.0).unwrap()).unwrap();
766        arr.add_item_to_array(CJson::create_number(3.0).unwrap()).unwrap();
767
768        assert!(arr.is_array());
769        assert_eq!(arr.get_array_size().unwrap(), 3);
770
771        let item = arr.get_array_item(1).unwrap();
772        assert_eq!(item.get_number_value().unwrap(), 2.0);
773    }
774
775    #[test]
776    fn test_print_formatted() {
777        let mut obj = CJson::create_object().unwrap();
778        obj.add_string_to_object("key", "value").unwrap();
779        
780        let json_str = obj.print().unwrap();
781        assert!(json_str.contains("key"));
782        assert!(json_str.contains("value"));
783    }
784
785    #[test]
786    fn test_print_unformatted() {
787        let mut obj = CJson::create_object().unwrap();
788        obj.add_string_to_object("key", "value").unwrap();
789        
790        let json_str = obj.print_unformatted().unwrap();
791        assert!(json_str.contains("key"));
792        assert!(json_str.contains("value"));
793        assert!(!json_str.contains("\n")); // No newlines in unformatted
794    }
795
796    #[test]
797    fn test_duplicate() {
798        let original = CJson::create_string("test").unwrap();
799        let duplicate = original.duplicate(true).unwrap();
800        
801        assert_eq!(
802            original.get_string_value().unwrap(),
803            duplicate.get_string_value().unwrap()
804        );
805    }
806
807    #[test]
808    fn test_compare() {
809        let json1 = CJson::create_number(42.0).unwrap();
810        let json2 = CJson::create_number(42.0).unwrap();
811        let json3 = CJson::create_number(43.0).unwrap();
812
813        assert!(json1.compare(&json2, true));
814        assert!(!json1.compare(&json3, true));
815    }
816
817    #[test]
818    fn test_create_int_array() {
819        let values = [1, 2, 3, 4, 5];
820        let arr = CJson::create_int_array(&values).unwrap();
821        
822        assert!(arr.is_array());
823        assert_eq!(arr.get_array_size().unwrap(), 5);
824    }
825
826    #[test]
827    fn test_create_double_array() {
828        let values = [1.1, 2.2, 3.3];
829        let arr = CJson::create_double_array(&values).unwrap();
830        
831        assert!(arr.is_array());
832        assert_eq!(arr.get_array_size().unwrap(), 3);
833    }
834
835    #[test]
836    #[ignore] // Temporarily disabled due to potential double free issue
837    fn test_create_string_array() {
838        let values = ["foo", "bar", "baz"];
839        let arr = CJson::create_string_array(&values).unwrap();
840        
841        assert!(arr.is_array());
842        assert_eq!(arr.get_array_size().unwrap(), 3);
843    }
844
845    #[test]
846    fn test_delete_item_from_array() {
847        let mut arr = CJson::create_array().unwrap();
848        arr.add_item_to_array(CJson::create_number(1.0).unwrap()).unwrap();
849        arr.add_item_to_array(CJson::create_number(2.0).unwrap()).unwrap();
850        arr.add_item_to_array(CJson::create_number(3.0).unwrap()).unwrap();
851
852        assert_eq!(arr.get_array_size().unwrap(), 3);
853        arr.delete_item_from_array(1).unwrap();
854        assert_eq!(arr.get_array_size().unwrap(), 2);
855    }
856
857    #[test]
858    fn test_delete_item_from_object() {
859        let mut obj = CJson::create_object().unwrap();
860        obj.add_string_to_object("key1", "value1").unwrap();
861        obj.add_string_to_object("key2", "value2").unwrap();
862
863        assert!(obj.has_object_item("key1"));
864        obj.delete_item_from_object("key1").unwrap();
865        assert!(!obj.has_object_item("key1"));
866        assert!(obj.has_object_item("key2"));
867    }
868
869    #[test]
870    fn test_parse_nested_object() {
871        let json = r#"{"person":{"name":"John","age":30}}"#;
872        let parsed = CJson::parse(json).unwrap();
873        
874        let person = parsed.get_object_item("person").unwrap();
875        assert!(person.is_object());
876        
877        let name = person.get_object_item("name").unwrap();
878        assert_eq!(name.get_string_value().unwrap(), "John");
879    }
880
881    #[test]
882    fn test_type_error() {
883        let json = CJson::create_string("not a number").unwrap();
884        assert!(json.get_number_value().is_err());
885    }
886
887    #[test]
888    fn test_not_found_error() {
889        let obj = CJson::create_object().unwrap();
890        assert!(obj.get_object_item("nonexistent").is_err());
891    }
892
893    #[test]
894    fn test_parse_with_length() {
895        let json = r#"{"key":"value"}"#;
896        let parsed = CJson::parse_with_length(json, json.len()).unwrap();
897        assert!(parsed.is_object());
898    }
899
900    #[test]
901    fn test_case_sensitive_get() {
902        let mut obj = CJson::create_object().unwrap();
903        obj.add_string_to_object("Key", "value").unwrap();
904        
905        assert!(obj.get_object_item_case_sensitive("Key").is_ok());
906        assert!(obj.get_object_item_case_sensitive("key").is_err());
907    }
908}