ezk_sdp_types/
parser.rs

1use crate::{
2    Bandwidth, Connection, Direction, ExtMap, Fingerprint, Fmtp, Group, IceCandidate, IceOptions,
3    IcePassword, IceUsernameFragment, Media, MediaDescription, Origin, Rtcp, RtpMap,
4    SessionDescription, Setup, SrtpCrypto, Ssrc, Time, UnknownAttribute,
5};
6use bytesstr::BytesStr;
7use internal::verbose_error_to_owned;
8use nom::Finish;
9
10#[derive(Debug, thiserror::Error)]
11pub enum ParseSessionDescriptionError {
12    #[error("{0}")]
13    ParseError(nom::error::VerboseError<String>),
14    #[error("message ended unexpectedly")]
15    Incomplete,
16    #[error("message is missing the origin field (o=)")]
17    MissingOrigin,
18    #[error("message is missing the name (s=) field")]
19    MissingName,
20    #[error("message is missing the time (t=) field")]
21    MissingTime,
22}
23
24impl From<nom::error::VerboseError<&str>> for ParseSessionDescriptionError {
25    fn from(value: nom::error::VerboseError<&str>) -> Self {
26        Self::ParseError(verbose_error_to_owned(value))
27    }
28}
29
30#[derive(Default)]
31pub(crate) struct Parser {
32    origin: Option<Origin>,
33    name: Option<BytesStr>,
34    connection: Option<Connection>,
35    bandwidth: Vec<Bandwidth>,
36    time: Option<Time>,
37    direction: Direction,
38    group: Vec<Group>,
39    extmap: Vec<ExtMap>,
40    extmap_allow_mixed: bool,
41    ice_options: IceOptions,
42    ice_lite: bool,
43    ice_ufrag: Option<IceUsernameFragment>,
44    ice_pwd: Option<IcePassword>,
45    setup: Option<Setup>,
46    fingerprint: Vec<Fingerprint>,
47    attributes: Vec<UnknownAttribute>,
48    media_descriptions: Vec<MediaDescription>,
49}
50
51impl Parser {
52    pub(crate) fn parse_line(
53        &mut self,
54        src: &BytesStr,
55        complete_line: &str,
56    ) -> Result<(), ParseSessionDescriptionError> {
57        let line = complete_line
58            .get(2..)
59            .ok_or(ParseSessionDescriptionError::Incomplete)?;
60
61        match complete_line.as_bytes() {
62            [b'v', b'=', b'0'] => {
63                // parsed the version yay!
64            }
65            [b's', b'=', ..] => {
66                self.name = Some(BytesStr::from_parse(src.as_ref(), line));
67            }
68            [b'o', b'=', ..] => {
69                let (_, o) = Origin::parse(src.as_ref(), line).finish()?;
70                self.origin = Some(o);
71            }
72            [b't', b'=', ..] => {
73                let (_, t) = Time::parse(line).finish()?;
74                self.time = Some(t);
75            }
76            [b'c', b'=', ..] => {
77                let (_, c) = Connection::parse(src.as_ref(), line).finish()?;
78
79                if let Some(media_description) = self.media_descriptions.last_mut() {
80                    media_description.connection = Some(c);
81                } else {
82                    self.connection = Some(c);
83                }
84            }
85            [b'b', b'=', ..] => {
86                let (_, b) = Bandwidth::parse(src.as_ref(), line).finish()?;
87
88                if let Some(media_description) = self.media_descriptions.last_mut() {
89                    media_description.bandwidth.push(b);
90                } else {
91                    self.bandwidth.push(b);
92                }
93            }
94            [b'm', b'=', ..] => {
95                let (_, media) = Media::parse(src.as_ref(), line).finish()?;
96
97                self.media_descriptions.push(MediaDescription {
98                    media,
99                    // inherit session direction
100                    connection: None,
101                    bandwidth: vec![],
102                    direction: self.direction,
103                    rtcp: None,
104                    rtcp_mux: false,
105                    mid: None,
106                    rtpmap: vec![],
107                    fmtp: vec![],
108                    ice_ufrag: None,
109                    ice_pwd: None,
110                    ice_candidates: vec![],
111                    ice_end_of_candidates: false,
112                    crypto: vec![],
113                    extmap: vec![],
114                    // inherit extmap allow mixed atr
115                    extmap_allow_mixed: self.extmap_allow_mixed,
116                    ssrc: vec![],
117                    setup: self.setup,
118                    fingerprint: vec![],
119                    attributes: vec![],
120                });
121            }
122            [b'a', b'=', ..] => self.parse_attribute(src, line)?,
123            _ => {}
124        }
125
126        Ok(())
127    }
128
129    fn parse_attribute(
130        &mut self,
131        src: &BytesStr,
132        line: &str,
133    ) -> Result<(), ParseSessionDescriptionError> {
134        if let Some((name, value)) = line.split_once(':') {
135            self.parse_attribute_with_value(src, name, value)?;
136        } else {
137            self.parse_attribute_without_value(src, line);
138        }
139
140        Ok(())
141    }
142
143    fn parse_attribute_with_value(
144        &mut self,
145        src: &BytesStr,
146        name: &str,
147        value: &str,
148    ) -> Result<(), ParseSessionDescriptionError> {
149        match name {
150            "group" => {
151                let (_, group) = Group::parse(src.as_ref(), value).finish()?;
152                self.group.push(group);
153            }
154            "rtcp" => {
155                let (_, rtcp) = Rtcp::parse(src.as_ref(), value).finish()?;
156
157                if let Some(media_description) = self.media_descriptions.last_mut() {
158                    media_description.rtcp = Some(rtcp);
159                }
160
161                // TODO error here?
162            }
163            "mid" => {
164                if let Some(media_description) = self.media_descriptions.last_mut() {
165                    media_description.mid = Some(BytesStr::from_parse(src.as_ref(), value.trim()));
166                }
167
168                // TODO error here ?
169            }
170            "rtpmap" => {
171                let (_, rtpmap) = RtpMap::parse(src.as_ref(), value).finish()?;
172
173                if let Some(media_description) = self.media_descriptions.last_mut() {
174                    media_description.rtpmap.push(rtpmap);
175                }
176
177                // TODO error here ?
178            }
179            "fmtp" => {
180                let (_, fmtp) = Fmtp::parse(src.as_ref(), value).finish()?;
181
182                if let Some(media_description) = self.media_descriptions.last_mut() {
183                    media_description.fmtp.push(fmtp);
184                }
185
186                // TODO error here ?
187            }
188            "ice-lite" => {
189                self.ice_lite = true;
190            }
191            "ice-options" => {
192                let (_, options) = IceOptions::parse(src.as_ref(), value).finish()?;
193                self.ice_options = options;
194            }
195            "ice-ufrag" => {
196                let (_, ufrag) = IceUsernameFragment::parse(src.as_ref(), value).finish()?;
197
198                if let Some(media_description) = self.media_descriptions.last_mut() {
199                    media_description.ice_ufrag = Some(ufrag);
200                } else {
201                    self.ice_ufrag = Some(ufrag);
202                }
203            }
204            "ice-pwd" => {
205                let (_, pwd) = IcePassword::parse(src.as_ref(), value).finish()?;
206
207                if let Some(media_description) = self.media_descriptions.last_mut() {
208                    media_description.ice_pwd = Some(pwd);
209                } else {
210                    self.ice_pwd = Some(pwd);
211                }
212            }
213            "candidate" => {
214                let (_, candidate) = IceCandidate::parse(src.as_ref(), value).finish()?;
215
216                if let Some(media_description) = self.media_descriptions.last_mut() {
217                    media_description.ice_candidates.push(candidate);
218                }
219
220                // TODO error here?
221            }
222            "crypto" => {
223                let (_, crypto) = SrtpCrypto::parse(src.as_ref(), value).finish()?;
224
225                if let Some(media_description) = self.media_descriptions.last_mut() {
226                    media_description.crypto.push(crypto);
227                }
228
229                // TODO error here?
230            }
231            "extmap" => {
232                let (_, extmap) = ExtMap::parse(src.as_ref(), value).finish()?;
233
234                if let Some(media_description) = self.media_descriptions.last_mut() {
235                    media_description.extmap.push(extmap);
236                } else {
237                    self.extmap.push(extmap);
238                }
239            }
240            "ssrc" => {
241                let (_, ssrc) = Ssrc::parse(src.as_ref(), value).finish()?;
242
243                if let Some(media_description) = self.media_descriptions.last_mut() {
244                    media_description.ssrc.push(ssrc);
245                }
246
247                // TODO error here?
248            }
249            "setup" => {
250                let setup = match value {
251                    "active" => Setup::Active,
252                    "passive" => Setup::Passive,
253                    "actpass" => Setup::ActPass,
254                    "holdconn" => Setup::HoldConn,
255                    _ => return Ok(()),
256                };
257
258                if let Some(media_description) = self.media_descriptions.last_mut() {
259                    media_description.setup = Some(setup);
260                } else {
261                    self.setup = Some(setup);
262                }
263                // TODO error here?
264            }
265            "fingerprint" => {
266                let (_, fingerprint) = Fingerprint::parse(src.as_ref(), value).finish()?;
267
268                if let Some(media_description) = self.media_descriptions.last_mut() {
269                    media_description.fingerprint.push(fingerprint);
270                } else {
271                    self.fingerprint.push(fingerprint)
272                }
273            }
274            _ => {
275                let attr = UnknownAttribute {
276                    name: src.slice_ref(name),
277                    value: Some(src.slice_ref(value)),
278                };
279
280                if let Some(media_description) = self.media_descriptions.last_mut() {
281                    media_description.attributes.push(attr);
282                } else {
283                    self.attributes.push(attr);
284                }
285            }
286        }
287
288        Ok(())
289    }
290
291    fn parse_attribute_without_value(&mut self, src: &BytesStr, line: &str) {
292        let direction = if let Some(media_description) = self.media_descriptions.last_mut() {
293            &mut media_description.direction
294        } else {
295            &mut self.direction
296        };
297
298        match line {
299            "sendrecv" => *direction = Direction::SendRecv,
300            "recvonly" => *direction = Direction::RecvOnly,
301            "sendonly" => *direction = Direction::SendOnly,
302            "inactive" => *direction = Direction::Inactive,
303            "extmap-allow-mixed" => {
304                if let Some(media_description) = self.media_descriptions.last_mut() {
305                    media_description.extmap_allow_mixed = true;
306                } else {
307                    self.extmap_allow_mixed = true;
308                }
309            }
310            "rtcp-mux" => {
311                if let Some(media_description) = self.media_descriptions.last_mut() {
312                    media_description.rtcp_mux = true;
313                }
314            }
315            "end-of-candidates" => {
316                if let Some(media_description) = self.media_descriptions.last_mut() {
317                    media_description.ice_end_of_candidates = true;
318                }
319
320                // TODO error here?
321            }
322            _ => {
323                let attr = UnknownAttribute {
324                    name: src.slice_ref(line),
325                    value: None,
326                };
327
328                if let Some(media_description) = self.media_descriptions.last_mut() {
329                    media_description.attributes.push(attr);
330                } else {
331                    self.attributes.push(attr);
332                }
333            }
334        }
335    }
336
337    pub(crate) fn finish(self) -> Result<SessionDescription, ParseSessionDescriptionError> {
338        Ok(SessionDescription {
339            origin: self
340                .origin
341                .ok_or(ParseSessionDescriptionError::MissingOrigin)?,
342            name: self.name.ok_or(ParseSessionDescriptionError::MissingName)?,
343            connection: self.connection,
344            bandwidth: self.bandwidth,
345            time: self.time.ok_or(ParseSessionDescriptionError::MissingTime)?,
346            direction: self.direction,
347            group: self.group,
348            extmap: self.extmap,
349            extmap_allow_mixed: self.extmap_allow_mixed,
350            ice_lite: self.ice_lite,
351            ice_options: self.ice_options,
352            ice_ufrag: self.ice_ufrag,
353            ice_pwd: self.ice_pwd,
354            setup: self.setup,
355            fingerprint: self.fingerprint,
356            attributes: self.attributes,
357            media_descriptions: self.media_descriptions,
358        })
359    }
360}