ezk_sip_types/
method.rs

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/// Represents a SIP-Method.
11///
12/// To construct a known method use the constants:
13///
14/// # Example
15///
16/// ```
17/// use ezk_sip_types::Method;
18///
19/// // well known methods should be implemented as constants
20/// let _invite_method = Method::INVITE;
21///
22/// // custom methods can be also used:
23/// let _custom_method = Method::from("HELLO");
24/// ```
25#[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}