use std::sync::Arc;
use std::time::Duration;
use claviron_broadcast::GroupLayer;
use claviron_broadcast::groups::GroupError;
use pyo3::exceptions::{PyKeyError, PyRuntimeError, PyValueError};
use pyo3::prelude::*;
use pyo3::types::PyBytes;
use crate::config::Channels;
pub(crate) const MODULE_SOURCE: &str = include_str!("channels.py");
pub(crate) type Ring = Arc<dyn Fn(u64) + Send + Sync>;
#[derive(Clone)]
pub(crate) struct ChannelLayer {
groups: GroupLayer,
capacity: Option<usize>,
expiry: Option<Duration>,
}
impl ChannelLayer {
pub(crate) fn new(
groups: GroupLayer,
settings: &Channels,
) -> Self {
Self { groups, capacity: settings.capacity, expiry: settings.expiry }
}
}
#[pyclass(name = "ChannelCore", module = "claviron", frozen)]
pub(crate) struct ChannelCore {
layer: ChannelLayer,
ring: Ring,
next_id: Arc<dyn Fn() -> u64 + Send + Sync>,
}
impl ChannelCore {
pub(crate) fn new(
layer: ChannelLayer,
ring: Ring,
next_id: Arc<dyn Fn() -> u64 + Send + Sync>,
) -> Self {
Self { layer, ring, next_id }
}
}
#[pymethods]
impl ChannelCore {
fn open(
&self,
prefix: &str,
) -> PyResult<String> {
self.layer
.groups
.open(prefix, self.layer.capacity, self.layer.expiry)
.map_err(error)
}
fn close(
&self,
channel: &str,
) {
self.layer.groups.close(channel);
}
fn send(
&self,
channel: &str,
message: &[u8],
) -> PyResult<bool> {
match self
.layer
.groups
.send(channel, bytes::Bytes::copy_from_slice(message))
{
Ok(()) => Ok(true),
Err(GroupError::Full) => Ok(false),
Err(other) => Err(error(other)),
}
}
fn receive<'py>(
&self,
py: Python<'py>,
channel: String,
) -> PyResult<Bound<'py, PyAny>> {
let id = (self.next_id)();
match take_or_wake(&self.layer.groups, &self.ring, &channel, id)? {
Some(message) => Ok(PyBytes::new(py, &message).into_any()),
None => Ok(Bound::new(
py,
ChannelWait {
id,
channel,
groups: self.layer.groups.clone(),
ring: self.ring.clone(),
},
)?
.into_any()),
}
}
fn group_add(
&self,
group: &str,
channel: &str,
) -> PyResult<()> {
self.layer.groups.add(group, channel).map_err(error)
}
fn group_discard(
&self,
group: &str,
channel: &str,
) {
self.layer.groups.discard(group, channel);
}
fn group_send(
&self,
group: &str,
message: &[u8],
) -> PyResult<usize> {
self.layer
.groups
.group_send(group, bytes::Bytes::copy_from_slice(message))
.map_err(error)
}
}
#[pyclass(name = "ChannelWait", module = "claviron", frozen)]
pub(crate) struct ChannelWait {
#[pyo3(get)]
id: u64,
channel: String,
groups: GroupLayer,
ring: Ring,
}
#[pymethods]
impl ChannelWait {
fn try_receive<'py>(
&self,
py: Python<'py>,
) -> PyResult<Option<Bound<'py, PyBytes>>> {
Ok(take_or_wake(&self.groups, &self.ring, &self.channel, self.id)?
.map(|message| PyBytes::new(py, &message)))
}
}
fn take_or_wake(
groups: &GroupLayer,
ring: &Ring,
channel: &str,
id: u64,
) -> PyResult<Option<bytes::Bytes>> {
let ring = Arc::clone(ring);
groups.receive_or_wake(channel, Box::new(move || ring(id))).map_err(error)
}
fn error(error: GroupError) -> PyErr {
match error {
GroupError::UnknownChannel => PyKeyError::new_err(error.to_string()),
GroupError::NotProcessSpecific => {
PyValueError::new_err(error.to_string())
}
_ => PyRuntimeError::new_err(error.to_string()),
}
}