min_http11_core 0.2.2

Minimum HTTP/1.1 version, request line and headers
Documentation
use crate::error::{Error, Result};
use std::convert::{TryFrom, TryInto};
use std::fmt::{Display, Formatter};

const GET: &[u8] = b"GET";
const HEAD: &[u8] = b"HEAD";
const POST: &[u8] = b"POST";
const PUT: &[u8] = b"PUT";
const DELETE: &[u8] = b"DELETE";
const OPTIONS: &[u8] = b"OPTIONS";
const PATCH: &[u8] = b"PATCH";

#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub enum Method {
    Get,
    Head,
    Post,
    Put,
    Delete,
    Options,
    Patch,
    Other(&'static [u8]),
}

impl Method {
    pub fn as_slice(&self) -> &'static [u8] {
        self.into()
    }
    pub fn from_static(value: &'static [u8]) -> Self {
        value.try_into().unwrap_or(Method::Other(value))
    }
    pub fn can_have_body(&self) -> bool {
        !matches!(self, Method::Get | Method::Head | Method::Options)
    }
}

impl Display for Method {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.as_slice().escape_ascii())
    }
}

impl<'a, const N: usize> TryFrom<&'a [u8; N]> for Method {
    type Error = Error;

    fn try_from(value: &'a [u8; N]) -> Result<Self> {
        value.as_slice().try_into()
    }
}

impl<'a> TryFrom<&'a [u8]> for Method {
    type Error = Error;

    fn try_from(value: &'a [u8]) -> Result<Self> {
        match value {
            GET => Ok(Method::Get),
            HEAD => Ok(Method::Head),
            POST => Ok(Method::Post),
            PUT => Ok(Method::Put),
            DELETE => Ok(Method::Delete),
            OPTIONS => Ok(Method::Options),
            PATCH => Ok(Method::Patch),
            other => Err(Error::UnknownMethod(other.escape_ascii().to_string())),
        }
    }
}

impl From<&Method> for &'static [u8] {
    fn from(value: &Method) -> Self {
        match value {
            Method::Get => GET,
            Method::Head => HEAD,
            Method::Post => POST,
            Method::Put => PUT,
            Method::Delete => DELETE,
            Method::Options => OPTIONS,
            Method::Patch => PATCH,
            Method::Other(value) => value,
        }
    }
}

impl AsRef<[u8]> for Method {
    fn as_ref(&self) -> &'static [u8] {
        self.into()
    }
}

impl TryFrom<&str> for Method {
    type Error = Error;

    fn try_from(value: &str) -> Result<Self> {
        value.as_bytes().try_into()
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn from_bytes() {
        assert_eq!(GET.try_into(), Ok(Method::Get));
        assert_eq!(HEAD.try_into(), Ok(Method::Head));
        assert_eq!(POST.try_into(), Ok(Method::Post));
        assert_eq!(PUT.try_into(), Ok(Method::Put));
        assert_eq!(DELETE.try_into(), Ok(Method::Delete));
        assert_eq!(OPTIONS.try_into(), Ok(Method::Options));
        assert_eq!(PATCH.try_into(), Ok(Method::Patch));
        let unknown: Result<Method> = b"UNKNOWN".try_into();
        assert!(unknown.is_err());
    }

    #[test]
    fn from_str() {
        assert_eq!("GET".try_into(), Ok(Method::Get));
        assert_eq!("HEAD".try_into(), Ok(Method::Head));
        assert_eq!("POST".try_into(), Ok(Method::Post));
        assert_eq!("PUT".try_into(), Ok(Method::Put));
        assert_eq!("DELETE".try_into(), Ok(Method::Delete));
        assert_eq!("OPTIONS".try_into(), Ok(Method::Options));
        assert_eq!("PATCH".try_into(), Ok(Method::Patch));
        let unknown: Result<Method> = "UNKNOWN".try_into();
        assert!(unknown.is_err());
    }

    #[test]
    fn as_ref() {
        assert_eq!(Method::Get.as_ref(), GET);
        assert_eq!(Method::Head.as_ref(), HEAD);
        assert_eq!(Method::Post.as_ref(), POST);
        assert_eq!(Method::Put.as_ref(), PUT);
        assert_eq!(Method::Delete.as_ref(), DELETE);
        assert_eq!(Method::Options.as_ref(), OPTIONS);
        assert_eq!(Method::Patch.as_ref(), PATCH);
        assert_eq!(Method::from_static(b"UNKNOWN").as_ref(), b"UNKNOWN")
    }
}