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;
pub type RawJanssonValue = jansson_sys::json_t;
bitflags! {
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! {
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;
}
}
pub struct JanssonValue {
ptr: *mut RawJanssonValue,
}
impl JanssonValue {
pub unsafe fn from_and_incref(ptr: *mut RawJanssonValue) -> Option<Self> {
jansson_sys::json_incref(ptr);
Self::from_raw(ptr)
}
pub unsafe fn from_raw(ptr: *mut RawJanssonValue) -> Option<Self> {
ptr.as_mut().map(|p| Self { ptr: p })
}
pub fn into_raw(self) -> *mut RawJanssonValue {
unsafe { jansson_sys::json_incref(self.ptr) };
self.ptr
}
pub fn as_mut_ref(&mut self) -> &mut RawJanssonValue {
unsafe { self.ptr.as_mut().unwrap() }
}
pub fn from_str(input: &str, decoding_flags: JanssonDecodingFlags) -> Result<Self, Box<dyn Error + Send + Sync>> {
Self::from_cstr(&CString::new(input)?, decoding_flags)
}
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)?))
}
}
}
}
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());
}
}