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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
#![allow(non_camel_case_types)]
// See https://jansson.readthedocs.io/ for API documentation.

use std::os::raw::{c_char, c_int, c_longlong, c_void};

/// The type of a JSON value.
#[repr(u32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum json_type {
    JSON_OBJECT = 0,
    JSON_ARRAY = 1,
    JSON_STRING = 2,
    JSON_INTEGER = 3,
    JSON_REAL = 4,
    JSON_TRUE = 5,
    JSON_FALSE = 6,
    JSON_NULL = 7,
}

/// The maximum possible indentation when pretty-printing JSON.
pub const JSON_MAX_INDENT: u32 = 31;

/// A JSON value.
#[repr(C)]
#[derive(Debug)]
pub struct json_t {
    pub type_: json_type,
    pub refcount: usize,
}

/// An error that occurred during JSON processing.
#[repr(C)]
pub struct json_error_t {
    pub line: c_int,
    pub column: c_int,
    pub position: c_int,
    pub source: [c_char; 80usize],
    pub text: [c_char; 160usize],
}

pub type json_load_callback_t = unsafe extern "C" fn(buffer: *mut c_void, buflen: usize, data: *mut c_void) -> usize;
pub type json_dump_callback_t = unsafe extern "C" fn(buffer: *const c_char, size: usize, data: *mut c_void) -> c_int;
pub type json_malloc_t = unsafe extern "C" fn(arg1: usize) -> *mut c_void;
pub type json_free_t = unsafe extern "C" fn(arg1: *mut c_void);
pub type json_int_t = c_longlong;

extern "C" {
    pub fn json_object() -> *mut json_t;
    pub fn json_array() -> *mut json_t;
    pub fn json_string(value: *const c_char) -> *mut json_t;
    pub fn json_stringn(value: *const c_char, len: usize) -> *mut json_t;
    pub fn json_string_nocheck(value: *const c_char) -> *mut json_t;
    pub fn json_stringn_nocheck(value: *const c_char, len: usize) -> *mut json_t;
    pub fn json_integer(value: json_int_t) -> *mut json_t;
    pub fn json_real(value: f64) -> *mut json_t;
    pub fn json_true() -> *mut json_t;
    pub fn json_false() -> *mut json_t;
    pub fn json_null() -> *mut json_t;
    pub fn json_delete(json: *mut json_t);
    pub fn json_object_seed(seed: usize);
    pub fn json_object_size(object: *const json_t) -> usize;
    pub fn json_object_get(object: *const json_t, key: *const c_char) -> *mut json_t;
    pub fn json_object_set_new(object: *mut json_t, key: *const c_char, value: *mut json_t) -> c_int;
    pub fn json_object_set_new_nocheck(object: *mut json_t, key: *const c_char, value: *mut json_t) -> c_int;
    pub fn json_object_del(object: *mut json_t, key: *const c_char) -> c_int;
    pub fn json_object_clear(object: *mut json_t) -> c_int;
    pub fn json_object_update(object: *mut json_t, other: *mut json_t) -> c_int;
    pub fn json_object_update_existing(object: *mut json_t, other: *mut json_t) -> c_int;
    pub fn json_object_update_missing(object: *mut json_t, other: *mut json_t) -> c_int;
    pub fn json_object_iter(object: *mut json_t) -> *mut c_void;
    pub fn json_object_iter_at(object: *mut json_t, key: *const c_char) -> *mut c_void;
    pub fn json_object_key_to_iter(key: *const c_char) -> *mut c_void;
    pub fn json_object_iter_next(object: *mut json_t, iter: *mut c_void) -> *mut c_void;
    pub fn json_object_iter_key(iter: *mut c_void) -> *const c_char;
    pub fn json_object_iter_value(iter: *mut c_void) -> *mut json_t;
    pub fn json_object_iter_set_new(object: *mut json_t, iter: *mut c_void, value: *mut json_t) -> c_int;
    pub fn json_array_size(array: *const json_t) -> usize;
    pub fn json_array_get(array: *const json_t, index: usize) -> *mut json_t;
    pub fn json_array_set_new(array: *mut json_t, index: usize, value: *mut json_t) -> c_int;
    pub fn json_array_append_new(array: *mut json_t, value: *mut json_t) -> c_int;
    pub fn json_array_insert_new(array: *mut json_t, index: usize, value: *mut json_t) -> c_int;
    pub fn json_array_remove(array: *mut json_t, index: usize) -> c_int;
    pub fn json_array_clear(array: *mut json_t) -> c_int;
    pub fn json_array_extend(array: *mut json_t, other: *mut json_t) -> c_int;
    pub fn json_string_value(string: *const json_t) -> *const c_char;
    pub fn json_string_length(string: *const json_t) -> usize;
    pub fn json_integer_value(integer: *const json_t) -> json_int_t;
    pub fn json_real_value(real: *const json_t) -> f64;
    pub fn json_number_value(json: *const json_t) -> f64;
    pub fn json_string_set(string: *mut json_t, value: *const c_char) -> c_int;
    pub fn json_string_setn(string: *mut json_t, value: *const c_char, len: usize) -> c_int;
    pub fn json_string_set_nocheck(string: *mut json_t, value: *const c_char) -> c_int;
    pub fn json_string_setn_nocheck(string: *mut json_t, value: *const c_char, len: usize) -> c_int;
    pub fn json_integer_set(integer: *mut json_t, value: json_int_t) -> c_int;
    pub fn json_real_set(real: *mut json_t, value: f64) -> c_int;
    pub fn json_pack(fmt: *const c_char, ...) -> *mut json_t;
    pub fn json_pack_ex(error: *mut json_error_t, flags: usize, fmt: *const c_char, ...) -> *mut json_t;
    pub fn json_unpack(root: *mut json_t, fmt: *const c_char, ...) -> c_int;
    pub fn json_unpack_ex(root: *mut json_t, error: *mut json_error_t, flags: usize, fmt: *const c_char, ...) -> c_int;
    pub fn json_equal(value1: *mut json_t, value2: *mut json_t) -> c_int;
    pub fn json_copy(value: *mut json_t) -> *mut json_t;
    pub fn json_deep_copy(value: *const json_t) -> *mut json_t;
    pub fn json_loads(input: *const c_char, flags: usize, error: *mut json_error_t) -> *mut json_t;
    pub fn json_loadb(buffer: *const c_char, buflen: usize, flags: usize, error: *mut json_error_t) -> *mut json_t;
    pub fn json_loadfd(input: c_int, flags: usize, error: *mut json_error_t) -> *mut json_t;
    pub fn json_load_file(path: *const c_char, flags: usize, error: *mut json_error_t) -> *mut json_t;
    pub fn json_load_callback(callback: json_load_callback_t, data: *mut c_void, flags: usize, error: *mut json_error_t) -> *mut json_t;
    pub fn json_dumps(json: *const json_t, flags: usize) -> *mut c_char;
    pub fn json_dumpb(json: *const json_t, buffer: *mut c_char, size: usize, flags: usize) -> usize;
    pub fn json_dumpfd(json: *const json_t, output: c_int, flags: usize) -> c_int;
    pub fn json_dump_file(json: *const json_t, path: *const c_char, flags: usize) -> c_int;
    pub fn json_dump_callback(json: *const json_t, callback: json_dump_callback_t, data: *mut c_void, flags: usize) -> c_int;
    pub fn json_set_alloc_funcs(malloc_fn: json_malloc_t, free_fn: json_free_t);
    pub fn json_get_alloc_funcs(malloc_fn: *mut json_malloc_t, free_fn: *mut json_free_t);
}

pub unsafe fn json_incref(json: *mut json_t) -> *mut json_t {
    if !json.is_null() && (*json).refcount != usize::max_value() {
        (*json).refcount += 1;
    }
    json
}

pub unsafe fn json_decref(json: *mut json_t) {
    if !json.is_null() && (*json).refcount != usize::max_value() {
        (*json).refcount -= 1;
        if (*json).refcount == 0 {
            json_delete(json);
        }
    }
}

#[cfg(test)]
#[macro_use]
extern crate cstr_macro;

#[cfg(test)]
mod tests {

    use super::*;
    use std::ffi::CStr;
    use std::ptr;

    #[test]
    fn object_encoding() {
        unsafe {
            let x = json_object();
            json_object_set_new(x, cstr!("a"), json_string(cstr!("alpha")));
            json_object_set_new(x, cstr!("b"), json_true());
            json_object_set_new(x, cstr!("c"), json_false());
            json_object_set_new(x, cstr!("d"), json_integer(42));
            json_object_set_new(x, cstr!("e"), json_real(1.25));
            json_object_set_new(x, cstr!("f"), json_null());
            let ys = json_array();
            json_array_append_new(ys, json_integer(1));
            json_array_append_new(ys, json_integer(3));
            json_array_insert_new(ys, 1, json_integer(2));
            json_object_set_new(x, cstr!("g"), ys);
            let json = r#"{"a": "alpha", "b": true, "c": false, "d": 42, "e": 1.25, "f": null, "g": [1, 2, 3]}"#;
            assert_eq!(json, CStr::from_ptr(json_dumps(x, 0)).to_str().unwrap());
        }
    }

    #[test]
    fn object_decoding() {
        unsafe {
            let json = cstr!(r#"{"a": {"aa": [true, false], "ab": null}, "b": {}, "c": "charlie", "d": 8.75}"#);
            let root = json_loads(json, 0, ptr::null_mut());
            assert!((*root).type_ == json_type::JSON_OBJECT);
            let a = json_object_get(root, cstr!("a"));
            assert!((*a).type_ == json_type::JSON_OBJECT);
            let aa = json_object_get(a, cstr!("aa"));
            assert!((*aa).type_ == json_type::JSON_ARRAY);
            assert_eq!(json_array_size(aa), 2);
            assert!((*json_array_get(aa, 0)).type_ == json_type::JSON_TRUE);
            assert!((*json_array_get(aa, 1)).type_ == json_type::JSON_FALSE);
            let ab = json_object_get(a, cstr!("ab"));
            assert!((*ab).type_ == json_type::JSON_NULL);
            let b = json_object_get(root, cstr!("b"));
            assert!((*b).type_ == json_type::JSON_OBJECT);
            assert_eq!(json_object_size(b), 0);
            let c = json_object_get(root, cstr!("c"));
            assert!((*c).type_ == json_type::JSON_STRING);
            assert_eq!("charlie", CStr::from_ptr(json_string_value(c)).to_str().unwrap());
            let d = json_object_get(root, cstr!("d"));
            assert!((*d).type_ == json_type::JSON_REAL);
            assert_eq!(8.75, json_real_value(d));
        }
    }
}