use std::num::NonZeroUsize;
#[cfg(feature = "testing")]
use std::time::Duration;
use futures::Stream;
use google_cloud_pubsub::subscriber::{MessageStream, ShutdownToken};
use ruststream::{BatchSubscriber, BufferedSubscriber, Subscriber};
use tokio::runtime::Handle;
use tokio::sync::mpsc;
use crate::broker::Core;
use crate::error::{PubSubError, box_err};
#[cfg(feature = "testing")]
use crate::in_process::Consumer;
use crate::message::PubSubMessage;
use crate::runtime_slot::RuntimeSlot;
use crate::subscription::{DeliveryLimits, GooglePubSub};
const CHANNEL_CAPACITY: usize = 16;
pub struct PubSubSubscriber {
subscription: String,
buffer: BufferedSubscriber<Deliveries>,
}
impl std::fmt::Debug for PubSubSubscriber {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PubSubSubscriber")
.field("subscription", &self.subscription)
.finish_non_exhaustive()
}
}
impl PubSubSubscriber {
#[must_use]
pub fn subscription(&self) -> &str {
&self.subscription
}
pub(crate) fn open(core: &Core, descriptor: &GooglePubSub, runtime: &Handle) -> Self {
let name = core.subscription_name(descriptor.subscription());
let mut builder = core.subscriber.subscribe(name.clone());
if let Some(messages) = descriptor.max_outstanding_value() {
builder = builder.set_max_outstanding_messages(messages);
}
if let Some(extension) = descriptor.ack_extension_value() {
builder = builder.set_max_lease_extension(extension);
}
let limits = descriptor.limits();
builder = builder.set_max_lease(limits.max_lease);
let stream = {
let _connect_runtime = runtime.enter();
builder.build()
};
let shutdown = stream.shutdown_token();
let (tx, rx) = mpsc::channel(CHANNEL_CAPACITY);
runtime.spawn(pump(stream, tx, name.clone(), limits, core.runtime_slot()));
Self {
subscription: name,
buffer: BufferedSubscriber::new(Deliveries::Pull(Pull {
rx,
shutdown,
runtime: runtime.clone(),
}))
.max_wait(descriptor.batch_wait_value()),
}
}
#[cfg(feature = "testing")]
pub(crate) fn in_process(
subscription: String,
consumer: Consumer,
batch_wait: Duration,
) -> Self {
Self {
subscription,
buffer: BufferedSubscriber::new(Deliveries::InProcess(consumer)).max_wait(batch_wait),
}
}
}
impl Subscriber for PubSubSubscriber {
type Message = PubSubMessage;
type Error = PubSubError;
fn stream(&mut self) -> impl Stream<Item = Result<PubSubMessage, PubSubError>> + Send + '_ {
self.buffer.stream()
}
}
impl BatchSubscriber for PubSubSubscriber {
type Batch = Vec<PubSubMessage>;
fn batches(
&mut self,
size: NonZeroUsize,
) -> impl Stream<Item = Result<Self::Batch, PubSubError>> + Send + '_ {
self.buffer.batches(size)
}
}
enum Deliveries {
Pull(Pull),
#[cfg(feature = "testing")]
InProcess(Consumer),
}
#[cfg(not(feature = "testing"))]
const _: () = assert!(size_of::<Deliveries>() == size_of::<Pull>());
struct Pull {
rx: mpsc::Receiver<Result<PubSubMessage, PubSubError>>,
shutdown: ShutdownToken,
runtime: Handle,
}
impl Drop for Pull {
fn drop(&mut self) {
let token = self.shutdown.clone();
self.runtime.spawn(async move {
token.shutdown().await;
});
}
}
impl Subscriber for Deliveries {
type Message = PubSubMessage;
type Error = PubSubError;
fn stream(&mut self) -> impl Stream<Item = Result<PubSubMessage, PubSubError>> + Send + '_ {
futures::stream::poll_fn(move |cx| match self {
Self::Pull(pull) => pull.rx.poll_recv(cx),
#[cfg(feature = "testing")]
Self::InProcess(consumer) => consumer.poll_next(cx),
})
}
}
async fn pump(
mut stream: MessageStream,
out: mpsc::Sender<Result<PubSubMessage, PubSubError>>,
subscription: String,
limits: DeliveryLimits,
runtime: RuntimeSlot,
) {
while let Some(item) = stream.next().await {
match item {
Ok((message, handler)) => {
if out
.send(Ok(PubSubMessage::new(message, handler, limits, runtime)))
.await
.is_err()
{
break;
}
}
Err(err) => {
let _ = out
.send(Err(PubSubError::Receive {
subscription: subscription.clone(),
source: box_err(err),
}))
.await;
break;
}
}
}
}