Skip to main content

cjson_binding/
cjson.rs

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