taiko 0.1.37

A composable web server
Documentation
use std::time::Duration;
use serde::{Deserialize, Serialize};
use tokio::net::TcpListener;
use tokio::time::sleep;
use taiko::body::JsonError;
use taiko::extract::{ClientIp};
use taiko::prelude::*;
use taiko::request::{Consume, Extract};
use taiko::response::IntoResponse;

#[tokio::main]
async fn main() {
    tracing_subscriber::fmt::init();

    let listener = TcpListener::bind("0.0.0.0:8080")
        .await
        .expect("Could not listen to 8080");

    let router = Router::new()
        .get("/", index);

    App::new(())
        .handler(router)
        .listen(listener)
        .await;
}

#[derive(Serialize, Deserialize)]
struct Payload {
    message: String
}

#[derive(Serialize, Deserialize)]
enum HttpError {
    BadRequest(String)
}

impl IntoResponse for HttpError {
    fn into_response(self) -> Response {
        match self {
            HttpError::BadRequest(message) => Json(Payload {
                message: format!("Bad request: {message}")
            }).into_response()
        }
    }
}

impl From<JsonError> for HttpError {
    fn from(value: JsonError) -> Self {
        HttpError::BadRequest(value.to_string())
    }
}

impl From<Empty> for HttpError {
    fn from(value: Empty) -> Self {
        HttpError::BadRequest("".to_string())
    }
}

async fn index(request: Request, _state: ()) -> Result<Json<Payload>, HttpError> {
    let ip = ClientIp::extract(&request, &_state).await?;
    println!("{ip}");

    let Json(payload) = Json::<Payload>::consume(request).await?;
    sleep(Duration::from_secs(10)).await;

    Ok(Json(Payload {
        message: payload.message
    }))
}