use std::marker::PhantomData;
use crate::{codec::CodecType, error::{Error, Result}};
use super::consumer::{PullConsumerBuilder, PushConsumerBuilder};
#[derive(Clone)]
pub struct Stream<C: CodecType> {
inner: async_nats::jetstream::stream::Stream,
_codec: PhantomData<C>,
}
impl<C: CodecType> Stream<C> {
pub(crate) fn new(inner: async_nats::jetstream::stream::Stream) -> Self {
Self {
inner,
_codec: PhantomData,
}
}
pub fn inner(&self) -> &async_nats::jetstream::stream::Stream {
&self.inner
}
pub fn name(&self) -> &str {
&self.inner.cached_info().config.name
}
pub async fn info(&self) -> Result<StreamInfo> {
let mut stream = self.inner.clone();
let info = stream
.info()
.await
.map_err(|e| Error::JetStreamStream(e.to_string()))?;
Ok(StreamInfo {
config: StreamConfig::from_native(&info.config),
state: StreamState {
messages: info.state.messages,
bytes: info.state.bytes,
first_sequence: info.state.first_sequence,
last_sequence: info.state.last_sequence,
consumer_count: info.state.consumer_count,
},
})
}
pub fn pull_consumer_builder<T>(&self, name: &str) -> PullConsumerBuilder<T, C> {
PullConsumerBuilder::new(self.inner.clone(), name.to_string())
}
pub fn push_consumer_builder<T>(&self, name: &str) -> PushConsumerBuilder<T, C> {
PushConsumerBuilder::new(self.inner.clone(), name.to_string())
}
pub async fn get_pull_consumer<T>(
&self,
name: &str,
) -> Result<super::consumer::PullConsumer<T, C>> {
let inner: async_nats::jetstream::consumer::Consumer<
async_nats::jetstream::consumer::pull::Config,
> = self
.inner
.get_consumer(name)
.await
.map_err(|e| Error::JetStreamConsumer(e.to_string()))?;
Ok(super::consumer::PullConsumer::new(inner))
}
pub async fn get_push_consumer<T>(
&self,
name: &str,
) -> Result<super::consumer::PushConsumer<T, C>> {
let inner: async_nats::jetstream::consumer::Consumer<
async_nats::jetstream::consumer::push::Config,
> = self
.inner
.get_consumer(name)
.await
.map_err(|e| Error::JetStreamConsumer(e.to_string()))?;
Ok(super::consumer::PushConsumer::new(inner))
}
pub async fn delete_consumer(&self, name: &str) -> Result<()> {
self.inner
.delete_consumer(name)
.await
.map_err(|e| Error::JetStreamConsumer(e.to_string()))?;
Ok(())
}
pub async fn purge(&self) -> Result<u64> {
let response = self
.inner
.clone()
.purge()
.await
.map_err(|e| Error::JetStreamStream(e.to_string()))?;
Ok(response.purged)
}
pub async fn purge_subject(&self, filter: &str) -> Result<u64> {
let response = self
.inner
.clone()
.purge()
.filter(filter)
.await
.map_err(|e| Error::JetStreamStream(e.to_string()))?;
Ok(response.purged)
}
}
pub struct StreamBuilder<C: CodecType> {
context: async_nats::jetstream::Context,
config: async_nats::jetstream::stream::Config,
_codec: PhantomData<C>,
}
impl<C: CodecType> StreamBuilder<C> {
pub(crate) fn new(context: async_nats::jetstream::Context, name: String) -> Self {
Self {
context,
config: async_nats::jetstream::stream::Config {
name,
..Default::default()
},
_codec: PhantomData,
}
}
pub fn subjects(mut self, subjects: Vec<String>) -> Self {
self.config.subjects = subjects;
self
}
pub fn subject(mut self, subject: impl Into<String>) -> Self {
self.config.subjects.push(subject.into());
self
}
pub fn description(mut self, description: impl Into<String>) -> Self {
self.config.description = Some(description.into());
self
}
pub fn retention(mut self, retention: RetentionPolicy) -> Self {
self.config.retention = retention.into();
self
}
pub fn max_messages(mut self, max: i64) -> Self {
self.config.max_messages = max;
self
}
pub fn max_messages_per_subject(mut self, max: i64) -> Self {
self.config.max_messages_per_subject = max;
self
}
pub fn max_bytes(mut self, max: i64) -> Self {
self.config.max_bytes = max;
self
}
pub fn max_message_size(mut self, max: i32) -> Self {
self.config.max_message_size = max;
self
}
pub fn max_age(mut self, age: std::time::Duration) -> Self {
self.config.max_age = age;
self
}
pub fn max_consumers(mut self, max: i32) -> Self {
self.config.max_consumers = max;
self
}
pub fn replicas(mut self, replicas: usize) -> Self {
self.config.num_replicas = replicas;
self
}
pub fn storage(mut self, storage: StorageType) -> Self {
self.config.storage = storage.into();
self
}
pub fn discard_policy(mut self, policy: DiscardPolicy) -> Self {
self.config.discard = policy.into();
self
}
pub fn duplicate_window(mut self, window: std::time::Duration) -> Self {
self.config.duplicate_window = window;
self
}
pub fn allow_direct(mut self, allow: bool) -> Self {
self.config.allow_direct = allow;
self
}
pub fn mirror(mut self, source: StreamSource) -> Self {
self.config.mirror = Some(source.into());
self
}
pub fn add_source(mut self, source: StreamSource) -> Self {
self.config.sources.get_or_insert_with(Vec::new).push(source.into());
self
}
pub fn sealed(mut self, sealed: bool) -> Self {
self.config.sealed = sealed;
self
}
pub fn deny_delete(mut self, deny: bool) -> Self {
self.config.deny_delete = deny;
self
}
pub fn deny_purge(mut self, deny: bool) -> Self {
self.config.deny_purge = deny;
self
}
pub fn allow_rollup(mut self, allow: bool) -> Self {
self.config.allow_rollup = allow;
self
}
pub fn compression(mut self, compression: Compression) -> Self {
self.config.compression = Some(compression.into());
self
}
pub fn first_sequence(mut self, seq: u64) -> Self {
self.config.first_sequence = Some(seq);
self
}
pub fn subject_transform(mut self, source: &str, destination: &str) -> Self {
self.config.subject_transform = Some(async_nats::jetstream::stream::SubjectTransform {
source: source.to_string(),
destination: destination.to_string(),
});
self
}
pub async fn create(self) -> Result<Stream<C>> {
let inner = self
.context
.create_stream(self.config)
.await?;
Ok(Stream::new(inner))
}
pub async fn create_or_update(self) -> Result<Stream<C>> {
let inner = self
.context
.get_or_create_stream(self.config)
.await?;
Ok(Stream::new(inner))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum RetentionPolicy {
#[default]
Limits,
Interest,
WorkQueue,
}
impl From<RetentionPolicy> for async_nats::jetstream::stream::RetentionPolicy {
fn from(policy: RetentionPolicy) -> Self {
match policy {
RetentionPolicy::Limits => async_nats::jetstream::stream::RetentionPolicy::Limits,
RetentionPolicy::Interest => async_nats::jetstream::stream::RetentionPolicy::Interest,
RetentionPolicy::WorkQueue => async_nats::jetstream::stream::RetentionPolicy::WorkQueue,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum StorageType {
#[default]
File,
Memory,
}
impl From<StorageType> for async_nats::jetstream::stream::StorageType {
fn from(storage: StorageType) -> Self {
match storage {
StorageType::File => async_nats::jetstream::stream::StorageType::File,
StorageType::Memory => async_nats::jetstream::stream::StorageType::Memory,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum DiscardPolicy {
#[default]
Old,
New,
}
impl From<DiscardPolicy> for async_nats::jetstream::stream::DiscardPolicy {
fn from(policy: DiscardPolicy) -> Self {
match policy {
DiscardPolicy::Old => async_nats::jetstream::stream::DiscardPolicy::Old,
DiscardPolicy::New => async_nats::jetstream::stream::DiscardPolicy::New,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum Compression {
#[default]
None,
S2,
}
impl From<Compression> for async_nats::jetstream::stream::Compression {
fn from(compression: Compression) -> Self {
match compression {
Compression::None => async_nats::jetstream::stream::Compression::None,
Compression::S2 => async_nats::jetstream::stream::Compression::S2,
}
}
}
#[derive(Debug, Clone)]
pub struct StreamSource {
pub name: String,
pub start_seq: Option<u64>,
pub start_time: Option<time::OffsetDateTime>,
pub filter_subject: Option<String>,
}
impl StreamSource {
pub fn new(name: impl Into<String>) -> Self {
Self {
name: name.into(),
start_seq: None,
start_time: None,
filter_subject: None,
}
}
pub fn start_seq(mut self, seq: u64) -> Self {
self.start_seq = Some(seq);
self
}
pub fn start_time(mut self, time: time::OffsetDateTime) -> Self {
self.start_time = Some(time);
self
}
pub fn filter_subject(mut self, subject: impl Into<String>) -> Self {
self.filter_subject = Some(subject.into());
self
}
}
impl From<StreamSource> for async_nats::jetstream::stream::Source {
fn from(source: StreamSource) -> Self {
let mut s = async_nats::jetstream::stream::Source {
name: source.name,
..Default::default()
};
if let Some(seq) = source.start_seq {
s.start_sequence = Some(seq);
}
if let Some(time) = source.start_time {
s.start_time = Some(time);
}
if let Some(subject) = source.filter_subject {
s.filter_subject = Some(subject);
}
s
}
}
#[derive(Debug, Clone)]
pub struct StreamConfig {
pub name: String,
pub description: Option<String>,
pub subjects: Vec<String>,
pub retention: RetentionPolicy,
pub max_messages: i64,
pub max_bytes: i64,
pub max_age: std::time::Duration,
pub max_message_size: i32,
pub storage: StorageType,
pub replicas: usize,
}
impl StreamConfig {
pub(crate) fn from_native(config: &async_nats::jetstream::stream::Config) -> Self {
Self {
name: config.name.clone(),
description: config.description.clone(),
subjects: config.subjects.clone(),
retention: match config.retention {
async_nats::jetstream::stream::RetentionPolicy::Limits => RetentionPolicy::Limits,
async_nats::jetstream::stream::RetentionPolicy::Interest => {
RetentionPolicy::Interest
}
async_nats::jetstream::stream::RetentionPolicy::WorkQueue => {
RetentionPolicy::WorkQueue
}
},
max_messages: config.max_messages,
max_bytes: config.max_bytes,
max_age: config.max_age,
max_message_size: config.max_message_size,
storage: match config.storage {
async_nats::jetstream::stream::StorageType::File => StorageType::File,
async_nats::jetstream::stream::StorageType::Memory => StorageType::Memory,
},
replicas: config.num_replicas,
}
}
}
#[derive(Debug, Clone)]
pub struct StreamState {
pub messages: u64,
pub bytes: u64,
pub first_sequence: u64,
pub last_sequence: u64,
pub consumer_count: usize,
}
#[derive(Debug, Clone)]
pub struct StreamInfo {
pub config: StreamConfig,
pub state: StreamState,
}