use serde::de::{Error as DeError, Unexpected};
use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
use std::fmt::{self, Display};
use std::str::FromStr;
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum Method {
Get,
Head,
Post,
Put,
Delete,
Connect,
Options,
Trace,
Patch,
}
impl Method {
pub fn is_safe(&self) -> bool {
matches!(
self,
Method::Get | Method::Head | Method::Options | Method::Trace
)
}
}
struct MethodVisitor;
impl<'de> Visitor<'de> for MethodVisitor {
type Value = Method;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "a HTTP method &str")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: DeError,
{
match Method::from_str(v) {
Ok(method) => Ok(method),
Err(_) => Err(DeError::invalid_value(Unexpected::Str(v), &self)),
}
}
}
impl<'de> Deserialize<'de> for Method {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_str(MethodVisitor)
}
}
impl Serialize for Method {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
impl Display for Method {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Get => write!(f, "GET"),
Self::Head => write!(f, "HEAD"),
Self::Post => write!(f, "POST"),
Self::Put => write!(f, "PUT"),
Self::Delete => write!(f, "DELETE"),
Self::Connect => write!(f, "CONNECT"),
Self::Options => write!(f, "OPTIONS"),
Self::Trace => write!(f, "TRACE"),
Self::Patch => write!(f, "PATCH"),
}
}
}
impl FromStr for Method {
type Err = crate::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match &*s.to_ascii_uppercase() {
"GET" => Ok(Self::Get),
"HEAD" => Ok(Self::Head),
"POST" => Ok(Self::Post),
"PUT" => Ok(Self::Put),
"DELETE" => Ok(Self::Delete),
"CONNECT" => Ok(Self::Connect),
"OPTIONS" => Ok(Self::Options),
"TRACE" => Ok(Self::Trace),
"PATCH" => Ok(Self::Patch),
_ => crate::bail!("Invalid HTTP method"),
}
}
}
impl<'a> std::convert::TryFrom<&'a str> for Method {
type Error = crate::Error;
fn try_from(value: &'a str) -> Result<Self, Self::Error> {
Self::from_str(value)
}
}
impl AsRef<str> for Method {
fn as_ref(&self) -> &str {
match self {
Self::Get => "GET",
Self::Head => "HEAD",
Self::Post => "POST",
Self::Put => "PUT",
Self::Delete => "DELETE",
Self::Connect => "CONNECT",
Self::Options => "OPTIONS",
Self::Trace => "TRACE",
Self::Patch => "PATCH",
}
}
}
#[cfg(test)]
mod test {
use super::Method;
#[test]
fn serde() -> Result<(), serde_json::Error> {
assert_eq!(Method::Get, serde_json::from_str("\"GET\"")?);
assert_eq!(Some("PATCH"), serde_json::to_value(Method::Patch)?.as_str());
Ok(())
}
#[test]
fn serde_fail() -> Result<(), serde_json::Error> {
serde_json::from_str::<Method>("\"ABC\"").expect_err("Did deserialize from invalid string");
Ok(())
}
}