use super::vm_value::{to_value, Value};
use crate::error::{Result, Wrap};
use crate::ffi::{
unqlite_array_add_strkey_elem, unqlite_compile, unqlite_compile_file, unqlite_value,
unqlite_value_bool, unqlite_value_double, unqlite_value_int64, unqlite_value_null,
unqlite_value_string, unqlite_vm, unqlite_vm_config, unqlite_vm_dump, unqlite_vm_exec,
unqlite_vm_extract_variable, unqlite_vm_new_array, unqlite_vm_new_scalar, unqlite_vm_release,
unqlite_vm_release_value, unqlite_vm_reset,
};
use std::cell::RefCell;
use std::ffi::CString;
use std::os::raw::c_void;
use std::ptr::{null, null_mut, NonNull};
use std::rc::Rc;
use std::slice;
use std::sync::mpsc;
use crate::vars::{
UNQLITE_OK, UNQLITE_VM_CONFIG_ARGV_ENTRY, UNQLITE_VM_CONFIG_CREATE_VAR,
UNQLITE_VM_CONFIG_ENV_ATTR, UNQLITE_VM_CONFIG_ERR_REPORT, UNQLITE_VM_CONFIG_EXEC_VALUE,
UNQLITE_VM_CONFIG_EXTRACT_OUTPUT, UNQLITE_VM_CONFIG_IMPORT_PATH, UNQLITE_VM_CONFIG_OUTPUT,
UNQLITE_VM_CONFIG_RECURSION_DEPTH, UNQLITE_VM_OUTPUT_LENGTH,
};
use crate::UnQLite;
pub trait Jx9 {
fn compile<T: AsRef<[u8]>>(&self, jx9: T) -> Result<UnQLiteVm>;
fn compile_file<P: AsRef<str>>(&self, filename: P) -> Result<UnQLiteVm>;
}
impl Jx9 for UnQLite {
fn compile<T: AsRef<[u8]>>(&self, jx9: T) -> Result<UnQLiteVm> {
let mut vm: *mut unqlite_vm = null_mut();
let jx9 = jx9.as_ref();
wrap_raw!(self, compile, jx9.as_ptr() as _, jx9.len() as _, &mut vm)
.map(|_| UnQLiteVm::new(vm))
}
fn compile_file<P: AsRef<str>>(&self, filename: P) -> Result<UnQLiteVm> {
let mut vm: *mut unqlite_vm = null_mut();
let filename = CString::new(filename.as_ref())?;
wrap_raw!(self, compile_file, filename.as_ptr(), &mut vm).map(|_| UnQLiteVm::new(vm))
}
}
#[derive(Clone)]
pub struct UnQLiteVm {
native: NonNull<unqlite_vm>,
executed: bool,
output: Option<RefCell<Sender>>,
names: Vec<Rc<CString>>,
}
impl UnQLiteVm {
fn new(vm: *mut unqlite_vm) -> Self {
UnQLiteVm {
native: unsafe { NonNull::new_unchecked(vm) },
executed: false,
output: None,
names: Vec::new(),
}
}
pub fn exec(&mut self) -> Result<Option<Value>> {
self.exec_void()?;
let ptr: *mut unqlite_value = null_mut();
wrap_raw!(self, vm_config, UNQLITE_VM_CONFIG_EXEC_VALUE, &ptr)?;
Ok(unsafe { to_value(ptr) })
}
pub fn exec_void(&mut self) -> Result<()> {
if self.executed {
self.reset()?
}
wrap_raw!(self, vm_exec).map(|_| self.executed = true)
}
fn reset(&mut self) -> Result<()> {
wrap_raw!(self, vm_reset).map(|_| self.executed = false)
}
pub fn dump(&self) -> Result<()> {
wrap_raw!(
self,
vm_dump,
Some(callback_to_stdout),
null_mut::<c_void>()
)
}
pub fn output_to_channel(&mut self) -> Result<mpsc::Receiver<Vec<u8>>> {
let (sender, receiver) = mpsc::channel();
self.output = Some(RefCell::new(sender));
let sender_ptr = self.output.as_ref().unwrap().as_ptr();
wrap_raw!(
self,
vm_config,
UNQLITE_VM_CONFIG_OUTPUT,
callback_to_channel as OutputCallback,
sender_ptr
)
.map(|_| receiver)
}
pub fn output_to_stdout(&self) -> Result<()> {
wrap_raw!(
self,
vm_config,
UNQLITE_VM_CONFIG_OUTPUT,
callback_to_stdout as OutputCallback,
null_mut::<c_void>()
)
}
pub fn extract_output(&self) -> Result<&[u8]> {
let ptr: *const c_void = null();
let mut len: u32 = 0;
wrap_raw!(
self,
vm_config,
UNQLITE_VM_CONFIG_EXTRACT_OUTPUT,
&ptr,
&mut len
)
.map(|_| unsafe { slice::from_raw_parts(ptr as *const u8, len as usize) })
}
pub fn output_length(&self) -> Result<u32> {
let mut len: u32 = 0;
wrap_raw!(self, vm_config, UNQLITE_VM_OUTPUT_LENGTH, &mut len)?;
Ok(len)
}
pub fn import_path<T: AsRef<str>>(&self, path: T) -> Result<()> {
let path = CString::new(path.as_ref())?;
wrap_raw!(
self,
vm_config,
UNQLITE_VM_CONFIG_IMPORT_PATH,
path.as_ptr()
)
}
pub fn report_errors_to_output(&self) -> Result<()> {
wrap_raw!(self, vm_config, UNQLITE_VM_CONFIG_ERR_REPORT)
}
pub fn recursion_depth(&self, max_depth: i32) -> Result<()> {
wrap_raw!(
self,
vm_config,
UNQLITE_VM_CONFIG_RECURSION_DEPTH,
max_depth
)
}
pub fn add_argument<T: AsRef<[u8]>>(&self, arg: T) -> Result<()> {
let arg = CString::new(arg.as_ref())?;
wrap_raw!(self, vm_config, UNQLITE_VM_CONFIG_ARGV_ENTRY, arg.as_ptr())
}
pub fn add_env_attr<K, V>(&self, key: K, value: V) -> Result<()>
where
K: AsRef<[u8]>,
V: AsRef<[u8]>,
{
let key = CString::new(key.as_ref())?;
let value = value.as_ref();
wrap_raw!(
self,
vm_config,
UNQLITE_VM_CONFIG_ENV_ATTR,
key.as_ptr(),
value.as_ptr(),
value.len() as i32
)
}
fn new_variable(&self, value: Value) -> Result<RawValue> {
let raw: *mut unqlite_value = unsafe {
if value.is_scalar() {
unqlite_vm_new_scalar(self.as_raw_mut_ptr())
} else {
unqlite_vm_new_array(self.as_raw_mut_ptr())
}
};
match value {
Value::Null => wrap!(value_null, raw)?,
Value::Int(x) => wrap!(value_int64, raw, x)?,
Value::Bool(x) => wrap!(value_bool, raw, if x { 1 } else { 0 })?,
Value::Real(x) => wrap!(value_double, raw, x)?,
Value::String(x) => wrap!(
value_string,
raw,
CString::new(x.as_bytes()).unwrap().as_ptr(),
x.len() as i32
)?,
Value::Array(array) => {
for x in array {
let raw_elem = self.new_variable(x)?;
wrap!(array_add_strkey_elem, raw, null_mut(), raw_elem.as_ptr())?;
}
}
Value::Object(map) => {
for (key, value) in map {
let raw_key = CString::new(key.as_bytes()).unwrap();
let raw_value = self.new_variable(value)?;
wrap!(
array_add_strkey_elem,
raw,
raw_key.as_ptr(),
raw_value.as_ptr()
)?;
}
}
}
Ok(RawValue::new(unsafe { self.as_raw_mut_ptr() }, raw))
}
pub fn add_variable<T: Into<Vec<u8>>>(&mut self, name: T, value: Value) -> Result<()> {
let value = self.new_variable(value)?;
let name = Rc::new(CString::new(name).unwrap());
self.names.push(Rc::clone(&name));
wrap_raw!(
self,
vm_config,
UNQLITE_VM_CONFIG_CREATE_VAR,
name.as_ptr(),
value.as_ptr()
)
}
pub fn extract_variable<T: AsRef<[u8]>>(&self, name: T) -> Option<Value> {
let name = CString::new(name.as_ref()).unwrap();
unsafe {
to_value(unqlite_vm_extract_variable(
self.as_raw_mut_ptr(),
name.as_ptr(),
))
}
}
unsafe fn as_raw_mut_ptr(&self) -> *mut unqlite_vm {
self.native.as_ptr()
}
}
unsafe impl Send for UnQLiteVm {}
unsafe impl Sync for UnQLiteVm {}
impl Drop for UnQLiteVm {
fn drop(&mut self) {
unsafe {
unqlite_vm_release(self.as_raw_mut_ptr());
}
}
}
type Sender = mpsc::Sender<Vec<u8>>;
type OutputCallback = extern "C" fn(*const c_void, u32, *mut c_void) -> i32;
extern "C" fn callback_to_channel(data: *const c_void, len: u32, sender: *mut c_void) -> i32 {
let slice: &[u8] = unsafe { slice::from_raw_parts(data as *const u8, len as usize) };
let sender: &Sender = unsafe { &*(sender as *mut Sender) };
let mut msg = Vec::with_capacity(len as usize);
msg.extend_from_slice(slice);
match sender.send(msg) {
Ok(_) => UNQLITE_OK,
Err(_) => UNQLITE_OK,
}
}
extern "C" fn callback_to_stdout(data: *const c_void, len: u32, _: *mut c_void) -> i32 {
let slice = unsafe { slice::from_raw_parts(data as *const u8, len as usize) };
print!("{}", String::from_utf8_lossy(slice));
UNQLITE_OK
}
struct RawValue {
host: *mut unqlite_vm,
raw: *mut unqlite_value,
}
impl RawValue {
pub fn new(host: *mut unqlite_vm, raw: *mut unqlite_value) -> Self {
RawValue {
host: host,
raw: raw,
}
}
pub fn as_ptr(&self) -> *mut unqlite_value {
self.raw
}
}
impl Drop for RawValue {
fn drop(&mut self) {
unsafe {
unqlite_vm_release_value(self.host, self.raw);
}
}
}