use crate::messaging::{AsyncSendError, CanSend, CanSendAsync};
use futures::FutureExt;
use futures::future::BoxFuture;
use std::marker::PhantomData;

/// Allows a Sender to be created from a raw function.
pub struct SendFunction<M: 'static, F: Fn(M) + Send + Sync + 'static> {
    send: F,
    _phantom: PhantomData<fn(M)>,
}

impl<M: 'static, F: Fn(M) + Send + Sync + 'static> SendFunction<M, F> {
    pub fn new(send: F) -> Self {
        Self { send, _phantom: PhantomData }
    }
}

impl<M: 'static, F: Fn(M) + Send + Sync + 'static> CanSend<M> for SendFunction<M, F> {
    fn send(&self, message: M) {
        (self.send)(message)
    }
}

/// Allows an AsyncSender to be created from a raw (synchronous) function.
pub struct SendAsyncFunction<M: 'static, R: Send + 'static, F: Fn(M) -> R + Send + Sync + 'static> {
    f: F,
    _phantom: PhantomData<fn(M, R)>,
}

impl<M: 'static, R: Send + 'static, F: Fn(M) -> R + Send + Sync + 'static>
    SendAsyncFunction<M, R, F>
{
    pub fn new(f: F) -> Self {
        Self { f, _phantom: PhantomData }
    }
}

impl<M: 'static, R: Send + 'static, F: Fn(M) -> R + Send + Sync + 'static> CanSendAsync<M, R>
    for SendAsyncFunction<M, R, F>
{
    fn send_async(&self, message: M) -> BoxFuture<'static, Result<R, AsyncSendError>> {
        let result = (self.f)(message);
        async move { Ok(result) }.boxed()
    }
}
