bolt-web 0.1.6

⚡ A high-performance, minimalist web framework for Rust, inspired by Express.js and Gin.
Documentation
use std::collections::HashMap;

use bytes::Bytes;
use http_body_util::BodyExt;
use hyper::header::HeaderName;
use hyper::{Request, Uri, Version, body::Incoming, header::HeaderValue};
use serde::de::DeserializeOwned;
use url::form_urlencoded;

pub struct RequestBody {
    inner: Request<Incoming>,
    params: HashMap<String, String>,
}

#[allow(dead_code)]
impl RequestBody {
    pub fn new(req: Request<Incoming>) -> Self {
        Self {
            inner: req,
            params: HashMap::new(),
        }
    }

    pub fn params(&self) -> &HashMap<String, String> {
        &self.params
    }

    pub(crate) fn set_params(&mut self, params: HashMap<String, String>) {
        self.params = params;
    }

    pub fn method(&self) -> &hyper::Method {
        self.inner.method()
    }

    pub fn path(&self) -> &str {
        self.inner.uri().path()
    }

    pub fn headers(&self) -> &hyper::HeaderMap {
        self.inner.headers()
    }

    pub fn set_headers(&mut self, key: &str, value: &str) {
        let key = HeaderName::from_bytes(key.as_bytes()).expect("Invalid header name");
        let value = HeaderValue::from_str(value).expect("Invalid header value");

        self.inner.headers_mut().insert(key, value);
    }

    pub fn get_headers(&mut self, key: &str) -> Option<&HeaderValue> {
        self.inner.headers().get(key)
    }

    pub fn uri(&self) -> &Uri {
        self.inner.uri()
    }

    pub fn version(&self) -> Version {
        self.inner.version()
    }

    pub fn query(&self) -> HashMap<String, String> {
        self.inner
            .uri()
            .query()
            .map(|q| {
                form_urlencoded::parse(q.as_bytes())
                    .into_owned()
                    .collect::<HashMap<String, String>>()
            })
            .unwrap_or_default()
    }

    pub fn query_param(&self, key: &str) -> Option<String> {
        let query_params = self.query();
        query_params.get(key).cloned()
    }

    pub async fn bytes(self) -> Result<Bytes, hyper::Error> {
        let (_, body) = self.inner.into_parts();
        let collected = body.collect().await?;
        Ok(collected.to_bytes())
    }

    pub async fn text(self) -> Result<String, Box<dyn std::error::Error>> {
        let bytes = self.bytes().await?;
        let text = String::from_utf8(bytes.to_vec())?;
        Ok(text)
    }

    pub async fn json<T: DeserializeOwned>(self) -> Result<T, Box<dyn std::error::Error>> {
        let bytes = self.bytes().await?;
        let value = serde_json::from_slice(&bytes)?;
        Ok(value)
    }

    pub fn get_cookie(&self, name: &str) -> Option<String> {
        self.inner
            .headers()
            .get(hyper::header::COOKIE)?
            .to_str()
            .ok()
            .and_then(|cookie_header| {
                cookie_header.split(';').map(|s| s.trim()).find_map(|pair| {
                    let mut parts = pair.splitn(2, '=');
                    let key = parts.next()?;
                    let value = parts.next()?;
                    if key == name {
                        Some(value.to_string())
                    } else {
                        None
                    }
                })
            })
    }
}