sdp_nom/
lines.rs

1//! Lines that don't start with `a=`
2#![allow(dead_code)]
3
4use derive_into_owned::IntoOwned;
5use enum_as_inner::EnumAsInner;
6use nom::{
7    branch::alt,
8    bytes::complete::{tag, take_while},
9    combinator::map,
10    sequence::{preceded, separated_pair, tuple},
11    IResult,
12};
13
14use std::borrow::Cow;
15
16use self::{
17    bandwidth::*, connection::*, email::*, media::*, origin::*, phone_number::*,
18    session_information::*, session_name::*, timing::*, uri::*, version::*,
19};
20
21/// Session Line
22#[derive(Clone, IntoOwned, EnumAsInner, PartialEq, Eq)]
23#[cfg_attr(feature = "debug", derive(Debug))]
24#[cfg_attr(
25    feature = "serde",
26    derive(serde::Serialize, serde::Deserialize),
27    serde(rename_all = "camelCase")
28)]
29#[non_exhaustive]
30pub enum SessionLine<'a> {
31    /// `v=0`
32    Version(Version),
33
34    /// `s=-`
35    Name(SessionName<'a>),
36
37    /// `t=0 0`
38    Timing(Timing),
39
40    /// `o=- 20518 0 IN IP4 203.0.113.1`
41    Origin(Origin<'a>),
42
43    /// `b=AS:1024`
44    BandWidth(BandWidth),
45
46    /// `u=`
47    Uri(Uri<'a>),
48
49    /// `p=0118 999 881 999 119 7253`
50    PhoneNumber(PhoneNumber<'a>),
51
52    /// "e=email@example.com"
53    EmailAddress(EmailAddress<'a>),
54
55    /// `c=IN IP4 10.23.42.137`
56    Connection(Connection),
57
58    Description(SessionInformation<'a>),
59
60    /// `m=video 51744 RTP/AVP 126 97 98 34 31
61    Media(Media<'a>),
62}
63
64pub fn session_line(input: &str) -> IResult<&str, SessionLine> {
65    alt((
66        // two levels of `alt` because it's not implemented for such large tuples
67        map(version_line, SessionLine::Version),
68        map(name_line, SessionLine::Name),
69        map(description_line, SessionLine::Description),
70        map(bandwidth_line, SessionLine::BandWidth),
71        map(uri_line, SessionLine::Uri),
72        map(timing_line, SessionLine::Timing),
73        map(phone_number_line, SessionLine::PhoneNumber),
74        map(email_address_line, SessionLine::EmailAddress),
75        map(origin_line, SessionLine::Origin),
76        map(connection_line, SessionLine::Connection),
77        map(media_line, SessionLine::Media),
78    ))(input)
79}
80
81pub mod connection;
82pub mod media;
83pub mod origin;
84
85#[cfg(test)]
86use crate::assert_line;
87use crate::parsers::*;
88
89pub mod version {
90    use super::*;
91
92    #[derive(Clone, IntoOwned, PartialEq, Eq)]
93    #[cfg_attr(feature = "debug", derive(Debug))]
94    #[cfg_attr(
95        feature = "serde",
96        derive(serde::Serialize, serde::Deserialize),
97        serde(rename_all = "camelCase")
98    )]
99    pub struct Version(pub u32);
100
101    /// "v=0"
102    pub fn version_line(input: &str) -> IResult<&str, Version> {
103        preceded(tag("v="), map(wsf(read_number), Version))(input)
104    }
105
106    #[test]
107    fn test_version_line() {
108        assert_line!(version_line, "v=0", Version(0), print);
109        assert_line!(version_line, "v= 0");
110    }
111}
112
113pub mod session_name {
114    use super::*;
115
116    /// `s=somename`
117    ///
118    /// <https://tools.ietf.org/html/rfc4566#section-5.3>
119    #[derive(Clone, IntoOwned, PartialEq, Eq)]
120    #[cfg_attr(feature = "debug", derive(Debug))]
121    #[cfg_attr(
122        feature = "serde",
123        derive(serde::Serialize, serde::Deserialize),
124        serde(rename_all = "camelCase")
125    )]
126    pub struct SessionName<'a>(pub Cow<'a, str>);
127
128    /// "s=somename"
129    pub fn name_line(input: &str) -> IResult<&str, SessionName> {
130        line("s=", map(cowify(wsf(read_everything)), SessionName))(input)
131    }
132
133    #[test]
134    fn test_name_line() {
135        assert_line!(name_line, "s=", SessionName("".into()), print);
136        assert_line!(
137            name_line,
138            "s=testname",
139            SessionName("testname".into()),
140            print
141        );
142        assert_line!(name_line, "s= testname", SessionName("testname".into()));
143        assert_line!(name_line, "s=testname ", SessionName("testname".into()));
144        assert_line!(name_line, "s=test name ", SessionName("test name".into()));
145    }
146}
147
148pub mod session_information {
149    use super::*;
150
151    /// `i=<session description>`
152    #[derive(Clone, IntoOwned, PartialEq, Eq)]
153    #[cfg_attr(feature = "debug", derive(Debug))]
154    #[cfg_attr(
155        feature = "serde",
156        derive(serde::Serialize, serde::Deserialize),
157        serde(rename_all = "camelCase")
158    )]
159    pub struct SessionInformation<'a>(pub Cow<'a, str>);
160
161    /// SessionInformation "i=description"
162    pub fn description_line(input: &str) -> IResult<&str, SessionInformation> {
163        line("i=", map(cowify(wsf(read_everything)), SessionInformation))(input)
164    }
165
166    #[test]
167    fn test_description_line() {
168        assert_line!(
169            description_line,
170            "i=test description",
171            SessionInformation("test description".into()),
172            print
173        );
174        assert_line!(
175            description_line,
176            "i=test description ",
177            SessionInformation("test description".into())
178        );
179    }
180}
181
182pub mod uri {
183    use super::*;
184    /// Uri `u=<uri>`
185    #[derive(Clone, IntoOwned, PartialEq, Eq)]
186    #[cfg_attr(feature = "debug", derive(Debug))]
187    #[cfg_attr(
188        feature = "serde",
189        derive(serde::Serialize, serde::Deserialize),
190        serde(rename_all = "camelCase")
191    )]
192    pub struct Uri<'a>(pub Cow<'a, str>);
193
194    /// "i=description"
195    pub fn uri_line(input: &str) -> IResult<&str, Uri> {
196        line("u=", map(cowify(read_string), Uri))(input)
197    }
198
199    #[test]
200    fn test_uri_line() {
201        assert_line!(
202            uri_line,
203            "u=https://parse-my.sdp",
204            Uri("https://parse-my.sdp".into())
205        );
206    }
207}
208
209pub mod email {
210
211    use super::*;
212
213    /// Email `e=<email-address>`
214    #[derive(Clone, IntoOwned, PartialEq, Eq)]
215    #[cfg_attr(feature = "debug", derive(Debug))]
216    #[cfg_attr(
217        feature = "serde",
218        derive(serde::Serialize, serde::Deserialize),
219        serde(rename_all = "camelCase")
220    )]
221    pub struct EmailAddress<'a>(pub Cow<'a, str>);
222
223    /// "e=email@example.com"
224    pub fn email_address_line(input: &str) -> IResult<&str, EmailAddress> {
225        line("e=", wsf(map(cowify(read_string), EmailAddress)))(input)
226    }
227
228    #[test]
229    fn test_email_address_line() {
230        assert_line!(
231            email_address_line,
232            "e=test@example.com",
233            EmailAddress("test@example.com".into()),
234            print
235        );
236    }
237}
238
239// ////////////////////////
240
241pub mod phone_number {
242    use super::*;
243    /// Email `p=<phone-number>`
244    #[derive(Clone, IntoOwned, PartialEq, Eq)]
245    #[cfg_attr(feature = "debug", derive(Debug))]
246    #[cfg_attr(
247        feature = "serde",
248        derive(serde::Serialize, serde::Deserialize),
249        serde(rename_all = "camelCase")
250    )]
251    pub struct PhoneNumber<'a>(pub Cow<'a, str>);
252
253    /// "i=description"
254    pub fn phone_number_line(input: &str) -> IResult<&str, PhoneNumber> {
255        line("p=", map(cowify(take_while(|_| true)), PhoneNumber))(input)
256    }
257
258    #[test]
259    fn test_phone_number_line() {
260        assert_line!(
261            phone_number_line,
262            "p=0118 999 881 999 119 7253",
263            PhoneNumber("0118 999 881 999 119 7253".into()),
264            print
265        );
266    }
267}
268
269pub mod timing {
270    use super::*;
271
272    /// `t=0 0`
273    #[derive(Clone, PartialEq, Eq)]
274    #[cfg_attr(feature = "debug", derive(Debug))]
275    #[cfg_attr(
276        feature = "serde",
277        derive(serde::Serialize, serde::Deserialize),
278        serde(rename_all = "camelCase")
279    )]
280    pub struct Timing {
281        pub start: u32,
282        pub stop: u32,
283    }
284
285    /// "t=0 0"
286    pub fn timing_line(input: &str) -> IResult<&str, Timing> {
287        line(
288            "t=",
289            wsf(map(
290                tuple((wsf(read_number), wsf(read_number))),
291                |(start, stop)| Timing { start, stop },
292            )),
293        )(input)
294    }
295
296    #[test]
297    #[rustfmt::skip]
298    fn test_timing_line() {
299        assert_line!(timing_line,"t=0 1", Timing { start: 0, stop: 1 }, print);
300        assert_line!(timing_line,"t=  2 3 ", Timing { start: 2, stop: 3 });
301        assert_line!(timing_line,"t=  2  3 ", Timing { start: 2, stop: 3 });
302        assert_line!(timing_line,"t=23 42", Timing { start: 23, stop: 42 }, print);
303    }
304}
305
306pub mod bandwidth {
307    use super::*;
308    #[derive(Clone, PartialEq, Eq)]
309    #[cfg_attr(feature = "debug", derive(Debug))]
310    #[cfg_attr(
311        feature = "serde",
312        derive(serde::Serialize, serde::Deserialize),
313        serde(rename_all = "camelCase")
314    )]
315    #[non_exhaustive]
316    pub enum BandWidthType {
317        TIAS,
318        AS,
319        CT,
320        RR,
321        RS,
322    }
323    // TIAS|AS|CT|RR|RS
324    pub fn bandwidth_type(input: &str) -> IResult<&str, BandWidthType> {
325        alt((
326            map(tag("TIAS"), |_| BandWidthType::TIAS),
327            map(tag("AS"), |_| BandWidthType::AS),
328            map(tag("CT"), |_| BandWidthType::CT),
329            map(tag("RR"), |_| BandWidthType::RR),
330            map(tag("RS"), |_| BandWidthType::RS),
331        ))(input)
332    }
333
334    #[derive(Clone, PartialEq, Eq)]
335    #[cfg_attr(feature = "debug", derive(Debug))]
336    #[cfg_attr(
337        feature = "serde",
338        derive(serde::Serialize, serde::Deserialize),
339        serde(rename_all = "camelCase")
340    )]
341    /// "b=AS:1024"
342    pub struct BandWidth {
343        pub r#type: BandWidthType,
344        pub limit: u32,
345    }
346
347    /// "b=AS:1024"
348    pub fn bandwidth_line(input: &str) -> IResult<&str, BandWidth> {
349        line("b=", bandwidth)(input)
350    }
351
352    /// "AS:1024"
353    pub fn bandwidth(input: &str) -> IResult<&str, BandWidth> {
354        map(
355            separated_pair(bandwidth_type, tag(":"), read_number),
356            |(r#type, limit)| (BandWidth { r#type, limit }),
357        )(input)
358    }
359
360    #[test]
361    #[rustfmt::skip]
362    fn test_bandwidth_line() {
363        assert_line!(
364            bandwidth_line,"b=AS:30",
365            BandWidth { r#type: BandWidthType::AS, limit: 30 }, print
366        );
367        assert_line!(
368            bandwidth_line,"b=RR:1024",
369            BandWidth { r#type: BandWidthType::RR, limit: 1024 }, print
370        );
371    }
372}
373
374pub mod comment {
375    use super::*;
376
377    pub fn comment_line(input: &str) -> IResult<&str, &str> {
378        preceded(tag(";"), wsf(read_everything))(input)
379    }
380
381    #[test]
382    fn test_read_comment() {
383        assert_line!(
384            comment_line,
385            "; this should not be part of the document",
386            "this should not be part of the document"
387        )
388    }
389}