thruster 1.3.13

A middleware based http async web server.
Documentation
use actix_web::dev::{AppService, Body, HttpServiceFactory};
use actix_web::{web, Error, HttpRequest, HttpResponse};
use futures::TryFutureExt;
use http::StatusCode;
use std::marker::PhantomData;
use std::sync::Arc;
use tokio_util::sync::ReusableBoxFuture;

use crate::app::App;
use crate::context::basic_actix_context::ActixRequest;
use crate::core::context::Context;
use crate::server::ThrusterServer;
use crate::Response;

struct _Error {
    _message: String,
}

pub struct ActixServer<T: 'static + Context + Clone + Send + Sync, S: 'static + Send> {
    app: Arc<App<ActixRequest, T, S>>,
}

impl<T, S> ThrusterServer for ActixServer<T, S>
where
    T: Context<Response = Response> + 'static + Clone + Send + Sync,
    S: 'static + Send + Sync,
{
    type Context = T;
    type Response = Response;
    type Request = ActixRequest;
    type State = S;

    fn new(mut app: App<ActixRequest, T, Self::State>) -> Self {
        app = app.commit();

        ActixServer { app: Arc::new(app) }
    }

    fn build(self, _host: &str, _port: u16) -> ReusableBoxFuture<()> {
        panic!("Can't use build for the actix runtime.")
    }

    fn start(self, host: &str, port: u16)
    where
        Self: Sized,
    {
        actix_rt::System::new().block_on(async move {
            let app = self.app.clone();

            let _ = actix_web::HttpServer::new(move || {
                actix_web::App::new()
                    .data(app.clone())
                    .service(ThrusterActixServiceFactory::<T, S>::default())
            })
            .bind(format!("{}:{}", host, port))
            .expect(&format!("Could not bind to address {}:{}", host, port))
            .run()
            .await;
        });
    }
}

struct ThrusterActixServiceFactory<T, S> {
    context: PhantomData<T>,
    state: PhantomData<S>,
}

impl<T, S> Default for ThrusterActixServiceFactory<T, S> {
    fn default() -> Self {
        ThrusterActixServiceFactory {
            context: PhantomData::default(),
            state: PhantomData::default(),
        }
    }
}

impl<T, S> HttpServiceFactory for ThrusterActixServiceFactory<T, S>
where
    T: Context<Response = Response> + 'static + Clone + Send + Sync,
    S: 'static + Send + Sync,
{
    fn register(self, config: &mut AppService) {
        async fn handler<T, S>(
            req: HttpRequest,
            payload: web::Payload,
            app: web::Data<Arc<App<ActixRequest, T, S>>>,
        ) -> Result<HttpResponse, Error>
        where
            T: Context<Response = Response> + 'static + Clone + Send + Sync,
            S: 'static + Send + Sync,
        {
            let actix_req = ActixRequest::new(req, payload).await;

            let response = app
                .clone()
                .match_and_resolve(actix_req)
                .map_err(|e| Error::from(e))
                .await?;

            let r = response;

            let response = HttpResponse::new(StatusCode::OK).set_body(Body::from(r.response));

            Ok::<_, Error>(response)
        }

        let resource = actix_web::Resource::new("*")
            .name("ThrusterActix")
            .route(web::get().to(handler::<T, S>))
            .route(web::post().to(handler::<T, S>))
            .route(web::put().to(handler::<T, S>))
            .route(web::head().to(handler::<T, S>))
            .route(web::patch().to(handler::<T, S>))
            .route(web::delete().to(handler::<T, S>))
            .route(web::trace().to(handler::<T, S>));

        actix_web::dev::HttpServiceFactory::register(resource, config)
    }
}