use crate::error::{check, materialize, RayError, Result};
use crate::value::Value;
use rayforce_sys as sys;
use std::ffi::CString;
use std::marker::PhantomData;
unsafe fn ensure_poll() {
if sys::ray_runtime_get_poll().is_null() {
let p = sys::ray_poll_create();
sys::ray_runtime_set_poll(p);
}
}
pub struct TcpClient {
handle: i64,
_not_send: PhantomData<*mut ()>,
}
impl TcpClient {
pub fn connect(host: &str, port: u16, user: &str, password: &str) -> Result<TcpClient> {
let host_c = CString::new(host).map_err(|_| RayError::binding("host contains NUL"))?;
let user_c = CString::new(user).map_err(|_| RayError::binding("user contains NUL"))?;
let pass_c =
CString::new(password).map_err(|_| RayError::binding("password contains NUL"))?;
unsafe {
ensure_poll();
let handle =
sys::ray_ipc_connect(host_c.as_ptr(), port, user_c.as_ptr(), pass_c.as_ptr(), 0);
if handle < 0 {
return Err(RayError::binding(format!(
"connect to {host}:{port} failed"
)));
}
Ok(TcpClient {
handle,
_not_send: PhantomData,
})
}
}
pub fn execute(&self, query: &str) -> Result<Value> {
self.send(&Value::string(query))
}
pub fn send(&self, msg: &Value) -> Result<Value> {
unsafe {
let r = sys::ray_ipc_send(self.handle, msg.as_ptr());
if r.is_null() {
return Err(RayError::binding("ipc send failed"));
}
Ok(Value::from_owned(materialize(check(r)?)?))
}
}
pub fn send_async(&self, msg: &Value) -> Result<()> {
unsafe {
let e = sys::ray_ipc_send_async(self.handle, msg.as_ptr());
if e != sys::ray_err_t_RAY_OK {
return Err(RayError::binding(format!(
"ipc send_async failed (err {e})"
)));
}
}
Ok(())
}
pub fn close(self) {
drop(self);
}
}
impl Drop for TcpClient {
fn drop(&mut self) {
unsafe { sys::ray_ipc_close(self.handle) }
}
}