use pyo3::prelude::*;
use pyo3::types::{PyBytes, PyDict, PyList};
use std::time::Duration;
use super::json_utils::{json_value_to_py, py_to_json_value};
use crate::error::IpcError;
use crate::file_channel::{
FileChannel as RustFileChannel, FileMessage as RustFileMessage, MessageType as RustMessageType,
};
#[pyclass(name = "IpcChannel")]
pub struct PyIpcChannel {
inner: crate::channel::IpcChannel<Vec<u8>>,
}
#[pymethods]
impl PyIpcChannel {
#[staticmethod]
fn create(name: &str) -> PyResult<Self> {
let inner = crate::channel::IpcChannel::create(name)?;
Ok(Self { inner })
}
#[staticmethod]
fn connect(name: &str) -> PyResult<Self> {
let inner = crate::channel::IpcChannel::connect(name)?;
Ok(Self { inner })
}
#[getter]
fn name(&self) -> &str {
self.inner.name()
}
#[getter]
fn is_server(&self) -> bool {
self.inner.is_server()
}
fn wait_for_client(&mut self, py: Python<'_>) -> PyResult<()> {
py.detach(|| self.inner.wait_for_client())?;
Ok(())
}
fn send(&mut self, py: Python<'_>, data: Vec<u8>) -> PyResult<()> {
py.detach(|| self.inner.send_bytes(&data))?;
Ok(())
}
fn recv(&mut self, py: Python<'_>) -> PyResult<Py<PyBytes>> {
let data = py.detach(|| self.inner.recv_bytes())?;
Ok(PyBytes::new(py, &data).into())
}
fn send_json(&mut self, py: Python<'_>, obj: &Bound<'_, PyAny>) -> PyResult<()> {
let value = py_to_json_value(obj)?;
let json_bytes = serde_json::to_vec(&value)
.map_err(|e| PyErr::new::<pyo3::exceptions::PyValueError, _>(e.to_string()))?;
py.detach(|| self.inner.send_bytes(&json_bytes))?;
Ok(())
}
fn recv_json(&mut self, py: Python<'_>) -> PyResult<Py<PyAny>> {
let data = py.detach(|| self.inner.recv_bytes())?;
let value: serde_json::Value =
serde_json::from_slice(&data).map_err(|e| IpcError::deserialization(e.to_string()))?;
json_value_to_py(py, &value)
}
}
#[pyclass(name = "FileChannel")]
pub struct PyFileChannel {
inner: RustFileChannel,
}
#[pymethods]
impl PyFileChannel {
#[staticmethod]
fn backend(dir: &str) -> PyResult<Self> {
let inner = RustFileChannel::backend(dir)?;
Ok(Self { inner })
}
#[staticmethod]
fn frontend(dir: &str) -> PyResult<Self> {
let inner = RustFileChannel::frontend(dir)?;
Ok(Self { inner })
}
#[getter]
fn dir(&self) -> String {
self.inner.dir().to_string_lossy().to_string()
}
fn send_request(&self, method: &str, params: &Bound<'_, PyAny>) -> PyResult<String> {
let json_value = py_to_json_value(params)?;
let id = self.inner.send_request(method, json_value)?;
Ok(id)
}
fn send_response(&self, request_id: &str, result: &Bound<'_, PyAny>) -> PyResult<()> {
let json_value = py_to_json_value(result)?;
self.inner.send_response(request_id, json_value)?;
Ok(())
}
fn send_error(&self, request_id: &str, error: &str) -> PyResult<()> {
self.inner.send_error(request_id, error)?;
Ok(())
}
fn send_event(&self, name: &str, payload: &Bound<'_, PyAny>) -> PyResult<()> {
let json_value = py_to_json_value(payload)?;
self.inner.send_event(name, json_value)?;
Ok(())
}
fn recv(&mut self, py: Python<'_>) -> PyResult<Py<PyAny>> {
let messages = self.inner.recv()?;
let list = PyList::empty(py);
for msg in messages {
list.append(file_message_to_py(py, msg)?)?;
}
Ok(list.into())
}
fn recv_one(&mut self, py: Python<'_>) -> PyResult<Py<PyAny>> {
match self.inner.recv_one()? {
Some(msg) => file_message_to_py(py, msg),
None => Ok(py.None()),
}
}
fn wait_response(
&mut self,
py: Python<'_>,
request_id: &str,
timeout_ms: u64,
) -> PyResult<Py<PyAny>> {
let timeout = Duration::from_millis(timeout_ms);
let msg = self.inner.wait_response(request_id, timeout)?;
file_message_to_py(py, msg)
}
fn clear(&self) -> PyResult<()> {
self.inner.clear()?;
Ok(())
}
}
fn file_message_to_py(py: Python<'_>, msg: RustFileMessage) -> PyResult<Py<PyAny>> {
let dict = PyDict::new(py);
dict.set_item("id", &msg.id)?;
dict.set_item("timestamp", msg.timestamp)?;
let msg_type = match msg.msg_type {
RustMessageType::Request => "request",
RustMessageType::Response => "response",
RustMessageType::Event => "event",
};
dict.set_item("type", msg_type)?;
if let Some(method) = msg.method {
dict.set_item("method", method)?;
}
if let Some(reply_to) = msg.reply_to {
dict.set_item("reply_to", reply_to)?;
}
if let Some(error) = msg.error {
dict.set_item("error", error)?;
}
let payload = json_value_to_py(py, &msg.payload)?;
dict.set_item("payload", payload)?;
Ok(dict.into())
}