msf_sdp/
attribute.rs

1//! Session and media attributes.
2
3use std::{
4    borrow::Cow,
5    convert::Infallible,
6    fmt::{self, Display, Formatter},
7    ops::{Deref, DerefMut},
8    str::FromStr,
9};
10
11use str_reader::StringReader;
12
13use crate::ParseError;
14
15/// SDP attribute.
16#[derive(Debug, Clone)]
17pub struct Attribute {
18    name: String,
19    value: Option<String>,
20}
21
22impl Attribute {
23    /// Create a new flag-attribute (i.e. an attribute without a value).
24    #[inline]
25    pub fn new_flag<N>(name: N) -> Self
26    where
27        N: ToString,
28    {
29        Self {
30            name: name.to_string(),
31            value: None,
32        }
33    }
34
35    /// Create a new attribute.
36    #[inline]
37    pub fn new_attribute<N, V>(name: N, value: V) -> Self
38    where
39        N: ToString,
40        V: ToString,
41    {
42        Self {
43            name: name.to_string(),
44            value: Some(value.to_string()),
45        }
46    }
47
48    /// Get attribute name.
49    #[inline]
50    pub fn name(&self) -> &str {
51        &self.name
52    }
53
54    /// Get attribute value (if any).
55    #[inline]
56    pub fn value(&self) -> Option<&str> {
57        self.value.as_deref()
58    }
59}
60
61impl Display for Attribute {
62    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
63        f.write_str(&self.name)?;
64
65        if let Some(v) = self.value.as_ref() {
66            write!(f, ":{v}")?;
67        }
68
69        Ok(())
70    }
71}
72
73impl FromStr for Attribute {
74    type Err = Infallible;
75
76    fn from_str(s: &str) -> Result<Self, Self::Err> {
77        let (name, value) = if let Some(colon) = s.find(':') {
78            let (name, rest) = s.split_at(colon);
79
80            let value = &rest[1..];
81
82            (name, Some(value))
83        } else {
84            (s, None)
85        };
86
87        let res = Self {
88            name: name.to_string(),
89            value: value.map(|v| v.to_string()),
90        };
91
92        Ok(res)
93    }
94}
95
96/// Collection of attributes.
97#[derive(Clone)]
98pub struct Attributes {
99    inner: Vec<Attribute>,
100}
101
102impl Attributes {
103    /// Create a new collection of attributes.
104    #[inline]
105    pub const fn new() -> Self {
106        Self { inner: Vec::new() }
107    }
108
109    /// Find the first attribute matching a given predicate.
110    #[inline]
111    pub fn find<F>(&self, predicate: F) -> Option<&Attribute>
112    where
113        F: FnMut(&Attribute) -> bool,
114    {
115        self.find_all(predicate).next()
116    }
117
118    /// Find all attributes matching a given predicate.
119    #[inline]
120    pub fn find_all<F>(&self, predicate: F) -> PredicateMatchingIter<'_, F>
121    where
122        F: FnMut(&Attribute) -> bool,
123    {
124        PredicateMatchingIter::new(self, predicate)
125    }
126
127    /// Get the first attribute matching a given name (case sensitive).
128    #[inline]
129    pub fn get(&self, name: &str) -> Option<&Attribute> {
130        self.find(|a| a.name() == name)
131    }
132
133    /// Get all attributes matching a given name (case sensitive).
134    #[inline]
135    pub fn get_all<'a>(&'a self, name: &'a str) -> NameMatchingIter<'a> {
136        NameMatchingIter::new(self, name)
137    }
138
139    /// Get value of the first attribute matching a given name (case
140    /// sensitive).
141    #[inline]
142    pub fn get_value(&self, name: &str) -> Option<&str> {
143        self.get(name).and_then(|a| a.value())
144    }
145
146    /// Check if there is an attribute matching a given name (case sensitive).
147    #[inline]
148    pub fn contains(&self, name: &str) -> bool {
149        self.get(name).is_some()
150    }
151}
152
153impl Default for Attributes {
154    #[inline]
155    fn default() -> Self {
156        Self::new()
157    }
158}
159
160impl Deref for Attributes {
161    type Target = Vec<Attribute>;
162
163    #[inline]
164    fn deref(&self) -> &Self::Target {
165        &self.inner
166    }
167}
168
169impl DerefMut for Attributes {
170    #[inline]
171    fn deref_mut(&mut self) -> &mut Self::Target {
172        &mut self.inner
173    }
174}
175
176/// Iterator over attributes matching a given predicate.
177pub struct PredicateMatchingIter<'a, F> {
178    predicate: F,
179    attributes: std::slice::Iter<'a, Attribute>,
180}
181
182impl<'a, F> PredicateMatchingIter<'a, F> {
183    /// Create a new iterator.
184    fn new(attributes: &'a [Attribute], predicate: F) -> Self {
185        Self {
186            predicate,
187            attributes: attributes.iter(),
188        }
189    }
190}
191
192impl<'a, F> Iterator for PredicateMatchingIter<'a, F>
193where
194    F: FnMut(&Attribute) -> bool,
195{
196    type Item = &'a Attribute;
197
198    #[inline]
199    fn next(&mut self) -> Option<Self::Item> {
200        loop {
201            if let Some(item) = self.attributes.next() {
202                if (self.predicate)(item) {
203                    return Some(item);
204                }
205            } else {
206                return None;
207            }
208        }
209    }
210}
211
212/// Iterator over attributes matching a given name.
213pub struct NameMatchingIter<'a> {
214    name: &'a str,
215    attributes: std::slice::Iter<'a, Attribute>,
216}
217
218impl<'a> NameMatchingIter<'a> {
219    /// Create a new iterator.
220    fn new(attributes: &'a [Attribute], name: &'a str) -> Self {
221        Self {
222            name,
223            attributes: attributes.iter(),
224        }
225    }
226}
227
228impl<'a> Iterator for NameMatchingIter<'a> {
229    type Item = &'a Attribute;
230
231    #[inline]
232    fn next(&mut self) -> Option<Self::Item> {
233        loop {
234            if let Some(item) = self.attributes.next() {
235                if item.name() == self.name {
236                    return Some(item);
237                }
238            } else {
239                return None;
240            }
241        }
242    }
243}
244
245/// Mapping from an RTP payload type to an actual encoding.
246#[derive(Clone)]
247pub struct RTPMap<'a> {
248    payload_type: u8,
249    encoding_name: &'a str,
250    clock_rate: u32,
251    encoding_parameters: Option<Cow<'a, str>>,
252}
253
254impl<'a> RTPMap<'a> {
255    /// Create a new rtpmap attribute value.
256    #[inline]
257    pub const fn new(payload_type: u8, encoding_name: &'a str, clock_rate: u32) -> Self {
258        Self {
259            payload_type,
260            encoding_name,
261            clock_rate,
262            encoding_parameters: None,
263        }
264    }
265}
266
267impl RTPMap<'_> {
268    /// Set the encoding parameters.
269    #[inline]
270    pub fn with_encoding_parameters<T>(mut self, params: T) -> Self
271    where
272        T: ToString,
273    {
274        self.encoding_parameters = Some(Cow::Owned(params.to_string()));
275        self
276    }
277
278    /// Get the payload type.
279    #[inline]
280    pub fn payload_type(&self) -> u8 {
281        self.payload_type
282    }
283
284    /// Get name of the encoding.
285    #[inline]
286    pub fn encoding_name(&self) -> &str {
287        self.encoding_name
288    }
289
290    /// Get the clock rate.
291    #[inline]
292    pub fn clock_rate(&self) -> u32 {
293        self.clock_rate
294    }
295
296    /// Get the encoding parameters (if specified).
297    #[inline]
298    pub fn encoding_parameters(&self) -> Option<&str> {
299        self.encoding_parameters.as_deref()
300    }
301}
302
303impl Display for RTPMap<'_> {
304    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
305        write!(
306            f,
307            "{} {}/{}",
308            self.payload_type, self.encoding_name, self.clock_rate
309        )?;
310
311        if let Some(params) = self.encoding_parameters.as_ref() {
312            write!(f, "/{params}")?;
313        }
314
315        Ok(())
316    }
317}
318
319impl<'a> TryFrom<&'a str> for RTPMap<'a> {
320    type Error = ParseError;
321
322    fn try_from(s: &'a str) -> Result<Self, Self::Error> {
323        let mut reader = StringReader::new(s);
324
325        let payload_type = reader.read_u8()?;
326
327        reader.skip_whitespace();
328
329        let encoding_name = reader.read_until(|c| c == '/');
330
331        reader.match_char('/')?;
332
333        let clock_rate = reader.read_until(|c| c == '/').parse()?;
334
335        let encoding_parameters = if reader.is_empty() {
336            None
337        } else {
338            Some(Cow::Borrowed(&reader.as_str()[1..]))
339        };
340
341        let res = Self {
342            payload_type,
343            encoding_name,
344            clock_rate,
345            encoding_parameters,
346        };
347
348        Ok(res)
349    }
350}
351
352/// Format-specific parameters.
353#[derive(Clone)]
354pub struct FormatParameters<'a> {
355    format: Cow<'a, str>,
356    params: Cow<'a, str>,
357}
358
359impl FormatParameters<'_> {
360    /// Create a new fmtp attribute value.
361    #[inline]
362    pub fn new<T, U>(format: T, parameters: U) -> Self
363    where
364        T: ToString,
365        U: ToString,
366    {
367        Self {
368            format: Cow::Owned(format.to_string()),
369            params: Cow::Owned(parameters.to_string()),
370        }
371    }
372
373    /// Get the format.
374    #[inline]
375    pub fn format(&self) -> &str {
376        &self.format
377    }
378
379    /// Get the format parameters.
380    #[inline]
381    pub fn parameters(&self) -> &str {
382        &self.params
383    }
384}
385
386impl Display for FormatParameters<'_> {
387    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
388        write!(f, "{} {}", self.format, self.params)
389    }
390}
391
392impl<'a> From<&'a str> for FormatParameters<'a> {
393    fn from(s: &'a str) -> Self {
394        let s = s.trim();
395
396        let (format, params) = if let Some(space) = s.find(' ') {
397            let (f, r) = s.split_at(space);
398
399            let p = &r[1..];
400
401            (f, p)
402        } else {
403            (s, "")
404        };
405
406        Self {
407            format: Cow::Borrowed(format),
408            params: Cow::Borrowed(params),
409        }
410    }
411}