1use super::*;
6
7use super::parser_helpers::split_once;
8use std::fmt;
9
10#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
12#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
13pub enum Range {
14 Npt(NptRange),
16 Smpte(SmpteRange),
18 Utc(UtcRange),
20 Other(String),
22}
23
24impl fmt::Display for Range {
25 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26 match self {
27 Range::Npt(r) => <NptRange as fmt::Display>::fmt(r, f),
28 Range::Smpte(r) => <SmpteRange as fmt::Display>::fmt(r, f),
29 Range::Utc(r) => <UtcRange as fmt::Display>::fmt(r, f),
30 Range::Other(r) => <String as fmt::Display>::fmt(r, f),
31 }
32 }
33}
34
35impl std::str::FromStr for Range {
36 type Err = HeaderParseError;
37
38 fn from_str(s: &str) -> Result<Self, HeaderParseError> {
39 if s.starts_with("npt") {
40 Ok(Range::Npt(s.parse()?))
41 } else if s.starts_with("clock") {
42 Ok(Range::Utc(s.parse()?))
43 } else if s.starts_with("smpte") {
44 Ok(Range::Smpte(s.parse()?))
45 } else {
46 Ok(Range::Other(s.into()))
47 }
48 }
49}
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
53#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
54pub enum NptRange {
55 Empty,
57 From(NptTime),
59 FromTo(NptTime, NptTime),
61 To(NptTime),
63}
64
65impl fmt::Display for NptRange {
66 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
67 match self {
68 NptRange::Empty => fmt.write_str("npt"),
69 NptRange::From(f) => write!(fmt, "npt={f}-"),
70 NptRange::FromTo(f, t) => write!(fmt, "npt={f}-{t}"),
71 NptRange::To(t) => write!(fmt, "npt=-{t}"),
72 }
73 }
74}
75
76impl std::str::FromStr for NptRange {
77 type Err = HeaderParseError;
78
79 fn from_str(s: &str) -> Result<Self, HeaderParseError> {
80 let s = s.strip_prefix("npt").ok_or(HeaderParseError)?;
81
82 if s.is_empty() {
83 return Ok(NptRange::Empty);
84 }
85
86 let s = s.strip_prefix('=').ok_or(HeaderParseError)?;
87
88 let (from, to) = split_once(s, '-').ok_or(HeaderParseError)?;
89 let from = if from.is_empty() { None } else { Some(from) };
90 let to = if to.is_empty() { None } else { Some(to) };
91
92 let from = from
93 .map(|s| s.parse::<NptTime>().map_err(|_| HeaderParseError))
94 .transpose()?;
95 let to = to
96 .map(|s| s.parse::<NptTime>().map_err(|_| HeaderParseError))
97 .transpose()?;
98
99 match (from, to) {
100 (Some(from), Some(to)) => Ok(NptRange::FromTo(from, to)),
101 (None, Some(to)) => Ok(NptRange::To(to)),
102 (Some(from), None) => Ok(NptRange::From(from)),
103 (None, None) => Err(HeaderParseError),
104 }
105 }
106}
107
108#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
110#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
111pub enum NptTime {
112 Now,
114 Seconds(u64, Option<u32>),
116 Hms(u64, u8, u8, Option<u32>),
118}
119
120impl fmt::Display for NptTime {
121 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
122 match self {
123 NptTime::Now => fmt.write_str("now"),
124 NptTime::Seconds(seconds, None) => write!(fmt, "{seconds}"),
125 NptTime::Seconds(seconds, Some(nanoseconds)) => {
126 write!(fmt, "{seconds}.{nanoseconds:09}")
127 }
128 NptTime::Hms(hours, minutes, seconds, None) => {
129 write!(fmt, "{hours:02}:{minutes:02}:{seconds:02}")
130 }
131 NptTime::Hms(hours, minutes, seconds, Some(nanoseconds)) => {
132 write!(fmt, "{hours:02}:{minutes:02}:{seconds:02}.{nanoseconds:09}")
133 }
134 }
135 }
136}
137
138impl std::str::FromStr for NptTime {
139 type Err = HeaderParseError;
140
141 fn from_str(s: &str) -> Result<Self, HeaderParseError> {
142 if s == "now" {
143 return Ok(NptTime::Now);
144 }
145
146 match split_once(s, ':') {
147 None => match split_once(s, '.') {
148 None => {
149 let seconds = s.parse::<u64>().map_err(|_| HeaderParseError)?;
150 Ok(NptTime::Seconds(seconds, None))
151 }
152 Some((seconds, subseconds)) => {
153 let seconds = seconds.parse::<u64>().map_err(|_| HeaderParseError)?;
154 let digits = subseconds.len();
155 if digits > 9 || digits == 0 {
156 return Err(HeaderParseError);
157 }
158 let subseconds = subseconds.parse::<u32>().map_err(|_| HeaderParseError)?;
159
160 let nanoseconds = subseconds * u32::pow(10, 9 - digits as u32);
161
162 Ok(NptTime::Seconds(seconds, Some(nanoseconds)))
163 }
164 },
165 Some((hours, s)) => {
166 let hours = hours.parse::<u64>().map_err(|_| HeaderParseError)?;
167 let mut it = s.split(':');
168 let minutes = it
169 .next()
170 .and_then(|s| s.parse::<u8>().ok())
171 .ok_or(HeaderParseError)?;
172 let seconds = it.next().ok_or(HeaderParseError)?;
173
174 if let Some((seconds, subseconds)) = split_once(seconds, '.') {
175 let seconds = seconds.parse::<u8>().map_err(|_| HeaderParseError)?;
176 let digits = subseconds.len();
177 if digits > 9 || digits == 0 {
178 return Err(HeaderParseError);
179 }
180 let subseconds = subseconds.parse::<u32>().map_err(|_| HeaderParseError)?;
181
182 let nanoseconds = subseconds * u32::pow(10, 9 - digits as u32);
183
184 Ok(NptTime::Hms(hours, minutes, seconds, Some(nanoseconds)))
185 } else {
186 let seconds = seconds.parse::<u8>().map_err(|_| HeaderParseError)?;
187
188 Ok(NptTime::Hms(hours, minutes, seconds, None))
189 }
190 }
191 }
192 }
193}
194
195#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
197#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
198pub enum SmpteRange {
199 Empty(SmpteType),
201 From(SmpteType, SmpteTime),
203 FromTo(SmpteType, SmpteTime, SmpteTime),
205 To(SmpteType, SmpteTime),
207}
208
209impl fmt::Display for SmpteRange {
210 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
211 match self {
212 SmpteRange::Empty(ty) => write!(fmt, "{ty}"),
213 SmpteRange::From(ty, f) => write!(fmt, "{ty}={f}-"),
214 SmpteRange::FromTo(ty, f, t) => write!(fmt, "{ty}={f}-{t}"),
215 SmpteRange::To(ty, t) => write!(fmt, "{ty}=-{t}"),
216 }
217 }
218}
219
220impl std::str::FromStr for SmpteRange {
221 type Err = HeaderParseError;
222
223 fn from_str(s: &str) -> Result<Self, HeaderParseError> {
224 if let Some((ty, range)) = split_once(s, '=') {
225 let ty = ty.parse()?;
226
227 if range.is_empty() {
228 return Ok(SmpteRange::Empty(ty));
229 }
230
231 let range = range.strip_prefix('=').ok_or(HeaderParseError)?;
232
233 let (from, to) = split_once(range, '-').ok_or(HeaderParseError)?;
234 let from = if from.is_empty() { None } else { Some(from) };
235 let to = if to.is_empty() { None } else { Some(to) };
236
237 let from = from
238 .map(|s| s.parse::<SmpteTime>().map_err(|_| HeaderParseError))
239 .transpose()?;
240 let to = to
241 .map(|s| s.parse::<SmpteTime>().map_err(|_| HeaderParseError))
242 .transpose()?;
243
244 match (from, to) {
245 (Some(from), Some(to)) => Ok(SmpteRange::FromTo(ty, from, to)),
246 (None, Some(to)) => Ok(SmpteRange::To(ty, to)),
247 (Some(from), None) => Ok(SmpteRange::From(ty, from)),
248 (None, None) => Err(HeaderParseError),
249 }
250 } else {
251 Ok(SmpteRange::Empty(s.parse()?))
252 }
253 }
254}
255
256#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
258#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
259pub enum SmpteType {
260 Smpte,
262 Smpte30Drop,
264 Smpte25,
266 Other(String),
268}
269
270impl fmt::Display for SmpteType {
271 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
272 match self {
273 SmpteType::Smpte => f.write_str("smpte"),
274 SmpteType::Smpte30Drop => f.write_str("smpte-30-drop"),
275 SmpteType::Smpte25 => f.write_str("smpte-25"),
276 SmpteType::Other(o) => f.write_str(o),
277 }
278 }
279}
280
281impl std::str::FromStr for SmpteType {
282 type Err = HeaderParseError;
283
284 fn from_str(s: &str) -> Result<Self, HeaderParseError> {
285 let stripped = s.strip_prefix("smpte").ok_or(HeaderParseError)?;
286 match stripped {
287 "" => Ok(SmpteType::Smpte),
288 "-30-drop" => Ok(SmpteType::Smpte30Drop),
289 "-25" => Ok(SmpteType::Smpte25),
290 _ => Ok(SmpteType::Other(s.into())),
291 }
292 }
293}
294
295#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
297#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
298pub struct SmpteTime {
299 pub hours: u8,
301 pub minutes: u8,
303 pub seconds: u8,
305 pub frames: Option<(u8, Option<u8>)>,
307}
308
309impl fmt::Display for SmpteTime {
310 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
311 match self.frames {
312 None => write!(
313 f,
314 "{:02}:{:02}:{:02}",
315 self.hours, self.minutes, self.seconds
316 ),
317 Some((frames, None)) => write!(
318 f,
319 "{:02}:{:02}:{:02}:{:02}",
320 self.hours, self.minutes, self.seconds, frames
321 ),
322 Some((frames, Some(subframes))) => write!(
323 f,
324 "{:02}:{:02}:{:02}:{:02}.{:02}",
325 self.hours, self.minutes, self.seconds, frames, subframes
326 ),
327 }
328 }
329}
330
331impl std::str::FromStr for SmpteTime {
332 type Err = HeaderParseError;
333
334 fn from_str(s: &str) -> Result<Self, HeaderParseError> {
335 let mut s = s.split(':');
336
337 let hours = s
338 .next()
339 .and_then(|s| s.parse::<u8>().ok())
340 .ok_or(HeaderParseError)?;
341 let minutes = s
342 .next()
343 .and_then(|s| s.parse::<u8>().ok())
344 .ok_or(HeaderParseError)?;
345 let seconds = s
346 .next()
347 .and_then(|s| s.parse::<u8>().ok())
348 .ok_or(HeaderParseError)?;
349
350 let frames = match s.next() {
351 Some(frames) => frames,
352 None => {
353 return Ok(SmpteTime {
354 hours,
355 minutes,
356 seconds,
357 frames: None,
358 })
359 }
360 };
361
362 if s.next().is_some() {
363 return Err(HeaderParseError);
364 }
365
366 if let Some((frames, subframes)) = split_once(frames, '.') {
367 let frames = frames.parse::<u8>().map_err(|_| HeaderParseError)?;
368 let digits = subframes.len();
369
370 let factor = match digits {
371 1 => 10,
372 2 => 1,
373 _ => return Err(HeaderParseError),
374 };
375
376 let subframes = subframes.parse::<u8>().map_err(|_| HeaderParseError)? * factor;
377
378 Ok(SmpteTime {
379 hours,
380 minutes,
381 seconds,
382 frames: Some((frames, Some(subframes))),
383 })
384 } else {
385 let frames = frames.parse::<u8>().map_err(|_| HeaderParseError)?;
386
387 Ok(SmpteTime {
388 hours,
389 minutes,
390 seconds,
391 frames: Some((frames, None)),
392 })
393 }
394 }
395}
396
397#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
399#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
400pub enum UtcRange {
401 Empty,
403 From(UtcTime),
405 FromTo(UtcTime, UtcTime),
407 To(UtcTime),
409}
410
411impl fmt::Display for UtcRange {
412 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
413 match self {
414 UtcRange::Empty => fmt.write_str("clock"),
415 UtcRange::From(f) => write!(fmt, "clock={f}-"),
416 UtcRange::FromTo(f, t) => write!(fmt, "clock={f}-{t}"),
417 UtcRange::To(t) => write!(fmt, "clock=-{t}"),
418 }
419 }
420}
421
422impl std::str::FromStr for UtcRange {
423 type Err = HeaderParseError;
424
425 fn from_str(s: &str) -> Result<Self, HeaderParseError> {
426 let s = s.strip_prefix("clock").ok_or(HeaderParseError)?;
427
428 if s.is_empty() {
429 return Ok(UtcRange::Empty);
430 }
431
432 let s = s.strip_prefix('=').ok_or(HeaderParseError)?;
433
434 let (from, to) = split_once(s, '-').ok_or(HeaderParseError)?;
435 let from = if from.is_empty() { None } else { Some(from) };
436 let to = if to.is_empty() { None } else { Some(to) };
437
438 let from = from
439 .map(|s| s.parse::<UtcTime>().map_err(|_| HeaderParseError))
440 .transpose()?;
441 let to = to
442 .map(|s| s.parse::<UtcTime>().map_err(|_| HeaderParseError))
443 .transpose()?;
444
445 match (from, to) {
446 (Some(from), Some(to)) => Ok(UtcRange::FromTo(from, to)),
447 (None, Some(to)) => Ok(UtcRange::To(to)),
448 (Some(from), None) => Ok(UtcRange::From(from)),
449 (None, None) => Err(HeaderParseError),
450 }
451 }
452}
453
454#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
456#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
457pub struct UtcTime {
458 pub date: u32,
460 pub time: u32,
462 pub nanoseconds: Option<u32>,
464}
465
466impl fmt::Display for UtcTime {
467 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
468 if let Some(ns) = self.nanoseconds {
469 write!(f, "{:08}T{:06}.{:09}Z", self.date, self.time, ns)
470 } else {
471 write!(f, "{:08}T{:06}Z", self.date, self.time)
472 }
473 }
474}
475
476impl std::str::FromStr for UtcTime {
477 type Err = HeaderParseError;
478
479 fn from_str(s: &str) -> Result<Self, HeaderParseError> {
480 let (date, time) = split_once(s, 'T').ok_or(HeaderParseError)?;
481 let time = time.strip_suffix('Z').ok_or(HeaderParseError)?;
482
483 let date = date.parse::<u32>().map_err(|_| HeaderParseError)?;
484 let (time, nanoseconds) = if let Some((time, subseconds)) = split_once(time, '.') {
485 let time = time.parse::<u32>().map_err(|_| HeaderParseError)?;
486 let digits = subseconds.len();
487 if digits > 9 || digits == 0 {
488 return Err(HeaderParseError);
489 }
490 let subseconds = subseconds.parse::<u32>().map_err(|_| HeaderParseError)?;
491
492 let nanoseconds = subseconds * u32::pow(10, 9 - digits as u32);
493
494 (time, Some(nanoseconds))
495 } else {
496 let time = time.parse::<u32>().map_err(|_| HeaderParseError)?;
497
498 (time, None)
499 };
500
501 Ok(UtcTime {
502 date,
503 time,
504 nanoseconds,
505 })
506 }
507}
508
509impl super::TypedHeader for Range {
510 fn from_headers(headers: impl AsRef<Headers>) -> Result<Option<Self>, HeaderParseError> {
511 let headers = headers.as_ref();
512
513 let header = match headers.get(&RANGE) {
514 None => return Ok(None),
515 Some(header) => header,
516 };
517
518 Ok(Some(header.as_str().parse()?))
519 }
520
521 fn insert_into(&self, mut headers: impl AsMut<Headers>) {
522 let headers = headers.as_mut();
523 headers.insert(RANGE, self.to_string());
524 }
525}
526
527#[cfg(test)]
528mod tests {
529 use super::*;
530
531 #[test]
532 fn test_range() {
533 let headers = [
534 ("npt", Range::Npt(NptRange::Empty), None),
535 ("npt=now-", Range::Npt(NptRange::From(NptTime::Now)), None),
536 (
537 "npt=123-456",
538 Range::Npt(NptRange::FromTo(
539 NptTime::Seconds(123, None),
540 NptTime::Seconds(456, None),
541 )),
542 None,
543 ),
544 (
545 "npt=-456",
546 Range::Npt(NptRange::To(NptTime::Seconds(456, None))),
547 None,
548 ),
549 (
550 "npt=123-",
551 Range::Npt(NptRange::From(NptTime::Seconds(123, None))),
552 None,
553 ),
554 (
555 "npt=123.5-456.567",
556 Range::Npt(NptRange::FromTo(
557 NptTime::Seconds(123, Some(500_000_000)),
558 NptTime::Seconds(456, Some(567_000_000)),
559 )),
560 Some("npt=123.500000000-456.567000000"),
561 ),
562 (
563 "npt=43:53:10-1:17:59.123",
564 Range::Npt(NptRange::FromTo(
565 NptTime::Hms(43, 53, 10, None),
566 NptTime::Hms(1, 17, 59, Some(123_000_000)),
567 )),
568 Some("npt=43:53:10-01:17:59.123000000"),
569 ),
570 ];
571
572 for (header, expected, serialized) in &headers {
573 let request = crate::Request::builder(crate::Method::Play, crate::Version::V2_0)
574 .header(crate::headers::RANGE, *header)
575 .empty();
576
577 let range = request
578 .typed_header::<super::Range>()
579 .unwrap_or_else(|_| panic!("couldn't parse {}", header))
580 .unwrap();
581
582 assert_eq!(range, *expected, "{header}");
583
584 let request2 = crate::Request::builder(crate::Method::Play, crate::Version::V2_0)
585 .typed_header(&range)
586 .empty();
587
588 let range = request2.header(&crate::headers::RANGE).unwrap();
589
590 assert_eq!(range, serialized.unwrap_or(header), "{header}");
591 }
592 }
593}