use core::future::Future;
use bytes::Bytes;
use futures::{Stream, StreamExt};
use crate::codec::{Decode, OwningCodec};
use crate::envelope::PersistedEnvelope;
use crate::step::Step;
use crate::stream_id::StreamKey;
use mnesis::Version;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Decoded<T> {
pub event: T,
pub version: Version,
pub metadata: Option<Bytes>,
}
#[derive(Debug, thiserror::Error)]
pub enum DecodeStreamError<R, D> {
#[error("subscription stream read failed")]
Read(#[source] R),
#[error("event decode failed")]
Decode(#[source] D),
}
#[derive(Debug, thiserror::Error)]
pub enum FoldDecodedError<R, D, H> {
#[error("subscription stream read failed")]
Read(#[source] R),
#[error("event decode failed")]
Decode(#[source] D),
#[error("decoded-event handler failed")]
Handler(#[source] H),
}
mod sealed {
pub trait Sealed {}
}
pub trait RawItem: sealed::Sealed {
type Typed<T>;
fn envelope(&self) -> &PersistedEnvelope;
fn retag<T>(&self, decoded: Decoded<T>) -> Self::Typed<T>;
}
impl sealed::Sealed for PersistedEnvelope {}
impl RawItem for PersistedEnvelope {
type Typed<T> = Decoded<T>;
fn envelope(&self) -> &PersistedEnvelope {
self
}
fn retag<T>(&self, decoded: Decoded<T>) -> Decoded<T> {
decoded
}
}
impl<P: Copy> sealed::Sealed for (P, StreamKey, PersistedEnvelope) {}
impl<P: Copy> RawItem for (P, StreamKey, PersistedEnvelope) {
type Typed<T> = (P, StreamKey, Decoded<T>);
fn envelope(&self) -> &PersistedEnvelope {
&self.2
}
fn retag<T>(&self, decoded: Decoded<T>) -> (P, StreamKey, Decoded<T>) {
(self.0, self.1.clone(), decoded)
}
}
pub trait DecodedStreamExt<I, R>: Stream<Item = Result<I, R>> + Sized
where
I: RawItem,
{
fn decoded<E, C>(
self,
codec: C,
) -> impl Stream<Item = Result<I::Typed<E>, DecodeStreamError<R, C::Error>>> + Send
where
C: OwningCodec<E>,
E: Send + 'static,
I: Send + 'static,
R: Send + 'static,
Self: Send,
{
self.map(move |res| {
let item = res.map_err(DecodeStreamError::Read)?;
let event: E = codec
.decode(item.envelope())
.map_err(DecodeStreamError::Decode)?;
let env = item.envelope();
let decoded = Decoded {
event,
version: env.version(),
metadata: env.metadata_bytes(),
};
Ok(item.retag(decoded))
})
}
fn for_each_decoded<E, C, F, H>(
self,
codec: C,
mut f: F,
) -> impl Future<Output = Result<(), FoldDecodedError<R, C::Error, H>>>
where
E: ?Sized,
C: Decode<E>,
F: for<'a> FnMut(Decoded<<C as Decode<E>>::Output<'a>>) -> Result<(), H>,
{
async move {
let stream = self;
futures::pin_mut!(stream);
while let Some(res) = stream.next().await {
let item = res.map_err(FoldDecodedError::Read)?;
fold_one(&codec, &mut f, item.envelope())?;
}
Ok(())
}
}
}
fn fold_one<R, E, C, F, H>(
codec: &C,
f: &mut F,
env: &PersistedEnvelope,
) -> Result<(), FoldDecodedError<R, C::Error, H>>
where
E: ?Sized,
C: Decode<E>,
F: for<'a> FnMut(Decoded<<C as Decode<E>>::Output<'a>>) -> Result<(), H>,
{
let window = codec.decode(env).map_err(FoldDecodedError::Decode)?;
let decoded = Decoded {
event: window,
version: env.version(),
metadata: env.metadata_bytes(),
};
f(decoded).map_err(FoldDecodedError::Handler)
}
impl<St, I, R> DecodedStreamExt<I, R> for St
where
St: Stream<Item = Result<I, R>>,
I: RawItem,
{
}
pub trait StepStreamExt<I, R>: Stream<Item = Result<Step<I>, R>> + Sized
where
I: RawItem,
{
fn events(self) -> impl Stream<Item = Result<I, R>> + Send
where
Self: Send,
I: Send,
R: Send,
{
self.filter_map(|res| async move {
match res {
Ok(Step::Event(item)) => Some(Ok(item)),
Ok(Step::CaughtUp) => None,
Err(e) => Some(Err(e)),
}
})
}
#[allow(
clippy::type_complexity,
reason = "the Step<Decoded>/DecodeStreamError item is intrinsic to the contract; an \
alias would hide the `impl Stream` the API depends on"
)]
fn decoded<E, C>(
self,
codec: C,
) -> impl Stream<Item = Result<Step<I::Typed<E>>, DecodeStreamError<R, C::Error>>> + Send
where
C: OwningCodec<E>,
E: Send + 'static,
I: Send + 'static,
R: Send + 'static,
Self: Send,
{
self.map(move |res| {
let step = res.map_err(DecodeStreamError::Read)?;
match step {
Step::CaughtUp => Ok(Step::CaughtUp),
Step::Event(item) => {
let env = item.envelope();
let event: E = codec.decode(env).map_err(DecodeStreamError::Decode)?;
let decoded = Decoded {
event,
version: env.version(),
metadata: env.metadata_bytes(),
};
Ok(Step::Event(item.retag(decoded)))
}
}
})
}
}
impl<St, I, R> StepStreamExt<I, R> for St
where
St: Stream<Item = Result<Step<I>, R>>,
I: RawItem,
{
}