glideapi 0.1.3

Ergonomic Rust web framework — FastAPI-style DX, Actix-level performance
Documentation
use serde::Serialize;

/// The body of a response — text (JSON, plain) or raw bytes (file downloads).
pub enum ResponseBody {
    Text(String),
    Bytes(Vec<u8>),
}

impl Default for ResponseBody {
    fn default() -> Self { ResponseBody::Text(String::new()) }
}

impl From<&str> for ResponseBody {
    fn from(s: &str) -> Self { ResponseBody::Text(s.to_string()) }
}

impl From<String> for ResponseBody {
    fn from(s: String) -> Self { ResponseBody::Text(s) }
}

/// A minimal HTTP response
#[derive(Default)]
pub struct Response {
    pub status:  u16,
    pub headers: Vec<(String, String)>,
    pub body:    ResponseBody,
}

impl Response {
    pub fn ok(body: impl Into<String>) -> Self {
        Self {
            status:  200,
            body:    ResponseBody::Text(body.into()),
            headers: vec![],
        }
    }

    pub fn with_header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
        self.headers.push((key.into(), value.into()));
        self
    }

    /// Return raw bytes with a given content-type. No unsafe — bytes stay as bytes.
    pub fn binary(status: u16, bytes: Vec<u8>, content_type: &str) -> Self {
        Self {
            status,
            body:    ResponseBody::Bytes(bytes),
            headers: vec![("content-type".into(), content_type.into())],
        }
    }
}

/// Anything that can become a Response
pub trait IntoResponse {
    fn into_response(self) -> Response;
}

impl IntoResponse for Response {
    fn into_response(self) -> Response { self }
}

impl IntoResponse for &'static str {
    fn into_response(self) -> Response { Response::ok(self) }
}

impl IntoResponse for String {
    fn into_response(self) -> Response { Response::ok(self) }
}

/// Json<T> wrapper — serialises T as JSON
pub struct Json<T>(pub T);

impl<T: Serialize> IntoResponse for Json<T> {
    fn into_response(self) -> Response {
        match serde_json::to_string(&self.0) {
            Ok(body) => Response::ok(body),
            Err(e)   => Response {
                status:  500,
                body:    ResponseBody::Text(e.to_string()),
                headers: vec![],
            },
        }
    }
}

impl<T: IntoResponse> IntoResponse for crate::error::Result<T> {
    fn into_response(self) -> Response {
        match self {
            Ok(v)  => v.into_response(),
            Err(e) => e.into_response(),
        }
    }
}