use crate::codec::{Codec, CodecName};
use crate::erasure::payload::{
PayloadErased, PayloadErasedWrapper, PayloadRawErased, PayloadRawErasedWrapper,
};
use crate::reactant::{ErrorReactant, Reactant, ReactantError, ReactantRaw};
use std::any::{Any, TypeId};
use std::future::Future;
use std::marker::PhantomData;
use std::pin::Pin;
use std::sync::Arc;
pub trait ReactantErased: Send + Sync + 'static {
fn react_erased(
&self,
payload: Arc<dyn PayloadErased + Send + Sync + 'static>,
) -> Pin<Box<dyn Future<Output = Result<(), ReactantError>> + Send + 'static>>;
fn payload_type_id(&self) -> TypeId;
fn codec_type_id(&self) -> TypeId;
fn clone_to_box(&self) -> Box<dyn ReactantErased + Send + Sync + 'static>;
fn clone_to_arc(&self) -> Arc<dyn ReactantErased + Send + Sync + 'static>;
fn clone_to_any(&self) -> Arc<dyn Any + Send + Sync + 'static>;
fn as_any(&self) -> &dyn Any;
}
pub trait ReactantRawErased: Send + Sync + 'static {
fn react_erased(
&self,
payload: Arc<dyn PayloadRawErased + Send + Sync + 'static>,
) -> Pin<Box<dyn Future<Output = Result<(), ReactantError>> + Send + 'static>>;
fn payload_type_id(&self) -> TypeId;
fn codec_type_id(&self) -> TypeId;
fn clone_to_box(&self) -> Box<dyn ReactantRawErased + Send + Sync + 'static>;
fn clone_to_arc(&self) -> Arc<dyn ReactantRawErased + Send + Sync + 'static>;
fn clone_to_any(&self) -> Arc<dyn Any + Send + Sync + 'static>;
fn as_any(&self) -> &dyn Any;
}
pub trait ErrorReactantErased: Send + Sync + 'static {
fn react_error_erased(
&self,
error: Arc<ReactantError>,
payload: Arc<dyn PayloadErased + Send + Sync + 'static>,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'static>>;
fn payload_type_id(&self) -> TypeId;
fn codec_type_id(&self) -> TypeId;
fn clone_to_box(&self) -> Box<dyn ErrorReactantErased + Send + Sync + 'static>;
fn clone_to_arc(&self) -> Arc<dyn ErrorReactantErased + Send + Sync + 'static>;
fn clone_to_any(&self) -> Arc<dyn Any + Send + Sync + 'static>;
fn as_any(&self) -> &dyn Any;
}
pub struct ErrorReactantErasedWrapper<T, C, R> {
reactant: Box<R>,
_phantom: PhantomData<(T, C)>,
}
impl<T, C, R> ErrorReactantErasedWrapper<T, C, R>
where
T: Send + Sync + 'static,
C: Codec<T> + CodecName + Send + Sync + 'static,
R: ErrorReactant<T, C> + Send + Sync + Clone + 'static,
{
pub fn new(reactant: Box<R>) -> Self {
Self {
reactant,
_phantom: PhantomData,
}
}
pub fn from_typed_reactant(
reactant: Box<R>,
) -> Arc<dyn ErrorReactantErased + Send + Sync + 'static> {
Arc::new(Self::new(reactant))
}
}
impl<T, C, R> ErrorReactantErased for ErrorReactantErasedWrapper<T, C, R>
where
T: Send + Sync + 'static,
C: Codec<T> + CodecName + Send + Sync + 'static,
R: ErrorReactant<T, C> + Send + Sync + Clone + 'static,
{
fn react_error_erased(
&self,
error: Arc<ReactantError>,
payload: Arc<dyn PayloadErased + Send + Sync + 'static>,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'static>> {
if payload.payload_type_id() == TypeId::of::<T>()
&& payload.codec_type_id() == TypeId::of::<C>()
{
if let Some(wrapper) = payload
.as_any()
.downcast_ref::<PayloadErasedWrapper<T, C>>()
{
let typed_payload = wrapper.get_typed_payload();
self.reactant.react_error(error, typed_payload)
} else {
Box::pin(async move {})
}
} else {
Box::pin(async move {})
}
}
fn payload_type_id(&self) -> TypeId {
TypeId::of::<T>()
}
fn codec_type_id(&self) -> TypeId {
TypeId::of::<C>()
}
fn clone_to_box(&self) -> Box<dyn ErrorReactantErased + Send + Sync + 'static> {
Box::new(ErrorReactantErasedWrapper::new(Box::new(
(*self.reactant).clone(),
)))
}
fn clone_to_arc(&self) -> Arc<dyn ErrorReactantErased + Send + Sync + 'static> {
Arc::new(ErrorReactantErasedWrapper::new(Box::new(
(*self.reactant).clone(),
)))
}
fn clone_to_any(&self) -> Arc<dyn Any + Send + Sync + 'static> {
let cloned_reactant = (*self.reactant).clone();
let typed_arc: Arc<dyn ErrorReactant<T, C> + Send + Sync + 'static> =
Arc::new(cloned_reactant);
Arc::new(typed_arc)
}
fn as_any(&self) -> &dyn Any {
self
}
}
pub struct ReactantErasedWrapper<T, C, R> {
reactant: Box<R>,
_phantom: PhantomData<(T, C)>,
}
impl<T, C, R> ReactantErasedWrapper<T, C, R>
where
T: Send + Sync + 'static,
C: Codec<T> + CodecName + Send + Sync + 'static,
R: Reactant<T, C> + Send + Sync + Clone + 'static,
{
pub fn new(reactant: Box<R>) -> Self {
Self {
reactant,
_phantom: PhantomData,
}
}
pub fn from_typed_reactant(
reactant: Box<R>,
) -> Arc<dyn ReactantErased + Send + Sync + 'static> {
Arc::new(Self::new(reactant))
}
pub fn get_typed_reactant(&self) -> &R {
&self.reactant
}
}
impl<T, C, R> ReactantErased for ReactantErasedWrapper<T, C, R>
where
T: Send + Sync + 'static,
C: Codec<T> + CodecName + Send + Sync + 'static,
R: Reactant<T, C> + Send + Sync + Clone + 'static,
{
fn react_erased(
&self,
payload: Arc<dyn PayloadErased + Send + Sync + 'static>,
) -> Pin<Box<dyn Future<Output = Result<(), ReactantError>> + Send + 'static>> {
if payload.payload_type_id() == TypeId::of::<T>()
&& payload.codec_type_id() == TypeId::of::<C>()
{
if let Some(wrapper) = payload
.as_any()
.downcast_ref::<PayloadErasedWrapper<T, C>>()
{
let typed_payload = wrapper.get_typed_payload();
self.reactant.react(typed_payload)
} else {
Box::pin(async move { Ok(()) })
}
} else {
Box::pin(async move {
Ok(())
})
}
}
fn payload_type_id(&self) -> TypeId {
TypeId::of::<T>()
}
fn codec_type_id(&self) -> TypeId {
TypeId::of::<C>()
}
fn clone_to_box(&self) -> Box<dyn ReactantErased + Send + Sync + 'static> {
Box::new(ReactantErasedWrapper::new(Box::new(
(*self.reactant).clone(),
)))
}
fn clone_to_arc(&self) -> Arc<dyn ReactantErased + Send + Sync + 'static> {
Arc::new(ReactantErasedWrapper::new(Box::new(
(*self.reactant).clone(),
)))
}
fn clone_to_any(&self) -> Arc<dyn Any + Send + Sync + 'static> {
let cloned_reactant = (*self.reactant).clone();
let typed_arc: Arc<dyn Reactant<T, C> + Send + Sync + 'static> = Arc::new(cloned_reactant);
Arc::new(typed_arc)
}
fn as_any(&self) -> &dyn Any {
self
}
}
pub struct ReactantRawErasedWrapper<T, C, R> {
reactant: Box<R>,
_phantom: PhantomData<(T, C)>,
}
impl<T, C, R> ReactantRawErasedWrapper<T, C, R>
where
T: Send + Sync + 'static,
C: Codec<T> + CodecName + Send + Sync + 'static,
R: ReactantRaw<T, C> + Send + Sync + Clone + 'static,
{
pub fn new(reactant: Box<R>) -> Self {
Self {
reactant,
_phantom: PhantomData,
}
}
pub fn from_typed_reactant(
reactant: Box<R>,
) -> Arc<dyn ReactantRawErased + Send + Sync + 'static> {
Arc::new(Self::new(reactant))
}
pub fn get_typed_reactant(&self) -> &R {
&self.reactant
}
}
impl<T, C, R> ReactantRawErased for ReactantRawErasedWrapper<T, C, R>
where
T: Send + Sync + 'static,
C: Codec<T> + CodecName + Send + Sync + 'static,
R: ReactantRaw<T, C> + Send + Sync + Clone + 'static,
{
fn react_erased(
&self,
payload: Arc<dyn PayloadRawErased + Send + Sync + 'static>,
) -> Pin<Box<dyn Future<Output = Result<(), ReactantError>> + Send + 'static>> {
if payload.payload_type_id() == TypeId::of::<T>()
&& payload.codec_type_id() == TypeId::of::<C>()
{
if let Some(wrapper) = payload
.as_any()
.downcast_ref::<PayloadRawErasedWrapper<T, C>>()
{
let typed_payload = wrapper.to_typed_payload();
self.reactant.react(typed_payload)
} else {
Box::pin(async move { Ok(()) })
}
} else {
Box::pin(async move {
Ok(())
})
}
}
fn payload_type_id(&self) -> TypeId {
TypeId::of::<T>()
}
fn codec_type_id(&self) -> TypeId {
TypeId::of::<C>()
}
fn clone_to_box(&self) -> Box<dyn ReactantRawErased + Send + Sync + 'static> {
Box::new(ReactantRawErasedWrapper::new(Box::new(
(*self.reactant).clone(),
)))
}
fn clone_to_arc(&self) -> Arc<dyn ReactantRawErased + Send + Sync + 'static> {
Arc::new(ReactantRawErasedWrapper::new(Box::new(
(*self.reactant).clone(),
)))
}
fn clone_to_any(&self) -> Arc<dyn Any + Send + Sync + 'static> {
let cloned_reactant = (*self.reactant).clone();
let typed_arc: Arc<dyn ReactantRaw<T, C> + Send + Sync + 'static> =
Arc::new(cloned_reactant);
Arc::new(typed_arc)
}
fn as_any(&self) -> &dyn Any {
self
}
}
pub fn erase_reactant<T, C, R>(reactant: Box<R>) -> Arc<dyn ReactantErased + Send + Sync + 'static>
where
T: Send + Sync + 'static,
C: Codec<T> + CodecName + Send + Sync + 'static,
R: Reactant<T, C> + Send + Sync + Clone + 'static,
{
ReactantErasedWrapper::from_typed_reactant(reactant)
}
pub fn erase_reactant_raw<T, C, R>(
reactant: Box<R>,
) -> Arc<dyn ReactantRawErased + Send + Sync + 'static>
where
T: Send + Sync + 'static,
C: Codec<T> + CodecName + Send + Sync + 'static,
R: ReactantRaw<T, C> + Send + Sync + Clone + 'static,
{
ReactantRawErasedWrapper::from_typed_reactant(reactant)
}
pub fn unerase_reactant<T, C, R>(wrapper: &ReactantErasedWrapper<T, C, R>) -> &R
where
T: Send + Sync + 'static,
C: Codec<T> + CodecName + Send + Sync + 'static,
R: Reactant<T, C> + Send + Sync + Clone + 'static,
{
wrapper.get_typed_reactant()
}
pub fn unerase_reactant_raw<T, C, R>(wrapper: &ReactantRawErasedWrapper<T, C, R>) -> &R
where
T: Send + Sync + 'static,
C: Codec<T> + CodecName + Send + Sync + 'static,
R: ReactantRaw<T, C> + Send + Sync + Clone + 'static,
{
wrapper.get_typed_reactant()
}
pub fn erase_error_reactant<T, C, R>(
reactant: Box<R>,
) -> Arc<dyn ErrorReactantErased + Send + Sync + 'static>
where
T: Send + Sync + 'static,
C: Codec<T> + CodecName + Send + Sync + 'static,
R: ErrorReactant<T, C> + Send + Sync + Clone + 'static,
{
ErrorReactantErasedWrapper::from_typed_reactant(reactant)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_utils::{DebugCodec, DebugStruct, TokioMpscReactant, TokioMpscReactantRaw};
use tokio::sync::mpsc;
#[test]
fn test_reactant_erased_cloning() {
let (sender, _receiver) = mpsc::channel(10);
let reactant = TokioMpscReactant { sender };
let erased = erase_reactant::<DebugStruct, DebugCodec, _>(Box::new(reactant));
let _cloned_box = erased.clone_to_box();
let _cloned_arc = erased.clone_to_arc();
}
#[test]
fn test_reactant_raw_erased_cloning() {
let (sender, _receiver) = mpsc::channel(10);
let raw_reactant = TokioMpscReactantRaw { sender };
let erased_raw = erase_reactant_raw::<DebugStruct, DebugCodec, _>(Box::new(raw_reactant));
let _cloned_raw_box = erased_raw.clone_to_box();
let _cloned_raw_arc = erased_raw.clone_to_arc();
}
}