janus_plugin/
jansson.rs

1/// Utilities to work with Jansson JSON values, which are exposed in the Janus plugin API.
2
3use bitflags::bitflags;
4use jansson_sys;
5use std::error::Error;
6use std::fmt;
7use std::ffi::{CStr, CString};
8use std::mem::MaybeUninit;
9use std::ops::Deref;
10use std::slice;
11use std::str;
12use crate::utils::LibcString;
13
14/// A pointer to a raw Jansson value struct.
15pub type RawJanssonValue = jansson_sys::json_t;
16
17bitflags! {
18    /// Flags that can be passed to JSON decoding functions.
19    pub struct JanssonDecodingFlags: usize {
20        const JSON_REJECT_DUPLICATES = 0x0001;
21        const JSON_DISABLE_EOF_CHECK = 0x0002;
22        const JSON_DECODE_ANY = 0x0004;
23        const JSON_DECODE_INT_AS_REAL = 0x0008;
24        const JSON_ALLOW_NUL = 0x0010;
25    }
26}
27
28bitflags! {
29    /// Flags that can be passed to JSON encoding functions.
30    pub struct JanssonEncodingFlags: usize {
31        const JSON_COMPACT = 0x0020;
32        const JSON_ENSURE_ASCII = 0x0040;
33        const JSON_SORT_KEYS = 0x0080;
34        const JSON_PRESERVE_ORDER = 0x0100;
35        const JSON_ENCODE_ANY = 0x0200;
36        const JSON_ESCAPE_SLASH = 0x0400;
37        const JSON_EMBED = 0x0800;
38    }
39}
40
41/// A safe wrapper for a Jansson JSON value. Automatically increases and decreases the refcount
42/// of the underlying value when cloned/dropped.
43pub struct JanssonValue {
44    ptr: *mut RawJanssonValue,
45}
46
47impl JanssonValue {
48    /// Creates a `JanssonValue` owning the given raw pointer, incrementing the reference count.
49    pub unsafe fn from_and_incref(ptr: *mut RawJanssonValue) -> Option<Self> {
50        jansson_sys::json_incref(ptr);
51        Self::from_raw(ptr)
52    }
53
54    /// Creates a `JanssonValue` owning the given raw pointer. Does not increment the reference count, but the
55    /// `JanssonValue`'s destructor will decrement the reference count.
56    pub unsafe fn from_raw(ptr: *mut RawJanssonValue) -> Option<Self> {
57        ptr.as_mut().map(|p| Self { ptr: p })
58    }
59
60    /// Transfers ownership of this value to this pointer. The consumer of the pointer is responsible for calling
61    /// json_decref on it later.
62    pub fn into_raw(self) -> *mut RawJanssonValue {
63        unsafe { jansson_sys::json_incref(self.ptr) };
64        self.ptr
65    }
66
67    /// Gets the reference backing this value without taking ownership.
68    pub fn as_mut_ref(&mut self) -> &mut RawJanssonValue {
69        unsafe { self.ptr.as_mut().unwrap() }
70    }
71
72    /// Decodes a JSON string slice into a Jansson value, returning an error if decoding fails.
73    pub fn from_str(input: &str, decoding_flags: JanssonDecodingFlags) -> Result<Self, Box<dyn Error + Send + Sync>> {
74        Self::from_cstr(&CString::new(input)?, decoding_flags)
75    }
76
77    /// Decodes a JSON C-style string into a Jansson value, returning an error if decoding fails.
78    pub fn from_cstr(input: &CStr, decoding_flags: JanssonDecodingFlags) -> Result<Self, Box<dyn Error + Send + Sync>> {
79        unsafe {
80            let mut error = MaybeUninit::<jansson_sys::json_error_t>::uninit();
81            let result = jansson_sys::json_loads(input.as_ptr(), decoding_flags.bits(), error.as_mut_ptr());
82            match Self::from_raw(result) {
83                Some(val) => Ok(val),
84                None => {
85                    let ptr = &error.assume_init().text as *const _;
86                    let len = libc::strlen(ptr);
87                    let sli = slice::from_raw_parts(ptr as *mut u8, len);
88                    Err(From::from(str::from_utf8(sli)?))
89                }
90            }
91        }
92    }
93
94    /// Encodes this Jansson value as a JSON owned C-style string.
95    pub fn to_libcstring(&self, encoding_flags: JanssonEncodingFlags) -> LibcString {
96        unsafe {
97            let json = jansson_sys::json_dumps(self.ptr, encoding_flags.bits());
98            LibcString::from_chars(json).expect("Error writing JSON output from Jansson value :(")
99        }
100    }
101}
102
103impl fmt::Debug for JanssonValue {
104    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
105        write!(f, "JanssonValue {{ {} }}", self.to_libcstring(JanssonEncodingFlags::empty()).to_string_lossy())
106    }
107}
108
109impl Deref for JanssonValue {
110    type Target = RawJanssonValue;
111
112    fn deref(&self) -> &RawJanssonValue {
113        unsafe { &*self.ptr }
114    }
115}
116
117impl Clone for JanssonValue {
118    fn clone(&self) -> Self {
119        unsafe { jansson_sys::json_incref(self.ptr) };
120        Self { ptr: self.ptr }
121    }
122}
123
124impl Drop for JanssonValue {
125    fn drop(&mut self) {
126        unsafe { jansson_sys::json_decref(self.ptr) }
127    }
128}
129
130unsafe impl Send for JanssonValue {}
131
132#[cfg(test)]
133mod tests {
134
135    use super::*;
136
137    #[test]
138    fn round_trip() {
139        let json = r#"{"a": "alpha", "b": true, "c": false, "d": 42, "e": 1.25, "f": null, "g": [1, 2, 3]}"#;
140        let result = JanssonValue::from_str(json, JanssonDecodingFlags::empty()).unwrap();
141        assert_eq!(json, result.to_libcstring(JanssonEncodingFlags::JSON_SORT_KEYS).to_str().unwrap());
142    }
143
144    #[test]
145    fn produce_jansson_errors() {
146        let json = r#"{"a":"#;
147        let result = JanssonValue::from_str(json, JanssonDecodingFlags::empty());
148        assert!(result.is_err());
149    }
150}