jsonnet/
string.rs

1use libc::{c_char, size_t};
2use std::ffi::CStr;
3use std::marker::PhantomData;
4use std::ops::Deref;
5use std::{fmt, mem, ptr};
6
7use super::JsonnetVm;
8use jsonnet_sys;
9
10/// Rust wrapper for libjsonnet string values.
11///
12/// This string is allocated against a particular JsonnetVm, and may
13/// not outlive the JsonentVm.  It is UTF-8, and may not contain
14/// embedded nul characters (ie: it is javascript and C -safe).
15pub struct JsonnetString<'a> {
16    vm: &'a JsonnetVm,
17    data: *mut c_char,
18}
19
20impl<'a> JsonnetString<'a> {
21    /// Allocate a new JsonnetString and copy `v` into it.
22    ///
23    /// # Panics
24    ///
25    /// Panics if `v` contains an embedded nul character.
26    pub fn new(vm: &'a JsonnetVm, v: &str) -> Self {
27        JsonnetString::from_bytes(vm, v.as_bytes())
28    }
29
30    /// Allocate a new JsonnetString and copy `v` into it.
31    ///
32    /// # Panics
33    ///
34    /// Panics if `v` contains an embedded nul character.  In most
35    /// cases, jsonnet requires strings to be utf8, so prefer
36    /// `JsonnetString::new` over this function.
37    pub fn from_bytes(vm: &'a JsonnetVm, v: &[u8]) -> Self {
38        unsafe {
39            let p = jsonnet_sys::jsonnet_realloc(vm.as_ptr(), ptr::null(), v.len() + 1);
40            ptr::copy_nonoverlapping(v.as_ptr(), p as *mut u8, v.len());
41            *(p.offset(v.len() as isize)) = 0; // trailing nul for C string
42            Self::from_ptr(vm, p)
43        }
44    }
45
46    /// Construct a `JsonnetString` from a pointer returned from a
47    /// low-level jsonnet C function.
48    ///
49    /// # Safety
50    ///
51    /// It is assumed that `p` points to a UTF-8 C string (ie: nul
52    /// terminated).  It is up to the caller to ensure that `p` was
53    /// indeed allocated by `vm`.
54    pub unsafe fn from_ptr(vm: &'a JsonnetVm, p: *mut c_char) -> Self {
55        JsonnetString { vm: vm, data: p }
56    }
57
58    fn realloc(&mut self, size: size_t) {
59        unsafe {
60            self.data = jsonnet_sys::jsonnet_realloc(self.vm.as_ptr(), self.data, size);
61        }
62    }
63
64    /// Returns the contents as a `&str`.
65    pub fn as_str(&self) -> &'a str {
66        self.as_cstr().to_str().unwrap()
67    }
68
69    /// Returns the contents as a `&CStr`.
70    pub fn as_cstr(&self) -> &'a CStr {
71        unsafe { CStr::from_ptr(self.data) }
72    }
73
74    /// Returns the inner pointer to this jsonnet string.
75    ///
76    /// The returned pointer will be valid for as long as `self` is.
77    pub fn as_ptr(&self) -> *const c_char {
78        self.data
79    }
80
81    /// Transfer ownership to a C caller (presumably a jsonnet
82    /// function).
83    ///
84    /// If you call this, it is up to you to ensure that the string is
85    /// freed correctly (using the appropriate jsonnet function), or
86    /// the memory will leak.
87    pub fn into_raw(self) -> *mut c_char {
88        let result = self.data;
89        mem::forget(self);
90        result
91    }
92}
93
94impl<'a> Deref for JsonnetString<'a> {
95    type Target = str;
96    fn deref(&self) -> &str {
97        self.as_str()
98    }
99}
100
101impl<'a> fmt::Debug for JsonnetString<'a> {
102    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
103        fmt::Debug::fmt(self.as_str(), f)
104    }
105}
106
107impl<'a> fmt::Display for JsonnetString<'a> {
108    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
109        fmt::Display::fmt(self.as_str(), f)
110    }
111}
112
113impl<'a> Drop for JsonnetString<'a> {
114    fn drop(&mut self) {
115        self.realloc(0);
116    }
117}
118
119impl<'a> PartialEq for JsonnetString<'a> {
120    fn eq<'b>(&self, other: &JsonnetString<'b>) -> bool {
121        self.as_str() == other.as_str()
122    }
123}
124
125impl<'a> Eq for JsonnetString<'a> {}
126
127#[test]
128fn simple() {
129    let vm = JsonnetVm::new();
130    let s = JsonnetString::new(&vm, "1234");
131    assert_eq!(s.as_str(), "1234");
132    assert_eq!(s.as_cstr().to_bytes_with_nul(), b"1234\0");
133}
134
135/// An iterator over nul-separated multi-valued jsonnet string.
136#[derive(Debug)]
137pub struct JsonnetStringIter<'a, 'b: 'a> {
138    cursor: *const c_char,
139    marker: PhantomData<&'a JsonnetString<'b>>,
140}
141
142impl<'a, 'b> JsonnetStringIter<'a, 'b> {
143    /// Caller takes responsibility for ensuring this actually is a
144    /// multi-valued string.
145    pub unsafe fn new(inner: &'a JsonnetString<'b>) -> Self {
146        let cursor = inner.as_ptr();
147        JsonnetStringIter {
148            cursor: cursor,
149            marker: PhantomData,
150        }
151    }
152
153    fn end(&self) -> bool {
154        let c = unsafe { *self.cursor };
155        c == 0
156    }
157
158    // Caller checks that cursor is actually pointing at another string.
159    unsafe fn take_next(&mut self) -> &'a CStr {
160        let ret = CStr::from_ptr(self.cursor);
161        let len = ret.to_bytes_with_nul().len();
162        self.cursor = self.cursor.offset(len as isize);
163        ret
164    }
165}
166
167impl<'a, 'b> Iterator for JsonnetStringIter<'a, 'b> {
168    type Item = &'a str;
169    fn next(&mut self) -> Option<Self::Item> {
170        if self.end() {
171            None
172        } else {
173            let v = unsafe { self.take_next() };
174            Some(v.to_str().unwrap())
175        }
176    }
177}