ftth_rsip/common/
method.rs

1#[doc(hidden)]
2pub use tokenizer::Tokenizer;
3
4use crate::Error;
5use std::convert::TryFrom;
6
7macro_rules! create_methods {
8    ($($name:ident),*) => {
9
10        /// The SIP [Request](super::super::Request) method.
11        #[derive(Debug, PartialEq, Eq, Clone, Copy)]
12        pub enum Method {
13            $(
14                $name,
15            )*
16        }
17
18        impl Method {
19            pub fn all() -> Vec<Method> {
20                vec![
21                    $(
22                        Self::$name,
23                    )*
24                ]
25            }
26        }
27
28        impl std::fmt::Display for Method {
29            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30                match self {
31                    $(
32                        Self::$name => write!(f, "{}", stringify!($name).to_uppercase()),
33                    )*
34                }
35            }
36        }
37
38        impl<'a> std::convert::TryFrom<tokenizer::Tokenizer<'a, &'a str, char>> for Method {
39            type Error = Error;
40
41            fn try_from(tokenizer: tokenizer::Tokenizer<'a, &'a str, char>) -> Result<Self, Self::Error> {
42                match tokenizer.value {
43                    $(
44                        part if part.trim().eq_ignore_ascii_case(stringify!($name)) => Ok(Method::$name),
45                    )*
46                    part => Err(Error::ParseError(format!("invalid method: {}", part))),
47                }
48            }
49        }
50    }
51}
52
53create_methods!(
54    Ack, Bye, Cancel, Info, Invite, Message, Notify, Options, PRack, Publish, Refer, Register,
55    Subscribe, Update
56);
57
58//TODO: not ideal performance here
59impl std::str::FromStr for Method {
60    type Err = crate::Error;
61
62    fn from_str(s: &str) -> Result<Self, Self::Err> {
63        Self::try_from(Tokenizer::from(s))
64    }
65}
66
67impl<'a> std::convert::TryFrom<tokenizer::Tokenizer<'a, &'a [u8], u8>> for Method {
68    type Error = Error;
69
70    fn try_from(tokenizer: tokenizer::Tokenizer<'a, &'a [u8], u8>) -> Result<Self, Self::Error> {
71        use std::str::from_utf8;
72
73        let value = from_utf8(tokenizer.value)?;
74
75        Method::try_from(Tokenizer::from(value))
76    }
77}
78
79#[doc(hidden)]
80pub mod tokenizer {
81    use crate::{AbstractInput, AbstractInputItem, GResult, GenericNomError, TokenizerError};
82    use std::marker::PhantomData;
83
84    #[derive(Debug, PartialEq, Eq, Clone)]
85    pub struct Tokenizer<'a, T, I>
86    where
87        T: AbstractInput<'a, I>,
88        I: AbstractInputItem<I>,
89    {
90        pub value: T,
91        phantom1: PhantomData<&'a T>,
92        phantom2: PhantomData<I>,
93    }
94
95    impl<'a, T, I> From<T> for Tokenizer<'a, T, I>
96    where
97        T: AbstractInput<'a, I>,
98        I: AbstractInputItem<I>,
99    {
100        fn from(value: T) -> Self {
101            Self {
102                value,
103                phantom1: Default::default(),
104                phantom2: Default::default(),
105            }
106        }
107    }
108
109    impl<'a, T, I> Tokenizer<'a, T, I>
110    where
111        T: AbstractInput<'a, I>,
112        I: AbstractInputItem<I>,
113    {
114        //works for request line
115        pub fn tokenize(part: T) -> GResult<T, Self> {
116            use nom::bytes::complete::{tag, take_while1};
117
118            let (rem, method) =
119                take_while1(I::is_token)(part).map_err(|_: GenericNomError<'a, T>| {
120                    TokenizerError::from(("method", part)).into()
121                })?;
122            //TODO: helpful to return early in case we parse a response but maybe it should not
123            //be checked here though
124            match tag::<_, _, nom::error::VerboseError<T>>("SIP/")(method) {
125                Err(_) => Ok((rem, Self::from(method))),
126                Ok(_) => Err(TokenizerError::from(("method", part)).into()),
127            }
128        }
129    }
130}
131
132#[cfg(feature = "test-utils")]
133impl testing_utils::Randomize for Method {
134    fn random() -> Self {
135        testing_utils::sample_vec(&Self::all())
136    }
137}