1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
/// Utilities to work with Jansson JSON values, which are exposed in the Janus plugin API.

use bitflags::bitflags;
use jansson_sys;
use std::error::Error;
use std::fmt;
use std::ffi::{CStr, CString};
use std::mem::MaybeUninit;
use std::ops::Deref;
use std::slice;
use std::str;
use crate::utils::LibcString;

/// A pointer to a raw Jansson value struct.
pub type RawJanssonValue = jansson_sys::json_t;

bitflags! {
    /// Flags that can be passed to JSON decoding functions.
    pub struct JanssonDecodingFlags: usize {
        const JSON_REJECT_DUPLICATES = 0x0001;
        const JSON_DISABLE_EOF_CHECK = 0x0002;
        const JSON_DECODE_ANY = 0x0004;
        const JSON_DECODE_INT_AS_REAL = 0x0008;
        const JSON_ALLOW_NUL = 0x0010;
    }
}

bitflags! {
    /// Flags that can be passed to JSON encoding functions.
    pub struct JanssonEncodingFlags: usize {
        const JSON_COMPACT = 0x0020;
        const JSON_ENSURE_ASCII = 0x0040;
        const JSON_SORT_KEYS = 0x0080;
        const JSON_PRESERVE_ORDER = 0x0100;
        const JSON_ENCODE_ANY = 0x0200;
        const JSON_ESCAPE_SLASH = 0x0400;
        const JSON_EMBED = 0x0800;
    }
}

/// A safe wrapper for a Jansson JSON value. Automatically increases and decreases the refcount
/// of the underlying value when cloned/dropped.
pub struct JanssonValue {
    ptr: *mut RawJanssonValue,
}

impl JanssonValue {
    /// Creates a `JanssonValue` owning the given raw pointer, incrementing the reference count.
    pub unsafe fn from_and_incref(ptr: *mut RawJanssonValue) -> Option<Self> {
        jansson_sys::json_incref(ptr);
        Self::from_raw(ptr)
    }

    /// Creates a `JanssonValue` owning the given raw pointer. Does not increment the reference count, but the
    /// `JanssonValue`'s destructor will decrement the reference count.
    pub unsafe fn from_raw(ptr: *mut RawJanssonValue) -> Option<Self> {
        ptr.as_mut().map(|p| Self { ptr: p })
    }

    /// Transfers ownership of this value to this pointer. The consumer of the pointer is responsible for calling
    /// json_decref on it later.
    pub fn into_raw(self) -> *mut RawJanssonValue {
        unsafe { jansson_sys::json_incref(self.ptr) };
        self.ptr
    }

    /// Gets the reference backing this value without taking ownership.
    pub fn as_mut_ref(&mut self) -> &mut RawJanssonValue {
        unsafe { self.ptr.as_mut().unwrap() }
    }

    /// Decodes a JSON string slice into a Jansson value, returning an error if decoding fails.
    pub fn from_str(input: &str, decoding_flags: JanssonDecodingFlags) -> Result<Self, Box<dyn Error + Send + Sync>> {
        Self::from_cstr(&CString::new(input)?, decoding_flags)
    }

    /// Decodes a JSON C-style string into a Jansson value, returning an error if decoding fails.
    pub fn from_cstr(input: &CStr, decoding_flags: JanssonDecodingFlags) -> Result<Self, Box<dyn Error + Send + Sync>> {
        unsafe {
            let mut error = MaybeUninit::<jansson_sys::json_error_t>::uninit();
            let result = jansson_sys::json_loads(input.as_ptr(), decoding_flags.bits(), error.as_mut_ptr());
            match Self::from_raw(result) {
                Some(val) => Ok(val),
                None => {
                    let ptr = &error.assume_init().text as *const _;
                    let len = libc::strlen(ptr);
                    let sli = slice::from_raw_parts(ptr as *mut u8, len);
                    Err(From::from(str::from_utf8(sli)?))
                }
            }
        }
    }

    /// Encodes this Jansson value as a JSON owned C-style string.
    pub fn to_libcstring(&self, encoding_flags: JanssonEncodingFlags) -> LibcString {
        unsafe {
            let json = jansson_sys::json_dumps(self.ptr, encoding_flags.bits());
            LibcString::from_chars(json).expect("Error writing JSON output from Jansson value :(")
        }
    }
}

impl fmt::Debug for JanssonValue {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "JanssonValue {{ {} }}", self.to_libcstring(JanssonEncodingFlags::empty()).to_string_lossy())
    }
}

impl Deref for JanssonValue {
    type Target = RawJanssonValue;

    fn deref(&self) -> &RawJanssonValue {
        unsafe { &*self.ptr }
    }
}

impl Clone for JanssonValue {
    fn clone(&self) -> Self {
        unsafe { jansson_sys::json_incref(self.ptr) };
        Self { ptr: self.ptr }
    }
}

impl Drop for JanssonValue {
    fn drop(&mut self) {
        unsafe { jansson_sys::json_decref(self.ptr) }
    }
}

unsafe impl Send for JanssonValue {}

#[cfg(test)]
mod tests {

    use super::*;

    #[test]
    fn round_trip() {
        let json = r#"{"a": "alpha", "b": true, "c": false, "d": 42, "e": 1.25, "f": null, "g": [1, 2, 3]}"#;
        let result = JanssonValue::from_str(json, JanssonDecodingFlags::empty()).unwrap();
        assert_eq!(json, result.to_libcstring(JanssonEncodingFlags::JSON_SORT_KEYS).to_str().unwrap());
    }

    #[test]
    fn produce_jansson_errors() {
        let json = r#"{"a":"#;
        let result = JanssonValue::from_str(json, JanssonDecodingFlags::empty());
        assert!(result.is_err());
    }
}