ftth_rsip/common/
method.rs1#[doc(hidden)]
2pub use tokenizer::Tokenizer;
3
4use crate::Error;
5use std::convert::TryFrom;
6
7macro_rules! create_methods {
8 ($($name:ident),*) => {
9
10 #[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
58impl 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 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 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}