1use crate::syntax::is_token;
5use std::{borrow::Cow, error::Error, fmt, str::FromStr};
6
7#[derive(Clone, Debug, Eq, Hash, PartialEq)]
9pub struct Method(Cow<'static, str>);
10
11#[derive(Clone, Copy, Debug, Eq, PartialEq)]
12pub struct InvalidMethod;
13
14impl fmt::Display for InvalidMethod {
15 fn fmt(&self, output: &mut fmt::Formatter<'_>) -> fmt::Result {
16 output.write_str("invalid HTTP method token")
17 }
18}
19impl Error for InvalidMethod {}
20
21impl Method {
22 pub const GET: Self = Self(Cow::Borrowed("GET"));
23 pub const HEAD: Self = Self(Cow::Borrowed("HEAD"));
24 pub const POST: Self = Self(Cow::Borrowed("POST"));
25 pub const PUT: Self = Self(Cow::Borrowed("PUT"));
26 pub const DELETE: Self = Self(Cow::Borrowed("DELETE"));
27 pub const CONNECT: Self = Self(Cow::Borrowed("CONNECT"));
28 pub const OPTIONS: Self = Self(Cow::Borrowed("OPTIONS"));
29 pub const TRACE: Self = Self(Cow::Borrowed("TRACE"));
30 pub const PATCH: Self = Self(Cow::Borrowed("PATCH"));
31 pub const QUERY: Self = Self(Cow::Borrowed("QUERY"));
32
33 pub fn new(value: impl Into<String>) -> Result<Self, InvalidMethod> {
34 let value = value.into();
35 if !is_token(value.as_bytes()) {
36 return Err(InvalidMethod);
37 }
38 Ok(Self(Cow::Owned(value)))
39 }
40
41 pub fn as_str(&self) -> &str {
42 &self.0
43 }
44
45 pub fn is_safe(&self) -> bool {
47 matches!(
48 self.as_str(),
49 "GET" | "HEAD" | "OPTIONS" | "TRACE" | "QUERY"
50 )
51 }
52
53 pub fn is_idempotent(&self) -> bool {
56 self.is_safe() || matches!(self.as_str(), "PUT" | "DELETE")
57 }
58}
59
60impl FromStr for Method {
61 type Err = InvalidMethod;
62 fn from_str(value: &str) -> Result<Self, Self::Err> {
63 Self::new(value)
64 }
65}
66impl AsRef<str> for Method {
67 fn as_ref(&self) -> &str {
68 self.as_str()
69 }
70}
71impl fmt::Display for Method {
72 fn fmt(&self, output: &mut fmt::Formatter<'_>) -> fmt::Result {
73 output.write_str(self.as_str())
74 }
75}