Skip to main content

broadcast_dash/
utc_timing.rs

1//! `UTCTiming` scheme identity (ISO/IEC 23009-1 5.8.5.7 Table 35).
2//!
3//! The manifest states how a player may learn wall-clock time; acting on it is
4//! the client's business, so only the scheme vocabulary lives here.
5
6use broadcast_common::impl_spec_display;
7
8/// 5.8.5.7: `urn:mpeg:dash:utc:ntp:2014`.
9pub const SCHEME_NTP: &str = "urn:mpeg:dash:utc:ntp:2014";
10/// 5.8.5.7: `urn:mpeg:dash:utc:sntp:2014`.
11pub const SCHEME_SNTP: &str = "urn:mpeg:dash:utc:sntp:2014";
12/// 5.8.5.7: `urn:mpeg:dash:utc:http-head:2014`.
13pub const SCHEME_HTTP_HEAD: &str = "urn:mpeg:dash:utc:http-head:2014";
14/// 5.8.5.7: `urn:mpeg:dash:utc:http-xsdate:2014`.
15pub const SCHEME_HTTP_XSDATE: &str = "urn:mpeg:dash:utc:http-xsdate:2014";
16/// 5.8.5.7: `urn:mpeg:dash:utc:http-iso:2014`.
17pub const SCHEME_HTTP_ISO: &str = "urn:mpeg:dash:utc:http-iso:2014";
18/// 5.8.5.7: `urn:mpeg:dash:utc:http-ntp:2014`.
19pub const SCHEME_HTTP_NTP: &str = "urn:mpeg:dash:utc:http-ntp:2014";
20/// 5.8.5.7: `urn:mpeg:dash:utc:direct:2014`.
21pub const SCHEME_DIRECT: &str = "urn:mpeg:dash:utc:direct:2014";
22
23/// A `UTCTiming` scheme (Table 35).
24#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
25#[non_exhaustive]
26pub enum UtcScheme {
27    /// NTP servers; not acted on (no UDP in a sans-IO core).
28    Ntp,
29    /// SNTP servers; not acted on.
30    Sntp,
31    /// `HEAD`, read the `Date` header.
32    HttpHead,
33    /// `GET`, body is an `xs:dateTime`.
34    HttpXsdate,
35    /// `GET`, body is an ISO 8601 time.
36    HttpIso,
37    /// `GET`, body is an NTP timestamp.
38    HttpNtp,
39    /// `@value` is the time.
40    #[default]
41    Direct,
42    /// A scheme this core does not know.
43    Unknown,
44}
45
46impl UtcScheme {
47    /// Classify a `schemeIdUri`.
48    #[must_use]
49    pub fn from_scheme_id_uri(uri: &str) -> Self {
50        match uri {
51            SCHEME_NTP => Self::Ntp,
52            SCHEME_SNTP => Self::Sntp,
53            SCHEME_HTTP_HEAD => Self::HttpHead,
54            SCHEME_HTTP_XSDATE => Self::HttpXsdate,
55            SCHEME_HTTP_ISO => Self::HttpIso,
56            SCHEME_HTTP_NTP => Self::HttpNtp,
57            SCHEME_DIRECT => Self::Direct,
58            _ => Self::Unknown,
59        }
60    }
61
62    /// Stable snake_case label.
63    #[must_use]
64    pub fn name(&self) -> &'static str {
65        match self {
66            Self::Ntp => "ntp",
67            Self::Sntp => "sntp",
68            Self::HttpHead => "http_head",
69            Self::HttpXsdate => "http_xsdate",
70            Self::HttpIso => "http_iso",
71            Self::HttpNtp => "http_ntp",
72            Self::Direct => "direct",
73            Self::Unknown => "unknown",
74        }
75    }
76
77    /// Whether the client can act on the scheme without UDP.
78    #[must_use]
79    pub fn is_actionable(&self) -> bool {
80        !matches!(self, Self::Ntp | Self::Sntp | Self::Unknown)
81    }
82
83    /// Whether the scheme needs an HTTP request (`@value` lists URLs).
84    #[must_use]
85    pub fn needs_request(&self) -> bool {
86        matches!(
87            self,
88            Self::HttpHead | Self::HttpXsdate | Self::HttpIso | Self::HttpNtp
89        )
90    }
91}
92impl_spec_display!(UtcScheme);
93
94#[cfg(test)]
95mod tests {
96    use super::*;
97
98    #[test]
99    fn schemes_round_trip_through_their_uri() {
100        for (uri, scheme) in [
101            (SCHEME_NTP, UtcScheme::Ntp),
102            (SCHEME_SNTP, UtcScheme::Sntp),
103            (SCHEME_HTTP_HEAD, UtcScheme::HttpHead),
104            (SCHEME_HTTP_XSDATE, UtcScheme::HttpXsdate),
105            (SCHEME_HTTP_ISO, UtcScheme::HttpIso),
106            (SCHEME_HTTP_NTP, UtcScheme::HttpNtp),
107            (SCHEME_DIRECT, UtcScheme::Direct),
108            ("urn:example:utc", UtcScheme::Unknown),
109        ] {
110            assert_eq!(UtcScheme::from_scheme_id_uri(uri), scheme, "{uri}");
111        }
112    }
113}