use crate::error::{check, materialize, RayError, Result};
use crate::value::Value;
use rayforce_sys as sys;
use std::ffi::CString;
use std::marker::PhantomData;
use std::sync::atomic::{AtomicBool, Ordering};
static LIVE: AtomicBool = AtomicBool::new(false);
pub struct Runtime {
rt: *mut sys::ray_runtime_s,
_not_send: PhantomData<*mut ()>,
}
impl Runtime {
pub fn new() -> Result<Runtime> {
if LIVE
.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
.is_err()
{
return Err(RayError::binding(
"a rayforce runtime is already live in this process",
));
}
let rt = unsafe { sys::ray_runtime_create(0, std::ptr::null_mut()) };
if rt.is_null() {
LIVE.store(false, Ordering::SeqCst);
return Err(RayError::binding("ray_runtime_create failed"));
}
Ok(Runtime {
rt,
_not_send: PhantomData,
})
}
pub fn eval(&self, source: &str) -> Result<Value> {
eval(source)
}
pub fn set_global(&self, name: &str, value: &Value) -> Result<()> {
set_global(name, value)
}
pub fn get_global(&self, name: &str) -> Result<Value> {
get_global(name)
}
}
pub fn set_global(name: &str, value: &Value) -> Result<()> {
debug_assert!(is_live(), "set_global called without a live Runtime");
unsafe {
let id = sys::ray_sym_intern(name.as_ptr() as *const _, name.len());
let e = sys::ray_env_set(id, value.as_ptr());
if e != sys::ray_err_t_RAY_OK {
return Err(RayError::binding(format!(
"set_global({name}) failed (err {e})"
)));
}
}
Ok(())
}
pub fn get_global(name: &str) -> Result<Value> {
debug_assert!(is_live(), "get_global called without a live Runtime");
unsafe {
let id = sys::ray_sym_intern(name.as_ptr() as *const _, name.len());
let v = sys::ray_env_get(id);
if v.is_null() {
return Err(RayError::binding(format!("global not found: {name}")));
}
if crate::raw::is_err(v) {
return Err(RayError::from_obj(v));
}
Ok(Value::from_borrowed(v))
}
}
impl Drop for Runtime {
fn drop(&mut self) {
unsafe { sys::ray_runtime_destroy(self.rt) };
LIVE.store(false, Ordering::SeqCst);
}
}
pub fn is_live() -> bool {
LIVE.load(Ordering::SeqCst)
}
pub fn eval(source: &str) -> Result<Value> {
debug_assert!(is_live(), "eval called without a live Runtime");
let c = CString::new(source).map_err(|_| RayError::binding("source contains a NUL byte"))?;
unsafe {
let r = sys::ray_eval_str(c.as_ptr());
if r.is_null() {
return Ok(Value::null());
}
Ok(Value::from_owned(materialize(check(r)?)?))
}
}
pub fn eval_value(obj: &Value) -> Result<Value> {
debug_assert!(is_live(), "eval_value called without a live Runtime");
unsafe {
let r = sys::ray_eval(obj.as_ptr());
if r.is_null() {
return Ok(Value::null());
}
Ok(Value::from_owned(materialize(check(r)?)?))
}
}