acril 0.1.0

A kitchen-sink actor framework.
Documentation
use crate::Handler;

use super::*;
pub use acril_macros::{with_builder, ClientEndpoint};
use http_types::Url;

pub struct DefaultMiddleware;

impl Service for DefaultMiddleware {
    type Context = ();
    type Error = http_types::Error;
}

impl Handler<Request> for DefaultMiddleware {
    type Response = Response;

    async fn call(
        &mut self,
        request: Request,
        _cx: &mut Self::Context,
    ) -> Result<Self::Response, Self::Error> {
        acril_http::client::connect(request).await
    }
}

pub trait Middleware: Handler<Request, Response = Response, Context = ()> {}

#[derive(Default, Clone, Debug, PartialEq, Eq)]
pub struct HttpClient<M = DefaultMiddleware> {
    middleware: M,
    base_url: Option<Url>,
}

impl<M: Middleware> HttpClient<M> {
    pub fn new_with(middleware: M) -> Self {
        Self {
            middleware,
            base_url: None,
        }
    }

    pub fn with_base_url(mut self, base_url: Url) -> Self {
        self.base_url = Some(base_url);
        self
    }

    pub fn get_middleware(&self) -> &M {
        &self.middleware
    }
    pub fn base_url(&self) -> Option<&Url> {
        self.base_url.as_ref()
    }

    pub async fn call<E: Service<Context = Self> + ClientEndpoint>(
        &mut self,
        endpoint: E,
    ) -> Result<E::Output, E::Error> {
        endpoint.run(self).await
    }

    pub async fn execute(&mut self, request: Request) -> Result<Response, M::Error> {
        self.middleware.call(request, &mut ()).await
    }
}

impl HttpClient<DefaultMiddleware> {
    pub fn new() -> Self {
        Self {
            middleware: DefaultMiddleware,
            base_url: None,
        }
    }
}

pub trait HttpClientContext {
    type Error;

    fn new_request(&self, method: Method, url: &str) -> Request;
    async fn run_request(&mut self, request: Request) -> Result<Response, Self::Error>;
}

impl<M: Middleware> HttpClientContext for HttpClient<M> {
    type Error = <M as Service>::Error;

    fn new_request(&self, method: Method, url: &str) -> Request {
        Request::new(
            method,
            if let Some(base) = self.base_url.as_ref() {
                Url::options()
                    .base_url(Some(base))
                    .parse(url)
                    .expect("errors reparsing a perfectly good url")
            } else {
                Url::parse(url).unwrap()
            },
        )
    }

    async fn run_request(&mut self, request: Request) -> Result<Response, M::Error> {
        self.execute(request).await
    }
}

pub trait ClientEndpoint: Service {
    type Output;

    async fn run(&self, context: &mut Self::Context) -> Result<Self::Output, Self::Error>;
}