1use crate::parse::{Parse, token};
2use bytes::Bytes;
3use bytesstr::BytesStr;
4use internal::IResult;
5use nom::branch::alt;
6use nom::bytes::complete::{tag_no_case, take_while1};
7use nom::combinator::map;
8use std::fmt;
9
10#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
26pub struct Method(Repr);
27
28macro_rules! methods {
29 ($($(#[$comments:meta])* $print:literal, $ident:ident;)+) => {
30
31 #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
32 #[allow(clippy::upper_case_acronyms)]
33 enum Repr {
34 $($ident,)+
35 Other(BytesStr),
36 }
37
38 impl Method {
39 $(pub const $ident : Self = Self(Repr :: $ident );)+
40
41 pub fn from_parse(src: &Bytes, slice: &str) -> Self {
42 if let Ok((_, repr)) = alt((
43 $(
44 map(tag_no_case($print), |_| Repr::$ident),
45 )*
46 ))(slice) as IResult<&str, Repr> {
47 Self(repr)
48 } else {
49 Self(Repr::Other(BytesStr::from_parse(src, slice)))
50 }
51 }
52 }
53
54 impl fmt::Display for Method {
55 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56 match &self.0 {
57 $(Repr:: $ident => f.write_str($print),)+
58 Repr::Other(other) => f.write_str(&other),
59 }
60 }
61 }
62 };
63}
64
65methods! {
66 "INVITE", INVITE;
67 "ACK", ACK;
68 "CANCEL", CANCEL;
69 "BYE", BYE;
70 "REGISTER", REGISTER;
71 "MESSAGE", MESSAGE;
72 "UPDATE", UPDATE;
73 "PRACK", PRACK;
74 "OPTIONS", OPTIONS;
75 "SUBSCRIBE", SUBSCRIBE;
76 "NOTIFY", NOTIFY;
77 "PUBLISH", PUBLISH;
78 "INFO", INFO;
79 "REFER", REFER;
80}
81
82impl Parse for Method {
83 fn parse(src: &Bytes) -> impl Fn(&str) -> IResult<&str, Self> + '_ {
84 move |i| map(take_while1(token), |slice| Self::from_parse(src, slice))(i)
85 }
86}
87impl_from_str!(Method);
88
89impl From<&str> for Method {
90 fn from(s: &str) -> Self {
91 let s = BytesStr::from(s);
92
93 Self::from_parse(s.as_ref(), s.as_ref())
94 }
95}
96
97#[cfg(test)]
98mod test {
99 use std::str::FromStr;
100
101 use super::Method;
102 use crate::method::Repr;
103
104 #[test]
105 fn invite_method() {
106 assert_eq!(Method::from_str("INVITE").unwrap(), Method::INVITE);
107
108 assert_eq!(Method::INVITE.to_string(), "INVITE");
109 }
110
111 #[test]
112 fn other_method() {
113 let method: Method = "SOMEOBSCUREMETHOD".parse().unwrap();
114
115 assert_eq!(method, Method(Repr::Other("SOMEOBSCUREMETHOD".into())));
116
117 assert_eq!(method.to_string(), "SOMEOBSCUREMETHOD");
118 }
119}