aws-local 0.1.1

A HTTP Server proxying to local AWS Lambda Runtime
use axum::{routing::any, Extension, Router};
use thiserror::Error;

use log::info;
use reqwest;
use std::net::SocketAddr;

use crate::config::CliConfig;
use crate::handler::call_proxy;
use crate::lambda::LambdaFunction;

#[derive(Error, Debug)]
pub enum ServerError {
    #[error("Binding on {address} failed: {error}")]
    Binding {
        address: SocketAddr,
        error: std::io::Error,
    },

    #[error("Serving on {address} failed: {error}")]
    Serving {
        address: SocketAddr,
        error: std::io::Error,
    },

    #[error("Creating HTTP client failed: {error}")]
    Reqwest { error: reqwest::Error },
}

impl ServerError {
    fn raise_binding<T>(address: SocketAddr, error: std::io::Error) -> Result<T, Self> {
        Err(Self::Binding {
            address: address,
            error: error,
        })
    }

    fn raise_serving<T>(address: SocketAddr, error: std::io::Error) -> Result<T, Self> {
        Err(Self::Serving {
            address: address,
            error: error,
        })
    }

    fn raise_reqwest<T>(error: reqwest::Error) -> Result<T, Self> {
        Err(Self::Reqwest { error: error })
    }
}

pub struct Server {
    lambda: LambdaFunction,
}

impl Server {
    pub fn new(config: CliConfig) -> Result<Self, ServerError> {
        let lambda = match reqwest::Client::builder().http1_only().build() {
            Ok(gateway) => LambdaFunction::new(config.function_name, gateway),
            Err(error) => return ServerError::raise_reqwest(error),
        };

        Ok(Server { lambda })
    }

    pub async fn start(self) -> Result<(), ServerError> {
        let address = SocketAddr::from(([127, 0, 0, 1], 3000));
        info!("Binding server on {address}");

        let listener = match tokio::net::TcpListener::bind(address).await {
            Ok(listener) => listener,
            Err(error) => return ServerError::raise_binding(address, error),
        };

        let app = Router::new()
            .route("/", any(call_proxy))
            .route("/*path", any(call_proxy))
            .layer(Extension(self.lambda));

        match axum::serve(listener, app).await {
            Ok(()) => info!("Server started on {address}"),
            Err(error) => return ServerError::raise_serving(address, error),
        };

        Ok(())
    }
}