plabayo / rama

modular service framework to move and transform network packets
https://ramaproxy.org
Apache License 2.0
189 stars 20 forks source link

consider making service_fn more implicit #301

Closed GlenDC closed 2 months ago

GlenDC commented 2 months ago

We already use "into" like traits for stuff like the endpoint services in rama's web service. Perhaps it's time we move this concept also into the more abstract ServiceFn implementation, making it that you do not need to add service_fn everywhere.

Something to be thought about prior to releasing 0.2

GlenDC commented 2 months ago

This could be achieved using a the following definition at rama-core/src/service/into.rs:

use super::{
    handler::{Factory, FromContextRequest, ServiceFn},
    service_fn, Service,
};
use std::future::Future;

/// utility trait to both services and service functions as a [`rama_core::Service`].
pub trait IntoService<S, Request, X>:
    private::Sealed<S, Request, X> + Send + Sync + 'static
{
    /// [`rama_core::Service`] to turn `self` into
    type Service: Service<S, Request>;

    /// convert the type into a [`rama_core::Service`].
    fn into_service(self) -> Self::Service;
}

impl<T, S, Request> IntoService<S, Request, ()> for T
where
    T: Service<S, Request>,
{
    type Service = T;

    fn into_service(self) -> Self::Service {
        self
    }
}

impl<F, S, Request, T, R, O, E> IntoService<S, Request, (T, R, O, E)> for F
where
    F: Factory<T, R, O, E>,
    R: Future<Output = Result<O, E>>,
    T: FromContextRequest<S, Request>,
    R: Send + 'static,
    O: Send + 'static,
    E: Send + Sync + 'static,
{
    type Service = ServiceFn<F, T, R, O, E>;

    fn into_service(self) -> Self::Service {
        service_fn(self)
    }
}

mod private {
    use crate::{
        service::handler::{Factory, FromContextRequest},
        Service,
    };
    use std::future::Future;

    pub trait Sealed<S, Request, X> {}

    impl<T, S, Request> Sealed<S, Request, ()> for T where T: Service<S, Request> {}
    impl<F, S, Request, T, R, O, E> Sealed<S, Request, (T, R, O, E)> for F
    where
        F: Factory<T, R, O, E>,
        R: Future<Output = Result<O, E>>,
        T: FromContextRequest<S, Request>,
        R: Send + 'static,
        O: Send + 'static,
        E: Send + Sync + 'static,
    {
    }
}

However... It would lead to confusion: