1use std::{
4 fmt::{self, Display, Formatter},
5 num::ParseFloatError,
6 str::FromStr,
7};
8
9use crate::{Error, header::HeaderFieldValue};
10
11#[derive(Debug, Copy, Clone)]
15pub enum NptRange {
16 StartFrom(NptTime),
17 EndAt(NptTime),
18 Range(NptTime, NptTime),
19}
20
21impl Display for NptRange {
22 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
23 write!(f, "npt=")?;
24
25 match self {
26 Self::StartFrom(start) => write!(f, "{start}-"),
27 Self::EndAt(end) => write!(f, "-{end}"),
28 Self::Range(start, end) => write!(f, "{start}-{end}"),
29 }
30 }
31}
32
33impl From<NptRange> for HeaderFieldValue {
34 #[inline]
35 fn from(value: NptRange) -> Self {
36 HeaderFieldValue::from(value.to_string())
37 }
38}
39
40impl FromStr for NptRange {
41 type Err = Error;
42
43 fn from_str(s: &str) -> Result<Self, Self::Err> {
44 let range = s
45 .strip_prefix("npt=")
46 .ok_or_else(|| Error::from_static_msg("not an NPT range"))?;
47
48 let (start, end) = range
49 .split_once('-')
50 .ok_or_else(|| Error::from_static_msg("invalid NPT range"))?;
51
52 let start = if start.is_empty() {
53 None
54 } else {
55 start
56 .parse()
57 .map(Some)
58 .map_err(|_| Error::from_static_msg("invalid NPT range"))?
59 };
60
61 let end = if end.is_empty() {
62 None
63 } else {
64 end.parse()
65 .map(Some)
66 .map_err(|_| Error::from_static_msg("invalid NPT range"))?
67 };
68
69 if let Some(start) = start {
70 if let Some(end) = end {
71 Ok(Self::Range(start, end))
72 } else {
73 Ok(Self::StartFrom(start))
74 }
75 } else if let Some(end) = end {
76 Ok(Self::EndAt(end))
77 } else {
78 Err(Error::from_static_msg("invalid NPT range"))
79 }
80 }
81}
82
83impl TryFrom<&HeaderFieldValue> for NptRange {
84 type Error = Error;
85
86 fn try_from(value: &HeaderFieldValue) -> Result<Self, Self::Error> {
87 value
88 .to_str()
89 .map_err(|_| Error::from_static_msg("header field is not UTF-8 encoded"))?
90 .parse()
91 }
92}
93
94#[derive(Debug, Copy, Clone)]
96pub enum NptTime {
97 Now,
98 Timestamp(f64),
99}
100
101impl Display for NptTime {
102 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
103 match self {
104 Self::Now => f.write_str("now"),
105 Self::Timestamp(t) => write!(f, "{t:.3}"),
106 }
107 }
108}
109
110impl FromStr for NptTime {
111 type Err = ParseFloatError;
112
113 fn from_str(s: &str) -> Result<Self, Self::Err> {
114 if s == "now" {
115 Ok(Self::Now)
116 } else {
117 s.parse().map(Self::Timestamp)
118 }
119 }
120}