ftth_rsip/common/
status_code.rs

1use crate::Error;
2#[doc(hidden)]
3pub use tokenizer::Tokenizer;
4
5macro_rules! create_status_codes {
6    ($($name:ident => $code:expr),*) => {
7
8        /// The SIP [Response](super::super::Response) status code (or response code as SIP main
9        /// RFC refers to them). This is not a `Copy` type because in case of an unknown (= not
10        /// defined in any SIP RFC) status code, the reason is also provided inside the `Other`
11        /// tuple variant.
12        #[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Clone)]
13        pub enum StatusCode {
14            $(
15                $name,
16            )*
17            Other(u16, String),
18        }
19
20        impl StatusCode {
21            pub fn code(&self) -> u16 {
22                match self {
23                    $(
24                        Self::$name => $code,
25                    )*
26                    Self::Other(code, _) => *code,
27                }
28            }
29        }
30
31        impl From<u16> for StatusCode {
32            fn from(code: u16) -> Self {
33                match code {
34                    $(
35                        $code => Self::$name,
36                    )*
37                    code => Self::Other(code, "Other".into()),
38                }
39
40            }
41        }
42
43        impl std::fmt::Display for StatusCode {
44            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45                match self {
46                    $(
47                        Self::$name => write!(f, "{} {}", stringify!($code), stringify!($name)),
48                    )*
49                    Self::Other(code, reason) => write!(f, "{} {}", code, reason),
50                }
51            }
52        }
53
54        //Here we decide to completely ignore the reason if the code can be mapped to a well known status
55        impl<'a> std::convert::TryFrom<tokenizer::Tokenizer<'a, &'a str, char>> for StatusCode {
56            type Error = Error;
57
58            fn try_from(tokenizer: tokenizer::Tokenizer<'a, &'a str, char>) -> Result<Self, Self::Error> {
59                match (tokenizer.code.parse::<u16>()?, tokenizer.reason) {
60                    $(
61                        ($code, _) => Ok(StatusCode::$name),
62                    )*
63                    (code, reason) => Ok(StatusCode::Other(code, reason.into())),
64                }
65            }
66        }
67    }
68}
69
70create_status_codes!(Trying => 100,
71    Ringing => 180,
72    CallIsBeingForwarded => 181,
73    Queued => 182,
74    SessionProgress => 183,
75    EarlyDialogTerminated => 199,
76    OK => 200,
77    Accepted => 201,
78    NoNotification => 204,
79    MultipleChoices => 300,
80    MovedPermanently => 301,
81    MovedTemporarily => 302,
82    UseProxy => 305,
83    AlternativeService => 380,
84    BadRequest => 400,
85    Unauthorized => 401,
86    PaymentRequired => 402,
87    Forbidden => 403,
88    NotFound => 404,
89    MethodNotAllowed => 405,
90    NotAcceptable => 406,
91    ProxyAuthenticationRequired => 407,
92    RequestTimeout => 408,
93    Conflict => 409,
94    Gone => 410,
95    LengthRequired => 411,
96    ConditionalRequestFailed => 412,
97    RequestEntityTooLarge => 413,
98    RequestUriTooLong => 414,
99    UnsupportedMediaType => 415,
100    UnsupportedUriScheme => 416,
101    UnknownResourcePriority => 417,
102    BadExtension => 420,
103    ExtensionRequired => 421,
104    SessionIntervalTooSmall => 422,
105    IntervalTooBrief => 423,
106    BadLocationInformation => 424,
107    UseIdentityHeader => 428,
108    ProvideReferrerIdentity => 429,
109    AnonymityDisallowed => 433,
110    BadIdentityInfo => 436,
111    UnsupportedCertificate => 437,
112    InvalidIdentityHeader => 438,
113    FirstHopLacksOutboundSupport => 439,
114    MaxBreadthExceeded => 440,
115    BadInfoPackage => 469,
116    ConsentNeeded => 470,
117    TemporarilyUnavailable => 480,
118    CallTransactionDoesNotExist => 481,
119    LoopDetected => 482,
120    TooManyHops => 483,
121    AddressIncomplete => 484,
122    Ambiguous => 485,
123    BusyHere => 486,
124    RequestTerminated => 487,
125    NotAcceptableHere => 488,
126    BadEvent => 489,
127    RequestPending => 491,
128    Undecipherable => 493,
129    SecurityAgreementRequired => 494,
130    ServerInternalError => 500,
131    NotImplemented => 501,
132    BadGateway => 502,
133    ServiceUnavailable => 503,
134    ServerTimeOut => 504,
135    VersionNotSupported => 505,
136    MessageTooLarge => 513,
137    PreconditionFailure => 580,
138    BusyEverywhere => 600,
139    Decline => 603,
140    DoesNotExistAnywhere => 604,
141    NotAcceptableGlobal => 606,
142    Unwanted => 607
143);
144
145#[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Clone, Copy)]
146pub enum StatusCodeKind {
147    Provisional,
148    Successful,
149    Redirection,
150    RequestFailure,
151    ServerFailure,
152    GlobalFailure,
153    Other,
154}
155
156impl StatusCode {
157    pub fn kind(&self) -> StatusCodeKind {
158        let code = self.code();
159        match code {
160            code if (100..200).contains(&code) => StatusCodeKind::Provisional,
161            code if (200..300).contains(&code) => StatusCodeKind::Successful,
162            code if (300..400).contains(&code) => StatusCodeKind::Redirection,
163            code if (400..500).contains(&code) => StatusCodeKind::RequestFailure,
164            code if (500..600).contains(&code) => StatusCodeKind::ServerFailure,
165            code if (600..700).contains(&code) => StatusCodeKind::GlobalFailure,
166            _ => StatusCodeKind::Other,
167        }
168    }
169}
170
171impl From<StatusCode> for u16 {
172    fn from(from: StatusCode) -> u16 {
173        from.code()
174    }
175}
176
177impl Default for StatusCode {
178    fn default() -> Self {
179        Self::OK
180    }
181}
182
183impl<'a> std::convert::TryFrom<tokenizer::Tokenizer<'a, &'a [u8], u8>> for StatusCode {
184    type Error = Error;
185
186    fn try_from(tokenizer: tokenizer::Tokenizer<'a, &'a [u8], u8>) -> Result<Self, Self::Error> {
187        use std::str::from_utf8;
188
189        Self::try_from(Tokenizer::from((
190            from_utf8(tokenizer.code)?,
191            from_utf8(tokenizer.reason)?,
192        )))
193    }
194}
195
196#[doc(hidden)]
197mod tokenizer {
198    use crate::{AbstractInput, AbstractInputItem, GResult, GenericNomError, TokenizerError};
199    use std::marker::PhantomData;
200
201    #[derive(Debug, PartialEq, Eq, Clone)]
202    pub struct Tokenizer<'a, T, I>
203    where
204        T: AbstractInput<'a, I>,
205        I: AbstractInputItem<I>,
206    {
207        pub code: T,
208        pub reason: T,
209        phantom1: PhantomData<&'a T>,
210        phantom2: PhantomData<I>,
211    }
212
213    impl<'a, T, I> From<(T, T)> for Tokenizer<'a, T, I>
214    where
215        T: AbstractInput<'a, I>,
216        I: AbstractInputItem<I>,
217    {
218        fn from(from: (T, T)) -> Self {
219            Self {
220                code: from.0,
221                reason: from.1,
222                phantom1: PhantomData,
223                phantom2: PhantomData,
224            }
225        }
226    }
227
228    impl<'a, T, I> Tokenizer<'a, T, I>
229    where
230        T: AbstractInput<'a, I>,
231        I: AbstractInputItem<I>,
232    {
233        pub fn tokenize(part: T) -> GResult<T, Self> {
234            use nom::{
235                branch::alt,
236                bytes::complete::{tag, take, take_until},
237                combinator::rest,
238                sequence::tuple,
239            };
240
241            let (rem, (code, _, reason)) =
242                tuple((take(3usize), tag(" "), alt((take_until("\r\n"), rest))))(part).map_err(
243                    |_: GenericNomError<'a, T>| TokenizerError::from(("status", part)).into(),
244                )?;
245
246            Ok((rem, (code, reason).into()))
247        }
248    }
249}