use std::any::type_name;
use std::borrow::Cow;
use std::fmt;
use std::marker::PhantomData;
use std::time::Duration;
#[cfg(feature = "asyncapi")]
use ruststream::asyncapi::{Binding, Bindings};
use ruststream::{BrokerMoves, DeclareRetryError, RetryDeclaration, SubscriptionSource};
#[cfg(feature = "asyncapi")]
use ruststream_sqlx_dialect::Role;
use ruststream_sqlx_dialect::{Dialect, Opens};
#[cfg(feature = "asyncapi")]
use serde::Serialize;
use super::broker::ConnectedSqlxBroker;
use super::database::QueueDatabase;
use super::engine::Events;
use super::error::SqlxBrokerError;
#[cfg(feature = "asyncapi")]
use super::publish::table_of;
use super::subscriber::InboxSubscriber;
use super::threads::InboxThreads;
use super::time::LeaseRow;
use super::transactional::{InboxMode, Plain, Transactional};
use super::{FormOn, InboxRow};
mod build;
mod check;
mod description;
mod open;
pub(crate) use description::{Description, Timing, refused_declaration};
pub use open::Queue;
pub(crate) use open::{Registration, open};
pub struct InboxQueue<Row, Mode = Plain> {
name: Cow<'static, str>,
poll_interval: Option<Duration>,
lease: Option<Duration>,
declaration: RetryDeclaration,
threads: Option<InboxThreads>,
_row: PhantomData<fn() -> (Row, Mode)>,
}
impl<Row> InboxQueue<Row> {
#[must_use]
pub fn new(name: impl Into<Cow<'static, str>>) -> Self {
Self {
name: name.into(),
poll_interval: None,
lease: None,
declaration: RetryDeclaration::new(),
threads: None,
_row: PhantomData,
}
}
#[must_use]
pub const fn poll_interval(mut self, interval: Duration) -> Self {
self.poll_interval = Some(interval);
self
}
#[must_use]
pub const fn lease(mut self, lease: Duration) -> Self
where
Row: LeaseRow,
{
self.lease = Some(lease);
self
}
pub(crate) fn into_transactional(self) -> InboxQueue<Row, Transactional> {
InboxQueue {
name: self.name,
poll_interval: self.poll_interval,
lease: self.lease,
declaration: self.declaration,
threads: self.threads,
_row: PhantomData,
}
}
}
impl<Row, Mode> InboxQueue<Row, Mode> {
pub(crate) const fn on_threads(mut self, threads: InboxThreads) -> Self {
self.threads = Some(threads);
self
}
}
impl<Row, Mode> Clone for InboxQueue<Row, Mode> {
fn clone(&self) -> Self {
Self {
name: self.name.clone(),
poll_interval: self.poll_interval,
lease: self.lease,
declaration: self.declaration.clone(),
threads: self.threads,
_row: PhantomData,
}
}
}
impl<Row, Mode: InboxMode> fmt::Debug for InboxQueue<Row, Mode> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("InboxQueue")
.field("row", &type_name::<Row>())
.field("transactional", &Mode::TRANSACTIONAL)
.field("name", &self.name)
.field("poll_interval", &self.poll_interval)
.field("lease", &self.lease)
.field("declaration", &self.declaration)
.field("threads", &self.threads)
.finish()
}
}
impl<DB, D, Row, Mode> SubscriptionSource<ConnectedSqlxBroker<DB, D>> for InboxQueue<Row, Mode>
where
DB: QueueDatabase,
D: Dialect + Opens<Row::Opening> + 'static,
Row: InboxRow + Events<DB>,
Row::Form: FormOn<D>,
Mode: InboxMode,
{
type Subscriber = InboxSubscriber<DB, Row, Mode>;
type Copies = BrokerMoves;
fn name(&self) -> &str {
&self.name
}
async fn subscribe(
self,
connected: &ConnectedSqlxBroker<DB, D>,
) -> Result<Self::Subscriber, SqlxBrokerError> {
open::<DB, Row, Mode>(
&connected.shared,
&<Row::Form as FormOn<D>>::erase(&connected.dialect),
&self.name,
Timing {
poll_interval: self.poll_interval,
lease: self.lease,
threads: self.threads,
},
&self.declaration,
&Description::of::<DB, Row>(),
)
.await
}
fn declare_retry(mut self, declaration: &RetryDeclaration) -> Self {
self.declaration = declaration.clone();
self
}
#[cfg(feature = "asyncapi")]
fn channel_bindings(&self) -> Bindings {
let group = Row::SPEC.column(Role::Group).map(|_| self.name.as_ref());
let table = TableBinding {
table: table_of(&Row::SPEC),
group,
};
Binding::extension("x-sqlx", &table)
.map_or_else(|_| Bindings::new(), |binding| Bindings::new().with(binding))
}
fn declare_retry_on(
&self,
_connected: &ConnectedSqlxBroker<DB, D>,
declaration: &RetryDeclaration,
) -> Result<(), DeclareRetryError> {
refused_declaration(&self.name, declaration, &Description::of::<DB, Row>())
.map_or(Ok(()), |error| {
Err(DeclareRetryError::Broker(Box::new(error)))
})
}
}
#[cfg(feature = "asyncapi")]
#[derive(Serialize)]
struct TableBinding<'a> {
table: String,
#[serde(skip_serializing_if = "Option::is_none")]
group: Option<&'a str>,
}