use std::any::type_name;
use std::marker::PhantomData;
use serde::de::DeserializeOwned;
use crate::codec::{Codec, CodecError};
use crate::{Carries, IncomingMessage, Subscriber, SubscriptionSource};
#[cfg(feature = "testing")]
use super::context::Context;
pub(crate) const REPORTED: &str = "the broker reports the delivery does not decode";
#[diagnostic::on_unimplemented(
message = "`{Self}` is not a handler input kind",
note = "the input type selects its kind: `Decoded<T>` for a `serde` type, `Provided<F>` for \
a `Deserialized` one"
)]
pub trait InputKind: Send + Sync + 'static {
const DESERIALIZED: bool = false;
type Owned: Send + Sync;
type Target: ?Sized + Sync;
fn input_label() -> &'static str;
}
#[diagnostic::on_unimplemented(
message = "`{Self}` cannot be decoded with the codec `{DecodeCodec}`",
note = "a typed input needs `serde::de::DeserializeOwned`; a `Deserialized` input decodes \
with any codec (it never calls one)",
note = "a batch of values a broker's batches carry mounts in the plain batch form only, \
with no reply and no `Out` slots"
)]
pub trait DecodeWith<DecodeCodec>: InputKind {
const CONTENT_TYPE: Option<&'static str> = None;
fn decode<M: IncomingMessage>(codec: &DecodeCodec, msg: &M) -> Result<Self::Owned, CodecError>;
}
pub trait Materialize<Decoder, M>: InputKind {
const CONTENT_TYPE: Option<&'static str> = None;
const FAILURE: &'static str = "codec decode failed";
type Held<'m>: Send + Sync
where
M: 'm;
fn materialize<'m>(decoder: &Decoder, msg: &'m M) -> Result<Self::Held<'m>, CodecError>;
fn view<'a>(held: &'a Self::Held<'_>, msg: &'a M) -> &'a Self::Target;
#[cfg(feature = "testing")]
fn record<C, S>(_held: &Self::Held<'_>, _ctx: &mut Context<'_, C, S>) {}
}
pub trait DecodeSite<Codec, Src, Conn> {
type Decoder: Clone + Send + Sync + 'static;
fn decoder(codec: Codec) -> Self::Decoder;
}
pub struct Decoded<T>(PhantomData<T>);
impl<T> std::fmt::Debug for Decoded<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Decoded").finish_non_exhaustive()
}
}
impl<T: Send + Sync + 'static> InputKind for Decoded<T> {
type Owned = T;
type Target = T;
fn input_label() -> &'static str {
type_name::<T>()
}
}
impl<DecodeCodec: Codec, T: DeserializeOwned + Send + Sync + 'static> DecodeWith<DecodeCodec>
for Decoded<T>
{
const CONTENT_TYPE: Option<&'static str> = Some(DecodeCodec::CONTENT_TYPE);
fn decode<M: IncomingMessage>(codec: &DecodeCodec, msg: &M) -> Result<T, CodecError> {
codec.decode(msg.payload())
}
}
impl<DecodeCodec, T, M> Materialize<DecodeCodec, M> for Decoded<T>
where
DecodeCodec: Codec,
T: DeserializeOwned + Send + Sync + 'static,
M: IncomingMessage,
{
const CONTENT_TYPE: Option<&'static str> = Some(DecodeCodec::CONTENT_TYPE);
type Held<'m>
= T
where
M: 'm;
fn materialize(codec: &DecodeCodec, msg: &M) -> Result<T, CodecError> {
codec.decode(msg.payload())
}
fn view<'a>(held: &'a T, _msg: &'a M) -> &'a T {
held
}
}
impl<T, Codec, Src, Conn> DecodeSite<Codec, Src, Conn> for Decoded<T>
where
Codec: Clone + Send + Sync + 'static,
{
type Decoder = Codec;
fn decoder(codec: Codec) -> Codec {
codec
}
}
pub struct Provided<F>(PhantomData<F>);
impl<F> std::fmt::Debug for Provided<F> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Provided").finish_non_exhaustive()
}
}
impl<F: Send + Sync + 'static> InputKind for Provided<F> {
const DESERIALIZED: bool = true;
type Owned = ();
type Target = [u8];
fn input_label() -> &'static str {
type_name::<F>()
}
}
impl<DecodeCodec, F: Send + Sync + 'static> DecodeWith<DecodeCodec> for Provided<F> {
fn decode<M: IncomingMessage>(_codec: &DecodeCodec, _msg: &M) -> Result<(), CodecError> {
Ok(())
}
}
impl<DecodeCodec, F, M> Materialize<DecodeCodec, M> for Provided<F>
where
F: Send + Sync + 'static,
M: IncomingMessage,
{
type Held<'m>
= ()
where
M: 'm;
fn materialize(_codec: &DecodeCodec, _msg: &M) -> Result<(), CodecError> {
Ok(())
}
fn view<'a>(_held: &'a (), msg: &'a M) -> &'a [u8] {
msg.payload()
}
}
impl<F, Codec, Src, Conn> DecodeSite<Codec, Src, Conn> for Provided<F>
where
Codec: Clone + Send + Sync + 'static,
{
type Decoder = Codec;
fn decoder(codec: Codec) -> Codec {
codec
}
}
pub struct DecodedPair<H, P>(PhantomData<(H, P)>);
impl<H, P> std::fmt::Debug for DecodedPair<H, P> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("DecodedPair").finish_non_exhaustive()
}
}
impl<H, P> InputKind for DecodedPair<H, P>
where
H: Send + Sync + 'static,
P: Send + Sync + 'static,
{
type Owned = crate::runtime::Message<H, P>;
type Target = crate::runtime::Message<H, P>;
fn input_label() -> &'static str {
type_name::<P>()
}
}
fn decode_pair<DecodeCodec, H, P, M>(
codec: &DecodeCodec,
msg: &M,
) -> Result<crate::runtime::Message<H, P>, CodecError>
where
DecodeCodec: Codec,
H: DeserializeOwned,
P: DeserializeOwned,
M: IncomingMessage,
{
let contract: H = msg
.headers()
.to_typed()
.map_err(|err| CodecError::Decode(Box::from(err.to_string())))?;
let body: P = codec.decode(msg.payload())?;
Ok(crate::runtime::Message::new(contract, body))
}
impl<DecodeCodec: Codec, H, P> DecodeWith<DecodeCodec> for DecodedPair<H, P>
where
H: DeserializeOwned + Send + Sync + 'static,
P: DeserializeOwned + Send + Sync + 'static,
{
const CONTENT_TYPE: Option<&'static str> = Some(DecodeCodec::CONTENT_TYPE);
fn decode<M: IncomingMessage>(codec: &DecodeCodec, msg: &M) -> Result<Self::Owned, CodecError> {
decode_pair(codec, msg)
}
}
impl<DecodeCodec, H, P, M> Materialize<DecodeCodec, M> for DecodedPair<H, P>
where
DecodeCodec: Codec,
H: DeserializeOwned + Send + Sync + 'static,
P: DeserializeOwned + Send + Sync + 'static,
M: IncomingMessage,
{
const CONTENT_TYPE: Option<&'static str> = Some(DecodeCodec::CONTENT_TYPE);
type Held<'m>
= crate::runtime::Message<H, P>
where
M: 'm;
fn materialize(
codec: &DecodeCodec,
msg: &M,
) -> Result<crate::runtime::Message<H, P>, CodecError> {
decode_pair(codec, msg)
}
fn view<'a>(
held: &'a crate::runtime::Message<H, P>,
_msg: &'a M,
) -> &'a crate::runtime::Message<H, P> {
held
}
}
impl<H, P, Codec, Src, Conn> DecodeSite<Codec, Src, Conn> for DecodedPair<H, P>
where
Codec: Clone + Send + Sync + 'static,
{
type Decoder = Codec;
fn decoder(codec: Codec) -> Codec {
codec
}
}
pub struct Carried<T>(PhantomData<T>);
impl<T> std::fmt::Debug for Carried<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Carried").finish_non_exhaustive()
}
}
impl<T: Clone + Send + Sync + 'static> InputKind for Carried<T> {
type Owned = T;
type Target = T;
fn input_label() -> &'static str {
type_name::<T>()
}
}
pub struct Lend<Src, Conn>(PhantomData<fn() -> (Src, Conn)>);
impl<Src, Conn> Lend<Src, Conn> {
const fn new() -> Self {
Self(PhantomData)
}
}
impl<Src, Conn> Clone for Lend<Src, Conn> {
fn clone(&self) -> Self {
*self
}
}
impl<Src, Conn> Copy for Lend<Src, Conn> {}
impl<Src, Conn> std::fmt::Debug for Lend<Src, Conn> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Lend").finish_non_exhaustive()
}
}
impl<T, Codec, Src, Conn> DecodeSite<Codec, Src, Conn> for Carried<T>
where
Src: 'static,
Conn: 'static,
{
type Decoder = Lend<Src, Conn>;
fn decoder(_codec: Codec) -> Lend<Src, Conn> {
Lend::new()
}
}
#[diagnostic::on_unimplemented(
message = "the subscription `{Self}` does not carry `{T}`",
label = "a `&{T}` handler reads the value this subscription's deliveries hold",
note = "mount it on a subscription whose deliveries implement `Carries<{T}>`; a `{T}` that \
arrives as bytes derives `Deserialize` instead and rides the codec"
)]
pub trait SourceCarries<T, Conn> {
type Delivery;
fn lend(delivery: &Self::Delivery) -> Option<&T>;
}
#[diagnostic::do_not_recommend]
impl<T, Conn, Src> SourceCarries<T, Conn> for Src
where
Conn: crate::ConnectedBroker,
Src: SubscriptionSource<Conn>,
<Src::Subscriber as Subscriber>::Message: Carries<T>,
{
type Delivery = <Src::Subscriber as Subscriber>::Message;
fn lend(delivery: &Self::Delivery) -> Option<&T> {
delivery.carried()
}
}
impl<T, Src, Conn, M> Materialize<Lend<Src, Conn>, M> for Carried<T>
where
T: Clone + Send + Sync + 'static,
Src: SourceCarries<T, Conn, Delivery = M>,
M: IncomingMessage,
{
const FAILURE: &'static str = "the delivery carries no value";
type Held<'m>
= &'m T
where
M: 'm;
fn materialize<'m>(_decoder: &Lend<Src, Conn>, msg: &'m M) -> Result<&'m T, CodecError> {
Src::lend(msg).ok_or_else(|| {
CodecError::Decode(Box::from(format!(
"the delivery carries no `{}`",
type_name::<T>()
)))
})
}
fn view<'a>(held: &'a &T, _msg: &'a M) -> &'a T {
held
}
#[cfg(feature = "testing")]
fn record<C, S>(held: &&T, ctx: &mut Context<'_, C, S>) {
ctx.record_carried(*held);
}
}