athene 2.0.4

A simple and lightweight rust web framework based on hyper
Documentation
#[cfg(feature = "multipart")]
use crate::multipart::FilePart;
use crate::{
    body::HttpBody,
    error::Error,
    responder::{Form, FromBytes, Json, Query},
};
#[cfg(feature = "cookie")]
use cookie::{Cookie, CookieJar};
use headers::{Header, HeaderMapExt};
use http_body_util::BodyExt;
use hyper::body::Bytes;
#[cfg(feature = "multipart")]
use multimap::MultiMap;
use serde::de::DeserializeOwned;
use std::{
    collections::HashMap,
    net::SocketAddr,
    ops::{Deref, DerefMut},
};

#[derive(Debug)]
pub struct Request {
    #[doc(hidden)]
    inner: hyper::http::Request<HttpBody>,
    #[doc(hidden)]
    params: HashMap<String, String>,
    #[doc(hidden)]
    addr: SocketAddr,
}

impl Request {
    /// Get inner Request
    #[inline]
    pub fn request(&mut self) -> &mut hyper::Request<HttpBody> {
        &mut self.inner
    }

    #[doc(hidden)]
    #[inline]
    pub(crate) fn new(inner: hyper::http::Request<HttpBody>, addr: SocketAddr) -> Self {
        Request {
            inner,
            params: HashMap::new(),
            addr,
        }
    }

    /// Get `Cookie` from Cookie header
    #[cfg(feature = "cookie")]
    #[inline]
    pub fn cookie(&mut self, name: &str) -> Option<Cookie<'static>> {
        if let Some(mut cookie_iter) = self
            .inner
            .headers()
            .get("Cookie")
            .and_then(|cookies| cookies.to_str().ok())
            .map(|cookies_str| cookies_str.split(';').map(|s| s.trim()))
            .map(|cookie_iter| {
                cookie_iter.filter_map(|cookie_s| Cookie::parse(cookie_s.to_string()).ok())
            })
        {
            cookie_iter.find(|cookie| cookie.name() == name)
        } else {
            None
        }
    }

    /// Get Cookies from the Cookie header
    #[cfg(feature = "cookie")]
    #[inline]
    pub fn cookies(&mut self) -> CookieJar {
        let mut jar = CookieJar::new();
        if let Some(cookie_iter) = self
            .inner
            .headers()
            .get("Cookie")
            .and_then(|cookies| cookies.to_str().ok())
            .map(|cookies_str| cookies_str.split(';').map(|s| s.trim()))
            .map(|cookie_iter| {
                cookie_iter.filter_map(|cookie_s| Cookie::parse(cookie_s.to_string()).ok())
            })
        {
            cookie_iter.for_each(|c| jar.add_original(c))
        }
        jar
    }

    /// Get accept.
    #[inline]
    pub fn accept(&self) -> Vec<mime::Mime> {
        let mut list: Vec<mime::Mime> = vec![];
        if let Some(accept) = self
            .inner
            .headers()
            .get("accept")
            .and_then(|h| h.to_str().ok())
        {
            let parts: Vec<&str> = accept.split(',').collect();
            for part in parts {
                if let Ok(mt) = part.parse() {
                    list.push(mt);
                }
            }
        }
        list
    }

    /// Get first accept.
    #[inline]
    pub fn first_accept(&self) -> Option<mime::Mime> {
        let mut accept = self.accept();
        if !accept.is_empty() {
            Some(accept.remove(0))
        } else {
            None
        }
    }

    #[inline]
    pub fn header<T: Header>(&self) -> Option<T> {
        self.inner.headers().typed_get()
    }

    // peer_addr
    #[inline]
    pub fn remote_addr(self) -> SocketAddr {
        self.addr
    }

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

    #[inline]
    pub fn params_mut(&mut self) -> &mut HashMap<String, String> {
        &mut self.params
    }

    #[inline]
    pub fn param<T: std::str::FromStr>(&mut self, key: &str) -> Result<T, Error> {
        let value = self
            .params_mut()
            .remove(key)
            .ok_or_else(|| Error::MissingParameter(key.to_string(), false))?;
        Ok(value
            .parse::<T>()
            .map_err(|_| Error::InvalidParameter(key.to_string(), false))?)
    }

    #[inline]
    pub fn query<'de, B>(&'de self) -> Result<B, Error>
    where
        B: serde::Deserialize<'de>,
    {
        let query = self.inner.uri().query().unwrap_or("");
        serde_urlencoded::from_str::<B>(query).map_err(Error::SerdeUrlDe)
    }

    #[inline]
    pub fn content_type(&self) -> Option<&str> {
        let content_type = self.inner.headers().get("content-type")?;
        let content_type = content_type.to_str().ok()?;
        Some(content_type)
    }

    #[cfg(feature = "multipart")]
    #[inline]
    async fn file_part(&mut self) -> Result<MultiMap<String, FilePart>, Error> {
        let c_type = self.content_type().expect("bad request");
        let boundary = multer::parse_boundary(c_type)?;
        let mut multipart =
            multer::Multipart::new(std::mem::take(self.inner.body_mut()), &boundary);
        let mut file_parts = MultiMap::new();
        while let Some(mut field) = multipart.next_field().await? {
            if let Some(name) = field.name().map(|s| s.to_owned()) {
                if field.headers().get("content-type").is_some() {
                    file_parts.insert(name, FilePart::new(&mut field).await?);
                }
            }
        }
        Ok(file_parts)
    }

    #[cfg(feature = "multipart")]
    #[inline]
    pub async fn file(&mut self, key: &str) -> Result<FilePart, Error> {
        let file_part = self.file_part().await?;
        let file_part = file_part.get(key).unwrap();
        Ok(file_part.to_owned())
    }

    #[cfg(feature = "multipart")]
    #[inline]
    pub async fn files(&mut self, key: &str) -> Result<Vec<FilePart>, Error> {
        let file_part = self.file_part().await?;
        let file_part = file_part.get_vec(key).unwrap();
        Ok(file_part.to_owned())
    }

    #[cfg(feature = "multipart")]
    #[inline]
    pub async fn upload(&mut self, key: &str, save_path: &str) -> Result<u64, Error> {
        let file = self.file(key).await?;
        std::fs::create_dir_all(save_path)?;
        let dest = format!("{}/{}", save_path, file.name().unwrap());
        Ok(std::fs::copy(file.path(), std::path::Path::new(&dest))?)
    }

    #[cfg(feature = "multipart")]
    #[inline]
    pub async fn uploads(&mut self, key: &str, save_path: &str) -> Result<String, Error> {
        let files = self.files(key).await?;
        std::fs::create_dir_all(save_path)?;
        let mut msgs = Vec::with_capacity(files.len());
        for file in files {
            let dest = format!("{}/{}", save_path, file.name().unwrap());
            if let Err(e) = std::fs::copy(file.path(), std::path::Path::new(&dest)) {
                return Ok(format!("file not found in request: {e}"));
            } else {
                msgs.push(dest);
            }
        }
        Ok(format!("Files uploaded:\n\n{}", msgs.join("\n")))
    }

    #[inline]
    pub async fn parse<T>(&mut self) -> Result<T, Error>
    where
        T: DeserializeOwned,
    {
        let body = self.inner.body_mut().collect().await?.to_bytes();
        let essence = self.content_type();
        match essence {
            Some("application/json") => serde_json::from_slice(&body).map_err(Error::SerdeJson),
            Some("application/x-www-form-urlencoded") => {
                serde_urlencoded::from_bytes(&body).map_err(Error::SerdeUrlDe)
            }
            #[cfg(feature = "cbor")]
            Some("application/cbor") => {
                ciborium::de::from_reader(&body[..]).map_err(|e| Error::Other(e.to_string()))
            }
            #[cfg(feature = "msgpack")]
            Some("application/msgpack") => {
                rmp_serde::from_slice(&body).map_err(Error::MsgpackDeserialization)
            }
            _ => Err(Error::Other(String::from("Invalid Context-Type"))),
        }
    }

    #[inline]
    pub async fn parse_body<T: FromBytes>(&mut self) -> Result<T::Output, Error> {
        let bytes = self.inner.body_mut().collect().await?.to_bytes();
        let value = T::from_bytes(bytes)?;
        Ok(value)
    }

    #[inline]
    pub async fn parse_query<T: FromBytes>(&mut self) -> Result<Query<T::Output>, Error> {
        let query = String::from(self.uri().query().unwrap_or(""));
        let bytes = Bytes::from(query);
        let value = T::from_bytes(bytes)?;
        Ok(Query(value))
    }

    #[inline]
    pub async fn parse_json<T: FromBytes>(&mut self) -> Result<Json<T::Output>, Error> {
        let bytes = self.inner.body_mut().collect().await?.to_bytes();
        let value = T::from_bytes(bytes)?;
        Ok(Json(value))
    }

    #[inline]
    pub async fn parse_form<T: FromBytes>(&mut self) -> Result<Form<T::Output>, Error> {
        let bytes = self.inner.body_mut().collect().await?.to_bytes();
        let value = T::from_bytes(bytes)?;
        Ok(Form(value))
    }
}

impl Deref for Request {
    type Target = hyper::Request<HttpBody>;
    #[inline]
    fn deref(&self) -> &Self::Target {
        &self.inner
    }
}

impl DerefMut for Request {
    #[inline]
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.inner
    }
}