use libc::{c_char, size_t};
use std::ffi::CStr;
use std::marker::PhantomData;
use std::ops::Deref;
use std::{fmt, mem, ptr};
use super::JsonnetVm;
use jsonnet_sys;
pub struct JsonnetString<'a> {
vm: &'a JsonnetVm,
data: *mut c_char,
}
impl<'a> JsonnetString<'a> {
pub fn new(vm: &'a JsonnetVm, v: &str) -> Self {
JsonnetString::from_bytes(vm, v.as_bytes())
}
pub fn from_bytes(vm: &'a JsonnetVm, v: &[u8]) -> Self {
unsafe {
let p = jsonnet_sys::jsonnet_realloc(vm.as_ptr(), ptr::null(), v.len() + 1);
ptr::copy_nonoverlapping(v.as_ptr(), p as *mut u8, v.len());
*(p.offset(v.len() as isize)) = 0; Self::from_ptr(vm, p)
}
}
pub unsafe fn from_ptr(vm: &'a JsonnetVm, p: *mut c_char) -> Self {
JsonnetString { vm: vm, data: p }
}
fn realloc(&mut self, size: size_t) {
unsafe {
self.data = jsonnet_sys::jsonnet_realloc(self.vm.as_ptr(), self.data, size);
}
}
pub fn as_str(&self) -> &'a str {
self.as_cstr().to_str().unwrap()
}
pub fn as_cstr(&self) -> &'a CStr {
unsafe { CStr::from_ptr(self.data) }
}
pub fn as_ptr(&self) -> *const c_char {
self.data
}
pub fn into_raw(self) -> *mut c_char {
let result = self.data;
mem::forget(self);
result
}
}
impl<'a> Deref for JsonnetString<'a> {
type Target = str;
fn deref(&self) -> &str {
self.as_str()
}
}
impl<'a> fmt::Debug for JsonnetString<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(self.as_str(), f)
}
}
impl<'a> fmt::Display for JsonnetString<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(self.as_str(), f)
}
}
impl<'a> Drop for JsonnetString<'a> {
fn drop(&mut self) {
self.realloc(0);
}
}
impl<'a> PartialEq for JsonnetString<'a> {
fn eq<'b>(&self, other: &JsonnetString<'b>) -> bool {
self.as_str() == other.as_str()
}
}
impl<'a> Eq for JsonnetString<'a> {}
#[test]
fn simple() {
let vm = JsonnetVm::new();
let s = JsonnetString::new(&vm, "1234");
assert_eq!(s.as_str(), "1234");
assert_eq!(s.as_cstr().to_bytes_with_nul(), b"1234\0");
}
#[derive(Debug)]
pub struct JsonnetStringIter<'a, 'b: 'a> {
cursor: *const c_char,
marker: PhantomData<&'a JsonnetString<'b>>,
}
impl<'a, 'b> JsonnetStringIter<'a, 'b> {
pub unsafe fn new(inner: &'a JsonnetString<'b>) -> Self {
let cursor = inner.as_ptr();
JsonnetStringIter {
cursor: cursor,
marker: PhantomData,
}
}
fn end(&self) -> bool {
let c = unsafe { *self.cursor };
c == 0
}
unsafe fn take_next(&mut self) -> &'a CStr {
let ret = CStr::from_ptr(self.cursor);
let len = ret.to_bytes_with_nul().len();
self.cursor = self.cursor.offset(len as isize);
ret
}
}
impl<'a, 'b> Iterator for JsonnetStringIter<'a, 'b> {
type Item = &'a str;
fn next(&mut self) -> Option<Self::Item> {
if self.end() {
None
} else {
let v = unsafe { self.take_next() };
Some(v.to_str().unwrap())
}
}
}