ftth_rsip/message/
sip_message.rs

1#[doc(hidden)]
2pub use tokenizer::Tokenizer;
3
4use super::{request, response};
5use crate::{common::Version, Error, Headers, Request, Response};
6use std::convert::{TryFrom, TryInto};
7
8/// SipMessage reprsents a generic SIP message, which could either be a [Request](crate::Request)
9/// or a [Response](crate::Response).
10///
11/// A SipMessage can be converted to a `String`, `&str`, or `Bytes`, all using the underlying `Debug`
12/// trait.
13///
14/// A SipMessage can be taken using the `TryFrom` convertions from a `String`, an `&str` or
15/// `Bytes`, all using the underlying Tokenizer. If a convertion fails, the tokenizer will give you
16/// a relevant underlying nom error wrapped in [ftth_rsip::Error](crate::Error).
17///
18/// In order to access specific [headers](crate::headers::untyped), you should take a look on the
19/// [HeadersExt](crate::message::HeadersExt) trait that is automatically implemented for any type
20/// that has implemented the [HasHeaders](crate::message::HasHeaders) trait, which SipMessage
21/// implements it.
22#[derive(Debug, PartialEq, Eq, Clone)]
23pub enum SipMessage {
24    Request(Request),
25    Response(Response),
26}
27
28impl SipMessage {
29    pub fn is_request(&self) -> bool {
30        matches!(self, Self::Request(_))
31    }
32
33    pub fn is_response(&self) -> bool {
34        matches!(self, Self::Response(_))
35    }
36
37    pub fn version(&self) -> &Version {
38        match self {
39            Self::Request(request) => request.version(),
40            Self::Response(response) => response.version(),
41        }
42    }
43
44    pub fn body(&self) -> &Vec<u8> {
45        match self {
46            Self::Request(request) => request.body(),
47            Self::Response(response) => response.body(),
48        }
49    }
50
51    pub fn body_mut(&mut self) -> &mut Vec<u8> {
52        match self {
53            Self::Request(request) => request.body_mut(),
54            Self::Response(response) => response.body_mut(),
55        }
56    }
57}
58
59impl From<Request> for SipMessage {
60    fn from(request: Request) -> Self {
61        Self::Request(request)
62    }
63}
64
65impl From<Response> for SipMessage {
66    fn from(response: Response) -> Self {
67        Self::Response(response)
68    }
69}
70
71impl super::HasHeaders for SipMessage {
72    fn headers(&self) -> &Headers {
73        match self {
74            Self::Request(request) => request.headers(),
75            Self::Response(response) => response.headers(),
76        }
77    }
78
79    fn headers_mut(&mut self) -> &mut Headers {
80        match self {
81            Self::Request(request) => request.headers_mut(),
82            Self::Response(response) => response.headers_mut(),
83        }
84    }
85}
86
87impl std::fmt::Display for SipMessage {
88    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89        match self {
90            Self::Request(request) => write!(f, "{}", request),
91            Self::Response(response) => write!(f, "{}", response),
92        }
93    }
94}
95
96impl TryFrom<&[u8]> for SipMessage {
97    type Error = Error;
98
99    fn try_from(from: &[u8]) -> Result<Self, Self::Error> {
100        Tokenizer::tokenize(from)?.1.try_into()
101    }
102}
103
104impl TryFrom<Vec<u8>> for SipMessage {
105    type Error = Error;
106
107    fn try_from(from: Vec<u8>) -> Result<Self, Self::Error> {
108        Tokenizer::tokenize(&from)?.1.try_into()
109    }
110}
111
112impl TryFrom<&str> for SipMessage {
113    type Error = Error;
114
115    fn try_from(from: &str) -> Result<Self, Self::Error> {
116        Tokenizer::tokenize(from.as_bytes())?.1.try_into()
117    }
118}
119
120impl TryFrom<String> for SipMessage {
121    type Error = Error;
122
123    fn try_from(from: String) -> Result<Self, Self::Error> {
124        Tokenizer::tokenize(from.as_bytes())?.1.try_into()
125    }
126}
127
128impl TryFrom<bytes::Bytes> for SipMessage {
129    type Error = Error;
130
131    fn try_from(from: bytes::Bytes) -> Result<Self, Self::Error> {
132        Tokenizer::tokenize(&from)?.1.try_into()
133    }
134}
135
136impl From<SipMessage> for String {
137    fn from(msg: SipMessage) -> Self {
138        msg.to_string()
139    }
140}
141
142impl From<SipMessage> for Vec<u8> {
143    fn from(msg: SipMessage) -> Self {
144        msg.to_string().into_bytes()
145    }
146}
147
148impl From<SipMessage> for bytes::Bytes {
149    fn from(msg: SipMessage) -> Self {
150        bytes::Bytes::from(msg.to_string())
151    }
152}
153
154#[doc(hidden)]
155pub mod tokenizer {
156    use super::{request, response, SipMessage};
157    use crate::{Error, IResult};
158    use std::convert::TryInto;
159
160    impl<'a> TryInto<SipMessage> for Tokenizer<'a> {
161        type Error = Error;
162
163        fn try_into(self) -> Result<SipMessage, Error> {
164            match self {
165                Tokenizer::Request(tokenizer) => Ok(SipMessage::Request(tokenizer.try_into()?)),
166                Tokenizer::Response(tokenizer) => Ok(SipMessage::Response(tokenizer.try_into()?)),
167            }
168        }
169    }
170
171    #[derive(Debug, PartialEq, Eq)]
172    pub enum Tokenizer<'a> {
173        Request(request::Tokenizer<'a>),
174        Response(response::Tokenizer<'a>),
175    }
176
177    impl<'a> From<request::Tokenizer<'a>> for Tokenizer<'a> {
178        fn from(tokenizer: request::Tokenizer<'a>) -> Self {
179            Self::Request(tokenizer)
180        }
181    }
182
183    impl<'a> From<response::Tokenizer<'a>> for Tokenizer<'a> {
184        fn from(tokenizer: response::Tokenizer<'a>) -> Self {
185            Self::Response(tokenizer)
186        }
187    }
188
189    impl<'a> Tokenizer<'a> {
190        pub fn tokenize(part: &'a [u8]) -> IResult<Self> {
191            use nom::{branch::alt, combinator::map};
192
193            let (_, message) = alt((
194                map(response::Tokenizer::tokenize, |r| r.into()),
195                map(request::Tokenizer::tokenize, |r| r.into()),
196            ))(part)?;
197
198            Ok((&[], message))
199        }
200    }
201}