use std::{io, sync::Arc, thread, time::Duration};
use crossbeam_channel::{Receiver, RecvTimeoutError};
use crate::{Message, SharedState, ShutdownDropper};
#[derive(Clone, Debug)]
pub struct Subscription {
pub(crate) subject: String,
pub(crate) sid: usize,
pub(crate) recv: Receiver<Message>,
pub(crate) shared_state: Arc<SharedState>,
pub(crate) do_unsub: bool,
pub(crate) shutdown_dropper: Arc<ShutdownDropper>,
}
impl Subscription {
pub fn next(&self) -> Option<Message> {
self.recv.iter().next()
}
pub fn try_next(&self) -> Option<Message> {
self.recv.try_iter().next()
}
pub fn next_timeout(&self, timeout: Duration) -> Result<Message, RecvTimeoutError> {
self.recv.recv_timeout(timeout)
}
pub const fn messages(&self) -> Iter<'_> {
Iter { subscription: self }
}
pub const fn iter(&self) -> Iter<'_> {
Iter { subscription: self }
}
pub const fn try_iter(&self) -> TryIter<'_> {
TryIter { subscription: self }
}
pub const fn timeout_iter(&self, timeout: Duration) -> TimeoutIter<'_> {
TimeoutIter {
subscription: self,
to: timeout,
}
}
pub fn with_handler<F>(mut self, handler: F) -> Handler
where
F: Fn(Message) -> io::Result<()> + Sync + Send + 'static,
{
self.do_unsub = false;
let r = self.recv.clone();
thread::Builder::new()
.name(format!("nats_subscriber_{}_{}", self.sid, self.subject))
.spawn(move || {
for m in r.iter() {
if let Err(e) = handler(m) {
log::error!("Error in callback! {:?}", e);
}
}
})
.expect("threads should be spawnable");
Handler { sub: self }
}
fn unsub(&mut self) -> io::Result<()> {
self.shared_state.outbound.send_unsub(self.sid)?;
self.do_unsub = false;
self.shared_state.subs.write().remove(&self.sid);
Ok(())
}
pub fn unsubscribe(mut self) -> io::Result<()> {
self.unsub()
}
pub fn close(mut self) -> io::Result<()> {
self.unsub()
}
pub fn drain(&mut self) -> io::Result<()> {
self.shared_state.outbound.send_unsub(self.sid)?;
self.do_unsub = false;
let ret = self
.shared_state
.flush_timeout(crate::DEFAULT_FLUSH_TIMEOUT);
self.shared_state.subs.write().remove(&self.sid);
ret
}
}
impl Drop for Subscription {
fn drop(&mut self) {
if self.do_unsub {
if let Err(error) = self.unsub() {
log::error!("error unsubscribing during Subscription Drop: {:?}", error);
}
}
}
}
impl IntoIterator for Subscription {
type Item = Message;
type IntoIter = IntoIter;
fn into_iter(self) -> IntoIter {
IntoIter { subscription: self }
}
}
impl<'a> IntoIterator for &'a Subscription {
type Item = Message;
type IntoIter = Iter<'a>;
fn into_iter(self) -> Iter<'a> {
Iter { subscription: self }
}
}
pub struct Handler {
sub: Subscription,
}
impl Handler {
pub fn unsubscribe(mut self) -> io::Result<()> {
self.sub.unsub()
}
}
pub struct TryIter<'a> {
subscription: &'a Subscription,
}
impl<'a> Iterator for TryIter<'a> {
type Item = Message;
fn next(&mut self) -> Option<Self::Item> {
self.subscription.recv.try_recv().ok()
}
}
pub struct Iter<'a> {
subscription: &'a Subscription,
}
impl<'a> Iterator for Iter<'a> {
type Item = Message;
fn next(&mut self) -> Option<Self::Item> {
self.subscription.recv.recv().ok()
}
}
pub struct IntoIter {
subscription: Subscription,
}
impl Iterator for IntoIter {
type Item = Message;
fn next(&mut self) -> Option<Self::Item> {
self.subscription.recv.recv().ok()
}
}
pub struct TimeoutIter<'a> {
subscription: &'a Subscription,
to: Duration,
}
impl<'a> Iterator for TimeoutIter<'a> {
type Item = Message;
fn next(&mut self) -> Option<Self::Item> {
self.subscription.recv.recv_timeout(self.to).ok()
}
}