jsonnet/
value.rs

1use libc::c_int;
2use std::ffi::{CStr, CString};
3use std::marker;
4use std::mem;
5
6use super::JsonnetVm;
7use jsonnet_sys::{self, JsonnetJsonValue};
8
9/// Rust wrapper for borrowed libjsonnet JSON values.
10///
11/// See `JsonValue` for the owned version.
12pub struct JsonVal<'a> {
13    vm: &'a JsonnetVm,
14    value: *const JsonnetJsonValue,
15}
16
17impl<'a> JsonVal<'a> {
18    /// Construct a `JsonVal` from a pointer returned from a
19    /// low-level jsonnet C function.
20    ///
21    /// # Safety
22    ///
23    /// It is up to the caller to ensure that `p` was indeed allocated
24    /// by `vm`.
25    pub unsafe fn from_ptr(vm: &'a JsonnetVm, p: *const JsonnetJsonValue) -> Self {
26        JsonVal { vm: vm, value: p }
27    }
28
29    /// Returns the inner pointer to this jsonnet value.
30    ///
31    /// The returned pointer will be valid for as long as `self` is.
32    pub fn as_ptr(&self) -> *const JsonnetJsonValue {
33        self.value
34    }
35
36    /// Returns the value, if it is a string.
37    pub fn as_str(&self) -> Option<&str> {
38        unsafe {
39            let p = jsonnet_sys::jsonnet_json_extract_string(self.vm.as_ptr(), self.as_ptr());
40            if p.is_null() {
41                None
42            } else {
43                // Jsonnet asserts this is UTF-8
44                Some(CStr::from_ptr(p).to_str().unwrap())
45            }
46        }
47    }
48
49    /// Returns the value, if it is a number.
50    pub fn as_num(&self) -> Option<f64> {
51        let mut number = 0.0;
52        let ok = unsafe {
53            jsonnet_sys::jsonnet_json_extract_number(self.vm.as_ptr(), self.as_ptr(), &mut number)
54        };
55        if ok != 0 {
56            Some(number)
57        } else {
58            None
59        }
60    }
61
62    /// Returns the value, if it is a bool.
63    pub fn as_bool(&self) -> Option<bool> {
64        let v = unsafe { jsonnet_sys::jsonnet_json_extract_bool(self.vm.as_ptr(), self.as_ptr()) };
65        match v {
66            0 => Some(false),
67            1 => Some(true),
68            2 => None,
69            _ => unreachable!(),
70        }
71    }
72
73    /// Returns `Some(())` if the value is `null`.
74    pub fn as_null(&self) -> Option<()> {
75        let v = unsafe { jsonnet_sys::jsonnet_json_extract_null(self.vm.as_ptr(), self.as_ptr()) };
76        match v {
77            0 => None,
78            1 => Some(()),
79            _ => unreachable!(),
80        }
81    }
82}
83
84/// Rust wrapper for owned libjsonnet JSON values.
85///
86/// These are used as return values from jsonnet native callbacks.
87/// See `JsonVal` for the borrowed version.
88pub struct JsonValue<'a> {
89    vm: &'a JsonnetVm,
90    value: *mut JsonnetJsonValue,
91    _marker: marker::PhantomData<JsonnetJsonValue>,
92}
93
94impl<'a> JsonValue<'a> {
95    /// Construct a `JsonValue` from a pointer returned from a
96    /// low-level jsonnet C function.
97    ///
98    /// # Safety
99    ///
100    /// It is up to the caller to ensure that `p` was indeed allocated
101    /// by `vm`.
102    pub unsafe fn from_ptr(vm: &'a JsonnetVm, p: *mut JsonnetJsonValue) -> Self {
103        JsonValue {
104            vm: vm,
105            value: p,
106            _marker: marker::PhantomData,
107        }
108    }
109
110    /// Returns the inner pointer to this jsonnet value.
111    ///
112    /// The returned pointer will be valid for as long as `self` is.
113    pub fn as_ptr(&self) -> *const JsonnetJsonValue {
114        self.value
115    }
116
117    /// Returns the value, if it is a string.
118    pub fn as_str(&self) -> Option<&str> {
119        unsafe {
120            let p = jsonnet_sys::jsonnet_json_extract_string(self.vm.as_ptr(), self.as_ptr());
121            if p.is_null() {
122                None
123            } else {
124                // Jsonnet asserts this is UTF-8
125                Some(CStr::from_ptr(p).to_str().unwrap())
126            }
127        }
128    }
129
130    /// Returns the value, if it is a number.
131    pub fn as_num(&self) -> Option<f64> {
132        let mut number = 0.0;
133        let ok = unsafe {
134            jsonnet_sys::jsonnet_json_extract_number(self.vm.as_ptr(), self.as_ptr(), &mut number)
135        };
136        if ok != 0 {
137            Some(number)
138        } else {
139            None
140        }
141    }
142
143    /// Returns the value, if it is a bool.
144    pub fn as_bool(&self) -> Option<bool> {
145        let v = unsafe { jsonnet_sys::jsonnet_json_extract_bool(self.vm.as_ptr(), self.as_ptr()) };
146        match v {
147            0 => Some(false),
148            1 => Some(true),
149            2 => None,
150            _ => unreachable!(),
151        }
152    }
153
154    /// Returns `Some(())` if the value is `null`.
155    pub fn as_null(&self) -> Option<()> {
156        let v = unsafe { jsonnet_sys::jsonnet_json_extract_null(self.vm.as_ptr(), self.as_ptr()) };
157        match v {
158            0 => None,
159            1 => Some(()),
160            _ => unreachable!(),
161        }
162    }
163
164    /// Convert the given UTF8 string to a JsonValue.
165    ///
166    /// # Panics
167    ///
168    /// Panics if `v` contains an embedded nul character.
169    pub fn from_str(vm: &'a JsonnetVm, v: &str) -> Self {
170        let cstr = CString::new(v).unwrap();
171        unsafe {
172            let p = jsonnet_sys::jsonnet_json_make_string(vm.as_ptr(), cstr.as_ptr());
173            Self::from_ptr(vm, p)
174        }
175    }
176
177    /// Convert the given double to a JsonValue.
178    pub fn from_num(vm: &'a JsonnetVm, v: f64) -> Self {
179        unsafe {
180            let p = jsonnet_sys::jsonnet_json_make_number(vm.as_ptr(), v);
181            Self::from_ptr(vm, p)
182        }
183    }
184
185    /// Convert the given bool to a JsonValue.
186    pub fn from_bool(vm: &'a JsonnetVm, v: bool) -> Self {
187        unsafe {
188            let p = jsonnet_sys::jsonnet_json_make_bool(vm.as_ptr(), v as c_int);
189            Self::from_ptr(vm, p)
190        }
191    }
192
193    /// Make a JsonValue representing `null`.
194    pub fn null(vm: &'a JsonnetVm) -> Self {
195        unsafe {
196            let p = jsonnet_sys::jsonnet_json_make_null(vm.as_ptr());
197            Self::from_ptr(vm, p)
198        }
199    }
200
201    /// Convert the given list into a JsonValue array.
202    pub fn from_array<T>(vm: &'a JsonnetVm, iter: T) -> Self
203    where
204        T: IntoIterator<Item = JsonValue<'a>>,
205    {
206        unsafe {
207            let p = jsonnet_sys::jsonnet_json_make_array(vm.as_ptr());
208            for item in iter {
209                jsonnet_sys::jsonnet_json_array_append(vm.as_ptr(), p, item.into_raw());
210            }
211            Self::from_ptr(vm, p)
212        }
213    }
214
215    /// Convert the given map into a JsonValue object.
216    pub fn from_map<'b, T>(vm: &'a JsonnetVm, iter: T) -> Self
217    where
218        T: IntoIterator<Item = (&'b CStr, JsonValue<'a>)>,
219    {
220        unsafe {
221            let p = jsonnet_sys::jsonnet_json_make_object(vm.as_ptr());
222            for (f, v) in iter {
223                jsonnet_sys::jsonnet_json_object_append(vm.as_ptr(), p, f.as_ptr(), v.into_raw());
224            }
225            Self::from_ptr(vm, p)
226        }
227    }
228
229    /// Transfer ownership to a C caller (presumably a jsonnet
230    /// function).
231    ///
232    /// If you call this, it is up to you to ensure that the value is
233    /// freed correctly (using the appropriate jsonnet function), or
234    /// the memory will leak.
235    pub fn into_raw(self) -> *mut JsonnetJsonValue {
236        let result = self.value;
237        mem::forget(self);
238        result
239    }
240}
241
242impl<'a> Drop for JsonValue<'a> {
243    fn drop(&mut self) {
244        unsafe {
245            jsonnet_sys::jsonnet_json_destroy(self.vm.as_ptr(), self.value);
246        }
247    }
248}