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