msf_sdp/
time.rs

1//! Time description.
2
3use std::{
4    fmt::{self, Display, Formatter},
5    ops::{Deref, DerefMut},
6    str::FromStr,
7};
8
9use str_reader::StringReader;
10
11use crate::{
12    parser::{FromSessionDescriptionLines, SessionDescriptionLines},
13    ParseError,
14};
15
16/// Time description.
17#[derive(Clone)]
18pub struct TimeDescription {
19    start: u64,
20    stop: u64,
21    repeat_times: Vec<RepeatTime>,
22}
23
24impl TimeDescription {
25    /// Create a new time description.
26    #[inline]
27    pub fn new<T>(start: u64, stop: u64, repeat_times: T) -> Self
28    where
29        T: Into<Vec<RepeatTime>>,
30    {
31        Self {
32            start,
33            stop,
34            repeat_times: repeat_times.into(),
35        }
36    }
37
38    /// Get the start time.
39    #[inline]
40    pub fn start(&self) -> u64 {
41        self.start
42    }
43
44    /// Get the stop time.
45    #[inline]
46    pub fn stop(&self) -> u64 {
47        self.stop
48    }
49
50    /// Get the repeat times.
51    #[inline]
52    pub fn repeat_times(&self) -> &[RepeatTime] {
53        &self.repeat_times
54    }
55}
56
57impl Default for TimeDescription {
58    #[inline]
59    fn default() -> Self {
60        Self::new(0, 0, Vec::new())
61    }
62}
63
64impl Display for TimeDescription {
65    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
66        write!(f, "t={} {}\r\n", self.start, self.stop)?;
67
68        for repeat in &self.repeat_times {
69            write!(f, "r={repeat}\r\n")?;
70        }
71
72        Ok(())
73    }
74}
75
76impl FromSessionDescriptionLines for TimeDescription {
77    fn from_sdp_lines(lines: &mut SessionDescriptionLines) -> Result<Self, ParseError> {
78        let (t, v) = lines.current().unwrap();
79
80        debug_assert_eq!(t, 't');
81
82        let mut reader = StringReader::new(v);
83
84        let mut res = Self {
85            start: reader.read_u64()?,
86            stop: reader.read_u64()?,
87            repeat_times: Vec::new(),
88        };
89
90        reader.skip_whitespace();
91
92        if !reader.is_empty() {
93            return Err(ParseError::plain());
94        }
95
96        lines.next()?;
97
98        while let Some((t, _)) = lines.current() {
99            if t == 'r' {
100                let repeat_time = lines
101                    .parse()
102                    .map_err(|err| ParseError::with_cause_and_msg("invalid repeat time", err))?;
103
104                res.repeat_times.push(repeat_time);
105            } else {
106                break;
107            }
108        }
109
110        Ok(res)
111    }
112}
113
114/// Repeat time.
115#[derive(Clone)]
116pub struct RepeatTime {
117    repeat_interval: UnsignedCompactDuration,
118    active_duration: UnsignedCompactDuration,
119    offsets: Vec<UnsignedCompactDuration>,
120}
121
122impl RepeatTime {
123    /// Create a new repeat time.
124    #[inline]
125    pub fn new<T>(
126        repeat_interval: UnsignedCompactDuration,
127        active_duration: UnsignedCompactDuration,
128        offsets: T,
129    ) -> Self
130    where
131        T: Into<Vec<UnsignedCompactDuration>>,
132    {
133        Self {
134            repeat_interval,
135            active_duration,
136            offsets: offsets.into(),
137        }
138    }
139
140    /// Get the repeat interval.
141    #[inline]
142    pub fn repeat_interval(&self) -> UnsignedCompactDuration {
143        self.repeat_interval
144    }
145
146    /// Get the active duration.
147    #[inline]
148    pub fn active_duration(&self) -> UnsignedCompactDuration {
149        self.active_duration
150    }
151
152    /// Get the offsets from the corresponding start time.
153    #[inline]
154    pub fn offsets(&self) -> &[UnsignedCompactDuration] {
155        &self.offsets
156    }
157}
158
159impl Display for RepeatTime {
160    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
161        write!(f, "{} {}", self.repeat_interval, self.active_duration)?;
162
163        for offset in &self.offsets {
164            write!(f, " {offset}")?;
165        }
166
167        Ok(())
168    }
169}
170
171impl FromStr for RepeatTime {
172    type Err = ParseError;
173
174    fn from_str(s: &str) -> Result<Self, Self::Err> {
175        let mut reader = StringReader::new(s);
176
177        let mut res = Self {
178            repeat_interval: reader.parse_word()?,
179            active_duration: reader.parse_word()?,
180            offsets: Vec::new(),
181        };
182
183        loop {
184            reader.skip_whitespace();
185
186            if reader.is_empty() {
187                return Ok(res);
188            }
189
190            res.offsets.push(reader.parse_word()?);
191        }
192    }
193}
194
195/// Timezone adjustment.
196#[derive(Copy, Clone)]
197pub struct TimeZoneAdjustment {
198    adjustment_time: u64,
199    offset: CompactDuration,
200}
201
202impl TimeZoneAdjustment {
203    /// Create a new timezone adjustment.
204    #[inline]
205    pub const fn new(adjustment_time: u64, offset: CompactDuration) -> Self {
206        Self {
207            adjustment_time,
208            offset,
209        }
210    }
211
212    /// Get the NTP time at which the adjustment is supposed to happen.
213    #[inline]
214    pub fn adjustment_time(&self) -> u64 {
215        self.adjustment_time
216    }
217
218    /// Get the adjustment offset.
219    #[inline]
220    pub fn offset(&self) -> CompactDuration {
221        self.offset
222    }
223}
224
225impl Display for TimeZoneAdjustment {
226    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
227        write!(f, "{} {}", self.adjustment_time, self.offset)
228    }
229}
230
231impl FromStr for TimeZoneAdjustment {
232    type Err = ParseError;
233
234    fn from_str(s: &str) -> Result<Self, Self::Err> {
235        let mut reader = StringReader::new(s);
236
237        let res = Self {
238            adjustment_time: reader.read_u64()?,
239            offset: reader.parse_word()?,
240        };
241
242        reader.skip_whitespace();
243
244        if reader.is_empty() {
245            Ok(res)
246        } else {
247            Err(ParseError::plain())
248        }
249    }
250}
251
252/// Collection of timezone adjustments.
253#[derive(Clone)]
254pub struct TimeZoneAdjustments {
255    inner: Vec<TimeZoneAdjustment>,
256}
257
258impl TimeZoneAdjustments {
259    /// Create a new empty collection of timezone adjustments.
260    #[inline]
261    pub const fn empty() -> Self {
262        Self { inner: Vec::new() }
263    }
264}
265
266impl Deref for TimeZoneAdjustments {
267    type Target = Vec<TimeZoneAdjustment>;
268
269    #[inline]
270    fn deref(&self) -> &Self::Target {
271        &self.inner
272    }
273}
274
275impl DerefMut for TimeZoneAdjustments {
276    #[inline]
277    fn deref_mut(&mut self) -> &mut Self::Target {
278        &mut self.inner
279    }
280}
281
282impl Display for TimeZoneAdjustments {
283    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
284        let mut iter = self.inner.iter();
285
286        if let Some(adj) = iter.next() {
287            write!(f, "{adj}")?;
288        }
289
290        for adj in iter {
291            write!(f, " {adj}")?;
292        }
293
294        Ok(())
295    }
296}
297
298impl FromStr for TimeZoneAdjustments {
299    type Err = ParseError;
300
301    fn from_str(s: &str) -> Result<Self, Self::Err> {
302        let mut reader = StringReader::new(s);
303
304        let mut res = Self::empty();
305
306        loop {
307            let adjustment_time = reader.read_u64()?;
308            let offset = reader.parse_word()?;
309
310            res.inner
311                .push(TimeZoneAdjustment::new(adjustment_time, offset));
312
313            reader.skip_whitespace();
314
315            if reader.is_empty() {
316                return Ok(res);
317            }
318        }
319    }
320}
321
322/// Duration that can be expressed in the compact form used in SDP.
323#[derive(Copy, Clone)]
324pub enum CompactDuration {
325    Seconds(i64),
326    Minutes(i64),
327    Hours(i64),
328    Days(i64),
329}
330
331impl CompactDuration {
332    /// Get the duration in seconds.
333    #[inline]
334    pub fn as_secs(&self) -> i64 {
335        match *self {
336            Self::Seconds(n) => n,
337            Self::Minutes(n) => n * 60,
338            Self::Hours(n) => n * 3_600,
339            Self::Days(n) => n * 86_400,
340        }
341    }
342}
343
344impl Display for CompactDuration {
345    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
346        match self {
347            Self::Seconds(v) => write!(f, "{v}"),
348            Self::Minutes(v) => write!(f, "{v}m"),
349            Self::Hours(v) => write!(f, "{v}h"),
350            Self::Days(v) => write!(f, "{v}d"),
351        }
352    }
353}
354
355impl FromStr for CompactDuration {
356    type Err = ParseError;
357
358    fn from_str(s: &str) -> Result<Self, Self::Err> {
359        let mut reader = StringReader::new(s.trim());
360
361        let n = reader
362            .read_until(|c| !c.is_ascii_digit() && c != '-')
363            .parse()?;
364
365        let res = match reader.current_char() {
366            Some('s') => Self::Seconds(n),
367            Some('m') => Self::Minutes(n),
368            Some('h') => Self::Hours(n),
369            Some('d') => Self::Days(n),
370            None => Self::Seconds(n),
371            _ => return Err(ParseError::plain()),
372        };
373
374        if reader.is_empty() {
375            Ok(res)
376        } else {
377            Err(ParseError::plain())
378        }
379    }
380}
381
382/// Unsigned duration that can be expressed in the compact form used in SDP.
383#[derive(Copy, Clone)]
384pub enum UnsignedCompactDuration {
385    Seconds(u64),
386    Minutes(u64),
387    Hours(u64),
388    Days(u64),
389}
390
391impl UnsignedCompactDuration {
392    /// Get the duration in seconds.
393    #[inline]
394    pub fn as_secs(&self) -> u64 {
395        match *self {
396            Self::Seconds(n) => n,
397            Self::Minutes(n) => n * 60,
398            Self::Hours(n) => n * 3_600,
399            Self::Days(n) => n * 86_400,
400        }
401    }
402}
403
404impl Display for UnsignedCompactDuration {
405    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
406        match self {
407            Self::Seconds(v) => write!(f, "{v}"),
408            Self::Minutes(v) => write!(f, "{v}m"),
409            Self::Hours(v) => write!(f, "{v}h"),
410            Self::Days(v) => write!(f, "{v}d"),
411        }
412    }
413}
414
415impl FromStr for UnsignedCompactDuration {
416    type Err = ParseError;
417
418    fn from_str(s: &str) -> Result<Self, Self::Err> {
419        let mut reader = StringReader::new(s.trim());
420
421        let n = reader.read_until(|c| !c.is_ascii_digit()).parse()?;
422
423        let res = match reader.current_char() {
424            Some('s') => Self::Seconds(n),
425            Some('m') => Self::Minutes(n),
426            Some('h') => Self::Hours(n),
427            Some('d') => Self::Days(n),
428            None => Self::Seconds(n),
429            _ => return Err(ParseError::plain()),
430        };
431
432        if reader.is_empty() {
433            Ok(res)
434        } else {
435            Err(ParseError::plain())
436        }
437    }
438}