use crate::error::{check, materialize, RayError, Result};
use crate::runtime::assert_on_runtime_thread;
use crate::value::Value;
use rayforce_sys as sys;
use std::borrow::Cow;
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.cast());
}
}
pub struct TcpClient {
handle: i64,
_not_send: PhantomData<*mut ()>,
}
impl TcpClient {
pub fn connect(host: &str, port: u16, user: &str, password: &str) -> Result<TcpClient> {
assert_on_runtime_thread("TcpClient::connect");
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 {
let reason: Cow<'static, str> = match handle {
sys::RAY_IPC_ERR_AUTH_REQUIRED => "server requires authentication".into(),
sys::RAY_IPC_ERR_AUTH_FAILED => "authentication failed".into(),
sys::RAY_IPC_ERR_WIRE_VERSION => "wire version mismatch".into(),
sys::RAY_IPC_ERR_TIMEOUT => "timed out".into(),
sys::RAY_IPC_ERR_OS => std::io::Error::last_os_error().to_string().into(),
_ => "connection refused".into(),
};
return Err(RayError::binding(format!(
"connect to {host}:{port} failed: {reason}"
)));
}
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) }
}
}