1use crate::Error;
2#[doc(hidden)]
3pub use tokenizer::Tokenizer;
4
5macro_rules! create_status_codes {
6 ($($name:ident => $code:expr),*) => {
7
8 #[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 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}