From 81f9a421fe9d0dd2e6f56c2d676d18b8aec53022 Mon Sep 17 00:00:00 2001 From: Luro02 <24826124+Luro02@users.noreply.github.com> Date: Sun, 22 Sep 2019 10:57:28 +0200 Subject: [PATCH] added RequiredVersion trait --- src/attribute.rs | 4 +- src/master_playlist.rs | 22 +++-- src/media_playlist.rs | 20 ++--- src/media_segment.rs | 21 ++--- src/tags/basic/m3u.rs | 11 +-- src/tags/basic/version.rs | 30 ++++--- .../master_playlist/i_frame_stream_inf.rs | 26 ++++-- src/tags/master_playlist/media.rs | 36 ++++++-- src/tags/master_playlist/session_data.rs | 16 +--- src/tags/master_playlist/session_key.rs | 31 +++---- src/tags/master_playlist/stream_inf.rs | 27 ++++-- .../media_playlist/discontinuity_sequence.rs | 50 +++++++---- src/tags/media_playlist/end_list.rs | 26 ++++-- src/tags/media_playlist/i_frames_only.rs | 28 ++++-- src/tags/media_playlist/media_sequence.rs | 50 +++++++---- src/tags/media_playlist/playlist_type.rs | 13 +-- src/tags/media_playlist/target_duration.rs | 49 +++++++---- src/tags/media_segment/byte_range.rs | 26 +++--- src/tags/media_segment/date_range.rs | 7 +- src/tags/media_segment/discontinuity.rs | 31 ++++--- src/tags/media_segment/inf.rs | 13 +-- src/tags/media_segment/map.rs | 49 +++++++---- src/tags/media_segment/program_date_time.rs | 38 +++++--- src/tags/shared/independent_segments.rs | 35 ++++++-- src/tags/shared/start.rs | 55 +++++++++--- src/types/decimal_floating_point.rs | 12 +++ src/types/decryption_key.rs | 30 +++---- src/types/protocol_version.rs | 87 +++++++++++++++---- 28 files changed, 550 insertions(+), 293 deletions(-) diff --git a/src/attribute.rs b/src/attribute.rs index 3c9807f..d0cf36e 100644 --- a/src/attribute.rs +++ b/src/attribute.rs @@ -61,10 +61,10 @@ impl FromStr for AttributePairs { let key = pair[0].to_uppercase(); let value = pair[1].to_string(); - result.insert(key.to_string(), value.to_string()); + result.insert(key.trim().to_string(), value.trim().to_string()); } - #[cfg(test)] + #[cfg(test)] // this is very useful, when a test fails! dbg!(&result); Ok(result) } diff --git a/src/master_playlist.rs b/src/master_playlist.rs index e228e13..6f52f0f 100644 --- a/src/master_playlist.rs +++ b/src/master_playlist.rs @@ -10,7 +10,7 @@ use crate::tags::{ ExtM3u, ExtXIFrameStreamInf, ExtXIndependentSegments, ExtXMedia, ExtXSessionData, ExtXSessionKey, ExtXStart, ExtXStreamInf, ExtXVersion, }; -use crate::types::{ClosedCaptions, MediaType, ProtocolVersion}; +use crate::types::{ClosedCaptions, MediaType, ProtocolVersion, RequiredVersion}; use crate::Error; /// Master playlist. @@ -92,6 +92,12 @@ impl MasterPlaylist { } } +impl RequiredVersion for MasterPlaylist { + fn required_version(&self) -> ProtocolVersion { + self.version_tag.version() + } +} + impl MasterPlaylistBuilder { fn validate(&self) -> Result<(), String> { let required_version = self.required_version(); @@ -118,43 +124,43 @@ impl MasterPlaylistBuilder { .chain( self.independent_segments_tag .iter() - .map(|t| t.iter().map(|t| t.requires_version())) + .map(|t| t.iter().map(|t| t.required_version())) .flatten(), ) .chain( self.start_tag .iter() - .map(|t| t.iter().map(|t| t.requires_version())) + .map(|t| t.iter().map(|t| t.required_version())) .flatten(), ) .chain( self.media_tags .iter() - .map(|t| t.iter().map(|t| t.requires_version())) + .map(|t| t.iter().map(|t| t.required_version())) .flatten(), ) .chain( self.stream_inf_tags .iter() - .map(|t| t.iter().map(|t| t.requires_version())) + .map(|t| t.iter().map(|t| t.required_version())) .flatten(), ) .chain( self.i_frame_stream_inf_tags .iter() - .map(|t| t.iter().map(|t| t.requires_version())) + .map(|t| t.iter().map(|t| t.required_version())) .flatten(), ) .chain( self.session_data_tags .iter() - .map(|t| t.iter().map(|t| t.requires_version())) + .map(|t| t.iter().map(|t| t.required_version())) .flatten(), ) .chain( self.session_key_tags .iter() - .map(|t| t.iter().map(|t| t.requires_version())) + .map(|t| t.iter().map(|t| t.required_version())) .flatten(), ) .max() diff --git a/src/media_playlist.rs b/src/media_playlist.rs index 3f673c0..da20213 100644 --- a/src/media_playlist.rs +++ b/src/media_playlist.rs @@ -11,7 +11,7 @@ use crate::tags::{ ExtM3u, ExtXDiscontinuitySequence, ExtXEndList, ExtXIFramesOnly, ExtXIndependentSegments, ExtXMediaSequence, ExtXPlaylistType, ExtXStart, ExtXTargetDuration, ExtXVersion, }; -use crate::types::ProtocolVersion; +use crate::types::{ProtocolVersion, RequiredVersion}; use crate::Error; /// Media playlist. @@ -142,60 +142,60 @@ impl MediaPlaylistBuilder { .chain( self.target_duration_tag .iter() - .map(|t| t.requires_version()), + .map(|t| t.required_version()), ) .chain(self.media_sequence_tag.iter().map(|t| { if let Some(p) = t { - p.requires_version() + p.required_version() } else { ProtocolVersion::V1 } })) .chain(self.discontinuity_sequence_tag.iter().map(|t| { if let Some(p) = t { - p.requires_version() + p.required_version() } else { ProtocolVersion::V1 } })) .chain(self.playlist_type_tag.iter().map(|t| { if let Some(p) = t { - p.requires_version() + p.required_version() } else { ProtocolVersion::V1 } })) .chain(self.i_frames_only_tag.iter().map(|t| { if let Some(p) = t { - p.requires_version() + p.required_version() } else { ProtocolVersion::V1 } })) .chain(self.independent_segments_tag.iter().map(|t| { if let Some(p) = t { - p.requires_version() + p.required_version() } else { ProtocolVersion::V1 } })) .chain(self.start_tag.iter().map(|t| { if let Some(p) = t { - p.requires_version() + p.required_version() } else { ProtocolVersion::V1 } })) .chain(self.end_list_tag.iter().map(|t| { if let Some(p) = t { - p.requires_version() + p.required_version() } else { ProtocolVersion::V1 } })) .chain(self.segments.iter().map(|t| { t.iter() - .map(|s| s.requires_version()) + .map(|s| s.required_version()) .max() .unwrap_or(ProtocolVersion::V1) })) diff --git a/src/media_segment.rs b/src/media_segment.rs index 8460547..e1d51bf 100644 --- a/src/media_segment.rs +++ b/src/media_segment.rs @@ -6,7 +6,7 @@ use derive_builder::Builder; use crate::tags::{ ExtInf, ExtXByteRange, ExtXDateRange, ExtXDiscontinuity, ExtXKey, ExtXMap, ExtXProgramDateTime, }; -use crate::types::ProtocolVersion; +use crate::types::{ProtocolVersion, RequiredVersion}; /// Media segment. #[derive(Debug, Clone, Builder)] @@ -118,21 +118,22 @@ impl MediaSegment { pub fn key_tags(&self) -> &[ExtXKey] { &self.key_tags } +} - /// Returns the protocol compatibility version that this segment requires. - pub fn requires_version(&self) -> ProtocolVersion { +impl RequiredVersion for MediaSegment { + fn required_version(&self) -> ProtocolVersion { iter::empty() - .chain(self.key_tags.iter().map(|t| t.requires_version())) - .chain(self.map_tag.iter().map(|t| t.requires_version())) - .chain(self.byte_range_tag.iter().map(|t| t.requires_version())) - .chain(self.date_range_tag.iter().map(|t| t.requires_version())) - .chain(self.discontinuity_tag.iter().map(|t| t.requires_version())) + .chain(self.key_tags.iter().map(|t| t.required_version())) + .chain(self.map_tag.iter().map(|t| t.required_version())) + .chain(self.byte_range_tag.iter().map(|t| t.required_version())) + .chain(self.date_range_tag.iter().map(|t| t.required_version())) + .chain(self.discontinuity_tag.iter().map(|t| t.required_version())) .chain( self.program_date_time_tag .iter() - .map(|t| t.requires_version()), + .map(|t| t.required_version()), ) - .chain(iter::once(self.inf_tag.requires_version())) + .chain(iter::once(self.inf_tag.required_version())) .max() .unwrap_or(ProtocolVersion::V7) } diff --git a/src/tags/basic/m3u.rs b/src/tags/basic/m3u.rs index 07f95ec..d64939c 100644 --- a/src/tags/basic/m3u.rs +++ b/src/tags/basic/m3u.rs @@ -1,7 +1,7 @@ use std::fmt; use std::str::FromStr; -use crate::types::ProtocolVersion; +use crate::types::{ProtocolVersion, RequiredVersion}; use crate::utils::tag; use crate::Error; @@ -13,9 +13,10 @@ pub struct ExtM3u; impl ExtM3u { pub(crate) const PREFIX: &'static str = "#EXTM3U"; +} - /// Returns the protocol compatibility version that this tag requires. - pub const fn requires_version(&self) -> ProtocolVersion { +impl RequiredVersion for ExtM3u { + fn required_version(&self) -> ProtocolVersion { ProtocolVersion::V1 } } @@ -50,7 +51,7 @@ mod test { } #[test] - fn test_requires_version() { - assert_eq!(ExtM3u.requires_version(), ProtocolVersion::V1); + fn test_required_version() { + assert_eq!(ExtM3u.required_version(), ProtocolVersion::V1); } } diff --git a/src/tags/basic/version.rs b/src/tags/basic/version.rs index 5f196db..b5afef2 100644 --- a/src/tags/basic/version.rs +++ b/src/tags/basic/version.rs @@ -1,7 +1,7 @@ use std::fmt; use std::str::FromStr; -use crate::types::ProtocolVersion; +use crate::types::{ProtocolVersion, RequiredVersion}; use crate::utils::tag; use crate::Error; @@ -20,12 +20,24 @@ impl ExtXVersion { } /// Returns the protocol compatibility version of the playlist containing this tag. + /// + /// # Example + /// ``` + /// # use hls_m3u8::tags::ExtXVersion; + /// use hls_m3u8::types::ProtocolVersion; + /// + /// assert_eq!( + /// ExtXVersion::new(ProtocolVersion::V6).version(), + /// ProtocolVersion::V6 + /// ); + /// ``` pub const fn version(&self) -> ProtocolVersion { self.0 } +} - /// Returns the protocol compatibility version that this tag requires. - pub const fn requires_version(&self) -> ProtocolVersion { +impl RequiredVersion for ExtXVersion { + fn required_version(&self) -> ProtocolVersion { ProtocolVersion::V1 } } @@ -78,18 +90,10 @@ mod test { } #[test] - fn test_requires_version() { + fn test_required_version() { assert_eq!( - ExtXVersion::new(ProtocolVersion::V6).requires_version(), + ExtXVersion::new(ProtocolVersion::V6).required_version(), ProtocolVersion::V1 ); } - - #[test] - fn test_version() { - assert_eq!( - ExtXVersion::new(ProtocolVersion::V6).version(), - ProtocolVersion::V6 - ); - } } diff --git a/src/tags/master_playlist/i_frame_stream_inf.rs b/src/tags/master_playlist/i_frame_stream_inf.rs index 989ac36..fc38814 100644 --- a/src/tags/master_playlist/i_frame_stream_inf.rs +++ b/src/tags/master_playlist/i_frame_stream_inf.rs @@ -3,12 +3,23 @@ use std::ops::{Deref, DerefMut}; use std::str::FromStr; use crate::attribute::AttributePairs; -use crate::types::{ProtocolVersion, StreamInf}; +use crate::types::{ProtocolVersion, RequiredVersion, StreamInf}; use crate::utils::{quote, tag, unquote}; use crate::Error; -/// [4.3.4.3. EXT-X-I-FRAME-STREAM-INF] +/// # [4.3.4.3. EXT-X-I-FRAME-STREAM-INF] +/// The [ExtXIFrameStreamInf] tag identifies a [Media Playlist] file +/// containing the I-frames of a multimedia presentation. It stands +/// alone, in that it does not apply to a particular URI in the [Master Playlist]. /// +/// Its format is: +/// +/// ```text +/// #EXT-X-I-FRAME-STREAM-INF: +/// ``` +/// +/// [Master Playlist]: crate::MasterPlaylist +/// [Media Playlist]: crate::MediaPlaylist /// [4.3.4.3. EXT-X-I-FRAME-STREAM-INF]: https://tools.ietf.org/html/rfc8216#section-4.3.4.3 #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct ExtXIFrameStreamInf { @@ -19,7 +30,7 @@ pub struct ExtXIFrameStreamInf { impl ExtXIFrameStreamInf { pub(crate) const PREFIX: &'static str = "#EXT-X-I-FRAME-STREAM-INF:"; - /// Makes a new `ExtXIFrameStreamInf` tag. + /// Makes a new [ExtXIFrameStreamInf] tag. pub fn new(uri: T, bandwidth: u64) -> Self { ExtXIFrameStreamInf { uri: uri.to_string(), @@ -55,9 +66,10 @@ impl ExtXIFrameStreamInf { self.uri = value.to_string(); self } +} - /// Returns the protocol compatibility version that this tag requires. - pub const fn requires_version(&self) -> ProtocolVersion { +impl RequiredVersion for ExtXIFrameStreamInf { + fn required_version(&self) -> ProtocolVersion { ProtocolVersion::V1 } } @@ -133,9 +145,9 @@ mod test { } #[test] - fn test_requires_version() { + fn test_required_version() { assert_eq!( - ExtXIFrameStreamInf::new("foo", 1000).requires_version(), + ExtXIFrameStreamInf::new("foo", 1000).required_version(), ProtocolVersion::V1 ); } diff --git a/src/tags/master_playlist/media.rs b/src/tags/master_playlist/media.rs index 819f62b..ed71581 100644 --- a/src/tags/master_playlist/media.rs +++ b/src/tags/master_playlist/media.rs @@ -2,7 +2,7 @@ use std::fmt; use std::str::FromStr; use crate::attribute::AttributePairs; -use crate::types::{InStreamId, MediaType, ProtocolVersion}; +use crate::types::{InStreamId, MediaType, ProtocolVersion, RequiredVersion}; use crate::utils::{parse_yes_or_no, quote, tag, unquote}; use crate::Error; @@ -273,9 +273,10 @@ impl ExtXMedia { pub fn channels(&self) -> Option<&String> { self.channels.as_ref() } +} - /// Returns the protocol compatibility version that this tag requires. - pub fn requires_version(&self) -> ProtocolVersion { +impl RequiredVersion for ExtXMedia { + fn required_version(&self) -> ProtocolVersion { match self.instream_id { None | Some(InStreamId::Cc1) @@ -385,11 +386,28 @@ mod test { use super::*; #[test] - fn ext_x_media() { - let tag = ExtXMedia::new(MediaType::Audio, "foo", "bar"); - let text = r#"#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="foo",NAME="bar""#; - assert_eq!(text.parse().ok(), Some(tag.clone())); - assert_eq!(tag.to_string(), text); - assert_eq!(tag.requires_version(), ProtocolVersion::V1); + fn test_display() { + assert_eq!( + ExtXMedia::new(MediaType::Audio, "foo", "bar").to_string(), + "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"foo\",NAME=\"bar\"".to_string() + ) + } + + #[test] + fn test_parser() { + assert_eq!( + ExtXMedia::new(MediaType::Audio, "foo", "bar"), + "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"foo\",NAME=\"bar\"" + .parse() + .unwrap() + ) + } + + #[test] + fn test_required_version() { + assert_eq!( + ExtXMedia::new(MediaType::Audio, "foo", "bar").required_version(), + ProtocolVersion::V1 + ) } } diff --git a/src/tags/master_playlist/session_data.rs b/src/tags/master_playlist/session_data.rs index ddd7ee6..09d74ae 100644 --- a/src/tags/master_playlist/session_data.rs +++ b/src/tags/master_playlist/session_data.rs @@ -4,7 +4,7 @@ use std::str::FromStr; use derive_builder::Builder; use crate::attribute::AttributePairs; -use crate::types::ProtocolVersion; +use crate::types::{ProtocolVersion, RequiredVersion}; use crate::utils::{quote, tag, unquote}; use crate::Error; @@ -243,18 +243,10 @@ impl ExtXSessionData { self.data = value; self } +} - /// Returns the protocol compatibility version, that this tag requires. - /// - /// # Example - /// ``` - /// # use hls_m3u8::types::ProtocolVersion; - /// # use hls_m3u8::tags::{ExtXSessionData, SessionData}; - /// # - /// let tag = ExtXSessionData::new("foo", SessionData::Value("bar".into())); - /// assert_eq!(tag.requires_version(), ProtocolVersion::V1); - /// ``` - pub const fn requires_version(&self) -> ProtocolVersion { +impl RequiredVersion for ExtXSessionData { + fn required_version(&self) -> ProtocolVersion { ProtocolVersion::V1 } } diff --git a/src/tags/master_playlist/session_key.rs b/src/tags/master_playlist/session_key.rs index e76fb64..ba1d9fc 100644 --- a/src/tags/master_playlist/session_key.rs +++ b/src/tags/master_playlist/session_key.rs @@ -2,7 +2,7 @@ use std::fmt; use std::ops::{Deref, DerefMut}; use std::str::FromStr; -use crate::types::{DecryptionKey, EncryptionMethod, ProtocolVersion}; +use crate::types::{DecryptionKey, EncryptionMethod, ProtocolVersion, RequiredVersion}; use crate::utils::tag; use crate::Error; @@ -25,24 +25,10 @@ impl ExtXSessionKey { Self(DecryptionKey::new(method, uri)) } +} - /// Returns the protocol compatibility version that this tag requires. - /// # Example - /// ``` - /// use hls_m3u8::tags::ExtXSessionKey; - /// use hls_m3u8::types::{EncryptionMethod, ProtocolVersion}; - /// - /// let mut key = ExtXSessionKey::new( - /// EncryptionMethod::Aes128, - /// "https://www.example.com/" - /// ); - /// - /// assert_eq!( - /// key.requires_version(), - /// ProtocolVersion::V1 - /// ); - /// ``` - pub fn requires_version(&self) -> ProtocolVersion { +impl RequiredVersion for ExtXSessionKey { + fn required_version(&self) -> ProtocolVersion { if self.0.key_format.is_some() | self.0.key_format_versions.is_some() { ProtocolVersion::V5 } else if self.0.iv.is_some() { @@ -143,4 +129,13 @@ mod test { key ) } + + #[test] + fn test_required_version() { + assert_eq!( + ExtXSessionKey::new(EncryptionMethod::Aes128, "https://www.example.com/") + .required_version(), + ProtocolVersion::V1 + ); + } } diff --git a/src/tags/master_playlist/stream_inf.rs b/src/tags/master_playlist/stream_inf.rs index dd237a7..e50a297 100644 --- a/src/tags/master_playlist/stream_inf.rs +++ b/src/tags/master_playlist/stream_inf.rs @@ -3,7 +3,9 @@ use std::ops::{Deref, DerefMut}; use std::str::FromStr; use crate::attribute::AttributePairs; -use crate::types::{ClosedCaptions, DecimalFloatingPoint, ProtocolVersion, StreamInf}; +use crate::types::{ + ClosedCaptions, DecimalFloatingPoint, ProtocolVersion, RequiredVersion, StreamInf, +}; use crate::utils::{quote, tag, unquote}; use crate::Error; @@ -23,7 +25,9 @@ pub struct ExtXStreamInf { impl ExtXStreamInf { pub(crate) const PREFIX: &'static str = "#EXT-X-STREAM-INF:"; - /// Makes a new [ExtXStreamInf] tag. + /// Creates a new [ExtXStreamInf] tag. + /// + /// # Examples /// ``` /// # use hls_m3u8::tags::ExtXStreamInf; /// # @@ -40,11 +44,21 @@ impl ExtXStreamInf { } } + pub fn set_uri(&mut self, value: T) -> &mut Self { + self.uri = value.to_string(); + self + } + /// Returns the URI that identifies the associated media playlist. pub const fn uri(&self) -> &String { &self.uri } + pub fn set_frame_rate(&mut self, value: Option) -> &mut Self { + self.frame_rate = value.map(|v| v.into()); + self + } + /// Returns the maximum frame rate for all the video in the variant stream. pub fn frame_rate(&self) -> Option { self.frame_rate.map_or(None, |v| Some(v.as_f64())) @@ -64,9 +78,10 @@ impl ExtXStreamInf { pub fn closed_captions(&self) -> Option<&ClosedCaptions> { self.closed_captions.as_ref() } +} - /// Returns the protocol compatibility version that this tag requires. - pub const fn requires_version(&self) -> ProtocolVersion { +impl RequiredVersion for ExtXStreamInf { + fn required_version(&self) -> ProtocolVersion { ProtocolVersion::V1 } } @@ -158,10 +173,10 @@ mod test { } #[test] - fn test_requires_version() { + fn test_required_version() { assert_eq!( ProtocolVersion::V1, - ExtXStreamInf::new("http://www.example.com", 1000).requires_version() + ExtXStreamInf::new("http://www.example.com", 1000).required_version() ); } diff --git a/src/tags/media_playlist/discontinuity_sequence.rs b/src/tags/media_playlist/discontinuity_sequence.rs index 6619db1..28dda75 100644 --- a/src/tags/media_playlist/discontinuity_sequence.rs +++ b/src/tags/media_playlist/discontinuity_sequence.rs @@ -1,40 +1,39 @@ use std::fmt; use std::str::FromStr; -use crate::types::ProtocolVersion; +use crate::types::{ProtocolVersion, RequiredVersion}; use crate::utils::tag; /// [4.3.3.3. EXT-X-DISCONTINUITY-SEQUENCE] /// /// [4.3.3.3. EXT-X-DISCONTINUITY-SEQUENCE]: https://tools.ietf.org/html/rfc8216#section-4.3.3.3 -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct ExtXDiscontinuitySequence { - seq_num: u64, -} +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] +pub struct ExtXDiscontinuitySequence(u64); impl ExtXDiscontinuitySequence { pub(crate) const PREFIX: &'static str = "#EXT-X-DISCONTINUITY-SEQUENCE:"; /// Makes a new `ExtXDiscontinuitySequence` tag. pub const fn new(seq_num: u64) -> Self { - ExtXDiscontinuitySequence { seq_num } + Self(seq_num) } /// Returns the discontinuity sequence number of /// the first media segment that appears in the associated playlist. - pub const fn seq_num(self) -> u64 { - self.seq_num + pub const fn seq_num(&self) -> u64 { + self.0 } +} - /// Returns the protocol compatibility version that this tag requires. - pub const fn requires_version(self) -> ProtocolVersion { +impl RequiredVersion for ExtXDiscontinuitySequence { + fn required_version(&self) -> ProtocolVersion { ProtocolVersion::V1 } } impl fmt::Display for ExtXDiscontinuitySequence { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}{}", Self::PREFIX, self.seq_num) + write!(f, "{}{}", Self::PREFIX, self.0) } } @@ -42,7 +41,7 @@ impl FromStr for ExtXDiscontinuitySequence { type Err = crate::Error; fn from_str(input: &str) -> Result { - let seq_num = tag(input, Self::PREFIX)?.parse().unwrap(); // TODO! + let seq_num = tag(input, Self::PREFIX)?.parse()?; Ok(Self::new(seq_num)) } } @@ -52,11 +51,26 @@ mod test { use super::*; #[test] - fn ext_x_discontinuity_sequence() { - let tag = ExtXDiscontinuitySequence::new(123); - let text = "#EXT-X-DISCONTINUITY-SEQUENCE:123"; - assert_eq!(text.parse().ok(), Some(tag)); - assert_eq!(tag.to_string(), text); - assert_eq!(tag.requires_version(), ProtocolVersion::V1); + fn test_display() { + assert_eq!( + ExtXDiscontinuitySequence::new(123).to_string(), + "#EXT-X-DISCONTINUITY-SEQUENCE:123".to_string() + ); + } + + #[test] + fn test_required_version() { + assert_eq!( + ExtXDiscontinuitySequence::new(123).required_version(), + ProtocolVersion::V1 + ) + } + + #[test] + fn test_parser() { + assert_eq!( + ExtXDiscontinuitySequence::new(123), + "#EXT-X-DISCONTINUITY-SEQUENCE:123".parse().unwrap() + ); } } diff --git a/src/tags/media_playlist/end_list.rs b/src/tags/media_playlist/end_list.rs index b34b357..c4fcc5b 100644 --- a/src/tags/media_playlist/end_list.rs +++ b/src/tags/media_playlist/end_list.rs @@ -1,7 +1,7 @@ use std::fmt; use std::str::FromStr; -use crate::types::ProtocolVersion; +use crate::types::{ProtocolVersion, RequiredVersion}; use crate::utils::tag; use crate::Error; @@ -10,11 +10,13 @@ use crate::Error; /// [4.3.3.4. EXT-X-ENDLIST]: https://tools.ietf.org/html/rfc8216#section-4.3.3.4 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct ExtXEndList; + impl ExtXEndList { pub(crate) const PREFIX: &'static str = "#EXT-X-ENDLIST"; +} - /// Returns the protocol compatibility version that this tag requires. - pub const fn requires_version(self) -> ProtocolVersion { +impl RequiredVersion for ExtXEndList { + fn required_version(&self) -> ProtocolVersion { ProtocolVersion::V1 } } @@ -39,11 +41,17 @@ mod test { use super::*; #[test] - fn ext_x_endlist() { - let tag = ExtXEndList; - let text = "#EXT-X-ENDLIST"; - assert_eq!(text.parse().ok(), Some(tag)); - assert_eq!(tag.to_string(), text); - assert_eq!(tag.requires_version(), ProtocolVersion::V1); + fn test_display() { + assert_eq!(ExtXEndList.to_string(), "#EXT-X-ENDLIST".to_string()); + } + + #[test] + fn test_parser() { + assert_eq!(ExtXEndList, "#EXT-X-ENDLIST".parse().unwrap()); + } + + #[test] + fn test_required_version() { + assert_eq!(ExtXEndList.required_version(), ProtocolVersion::V1); } } diff --git a/src/tags/media_playlist/i_frames_only.rs b/src/tags/media_playlist/i_frames_only.rs index dfe3abc..796c68f 100644 --- a/src/tags/media_playlist/i_frames_only.rs +++ b/src/tags/media_playlist/i_frames_only.rs @@ -1,7 +1,7 @@ use std::fmt; use std::str::FromStr; -use crate::types::ProtocolVersion; +use crate::types::{ProtocolVersion, RequiredVersion}; use crate::utils::tag; use crate::Error; @@ -13,9 +13,10 @@ pub struct ExtXIFramesOnly; impl ExtXIFramesOnly { pub(crate) const PREFIX: &'static str = "#EXT-X-I-FRAMES-ONLY"; +} - /// Returns the protocol compatibility version that this tag requires. - pub const fn requires_version(self) -> ProtocolVersion { +impl RequiredVersion for ExtXIFramesOnly { + fn required_version(&self) -> ProtocolVersion { ProtocolVersion::V4 } } @@ -40,11 +41,20 @@ mod test { use super::*; #[test] - fn ext_i_frames_only() { - let tag = ExtXIFramesOnly; - let text = "#EXT-X-I-FRAMES-ONLY"; - assert_eq!(text.parse().ok(), Some(tag)); - assert_eq!(tag.to_string(), text); - assert_eq!(tag.requires_version(), ProtocolVersion::V4); + fn test_display() { + assert_eq!( + ExtXIFramesOnly.to_string(), + "#EXT-X-I-FRAMES-ONLY".to_string(), + ) + } + + #[test] + fn test_parser() { + assert_eq!(ExtXIFramesOnly, "#EXT-X-I-FRAMES-ONLY".parse().unwrap(),) + } + + #[test] + fn test_required_version() { + assert_eq!(ExtXIFramesOnly.required_version(), ProtocolVersion::V4) } } diff --git a/src/tags/media_playlist/media_sequence.rs b/src/tags/media_playlist/media_sequence.rs index 1f328f0..db4301e 100644 --- a/src/tags/media_playlist/media_sequence.rs +++ b/src/tags/media_playlist/media_sequence.rs @@ -1,7 +1,7 @@ use std::fmt; use std::str::FromStr; -use crate::types::ProtocolVersion; +use crate::types::{ProtocolVersion, RequiredVersion}; use crate::utils::tag; use crate::Error; @@ -9,32 +9,32 @@ use crate::Error; /// /// [4.3.3.2. EXT-X-MEDIA-SEQUENCE]: https://tools.ietf.org/html/rfc8216#section-4.3.3.2 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct ExtXMediaSequence { - seq_num: u64, -} +pub struct ExtXMediaSequence(u64); impl ExtXMediaSequence { pub(crate) const PREFIX: &'static str = "#EXT-X-MEDIA-SEQUENCE:"; /// Makes a new `ExtXMediaSequence` tag. pub const fn new(seq_num: u64) -> Self { - ExtXMediaSequence { seq_num } + Self(seq_num) } - /// Returns the sequence number of the first media segment that appears in the associated playlist. - pub const fn seq_num(self) -> u64 { - self.seq_num + /// Returns the sequence number of the first media segment, + /// that appears in the associated playlist. + pub const fn seq_num(&self) -> u64 { + self.0 } +} - /// Returns the protocol compatibility version that this tag requires. - pub const fn requires_version(self) -> ProtocolVersion { +impl RequiredVersion for ExtXMediaSequence { + fn required_version(&self) -> ProtocolVersion { ProtocolVersion::V1 } } impl fmt::Display for ExtXMediaSequence { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}{}", Self::PREFIX, self.seq_num) + write!(f, "{}{}", Self::PREFIX, self.0) } } @@ -43,7 +43,6 @@ impl FromStr for ExtXMediaSequence { fn from_str(input: &str) -> Result { let seq_num = tag(input, Self::PREFIX)?.parse()?; - Ok(ExtXMediaSequence::new(seq_num)) } } @@ -53,11 +52,26 @@ mod test { use super::*; #[test] - fn ext_x_media_sequence() { - let tag = ExtXMediaSequence::new(123); - let text = "#EXT-X-MEDIA-SEQUENCE:123"; - assert_eq!(text.parse().ok(), Some(tag)); - assert_eq!(tag.to_string(), text); - assert_eq!(tag.requires_version(), ProtocolVersion::V1); + fn test_display() { + assert_eq!( + ExtXMediaSequence::new(123).to_string(), + "#EXT-X-MEDIA-SEQUENCE:123".to_string() + ); + } + + #[test] + fn test_required_version() { + assert_eq!( + ExtXMediaSequence::new(123).required_version(), + ProtocolVersion::V1 + ); + } + + #[test] + fn test_parser() { + assert_eq!( + ExtXMediaSequence::new(123), + "#EXT-X-MEDIA-SEQUENCE:123".parse().unwrap() + ); } } diff --git a/src/tags/media_playlist/playlist_type.rs b/src/tags/media_playlist/playlist_type.rs index 221914f..e0b7ce8 100644 --- a/src/tags/media_playlist/playlist_type.rs +++ b/src/tags/media_playlist/playlist_type.rs @@ -1,7 +1,7 @@ use std::fmt; use std::str::FromStr; -use crate::types::ProtocolVersion; +use crate::types::{ProtocolVersion, RequiredVersion}; use crate::utils::tag; use crate::Error; @@ -31,9 +31,10 @@ pub enum ExtXPlaylistType { impl ExtXPlaylistType { pub(crate) const PREFIX: &'static str = "#EXT-X-PLAYLIST-TYPE:"; +} - /// Returns the protocol compatibility version that this tag requires. - pub const fn requires_version(&self) -> ProtocolVersion { +impl RequiredVersion for ExtXPlaylistType { + fn required_version(&self) -> ProtocolVersion { ProtocolVersion::V1 } } @@ -99,13 +100,13 @@ mod test { } #[test] - fn test_requires_version() { + fn test_required_version() { assert_eq!( - ExtXPlaylistType::Vod.requires_version(), + ExtXPlaylistType::Vod.required_version(), ProtocolVersion::V1 ); assert_eq!( - ExtXPlaylistType::Event.requires_version(), + ExtXPlaylistType::Event.required_version(), ProtocolVersion::V1 ); } diff --git a/src/tags/media_playlist/target_duration.rs b/src/tags/media_playlist/target_duration.rs index 71b0c66..522616d 100644 --- a/src/tags/media_playlist/target_duration.rs +++ b/src/tags/media_playlist/target_duration.rs @@ -2,7 +2,7 @@ use std::fmt; use std::str::FromStr; use std::time::Duration; -use crate::types::ProtocolVersion; +use crate::types::{ProtocolVersion, RequiredVersion}; use crate::utils::tag; use crate::Error; @@ -10,9 +10,7 @@ use crate::Error; /// /// [4.3.3.1. EXT-X-TARGETDURATION]: https://tools.ietf.org/html/rfc8216#section-4.3.3.1 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] -pub struct ExtXTargetDuration { - duration: Duration, -} +pub struct ExtXTargetDuration(Duration); impl ExtXTargetDuration { pub(crate) const PREFIX: &'static str = "#EXT-X-TARGETDURATION:"; @@ -21,24 +19,24 @@ impl ExtXTargetDuration { /// /// Note that the nanoseconds part of the `duration` will be discarded. pub const fn new(duration: Duration) -> Self { - let duration = Duration::from_secs(duration.as_secs()); - ExtXTargetDuration { duration } + Self(Duration::from_secs(duration.as_secs())) } /// Returns the maximum media segment duration in the associated playlist. pub const fn duration(&self) -> Duration { - self.duration + self.0 } +} - /// Returns the protocol compatibility version that this tag requires. - pub const fn requires_version(&self) -> ProtocolVersion { +impl RequiredVersion for ExtXTargetDuration { + fn required_version(&self) -> ProtocolVersion { ProtocolVersion::V1 } } impl fmt::Display for ExtXTargetDuration { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}{}", Self::PREFIX, self.duration.as_secs()) + write!(f, "{}{}", Self::PREFIX, self.0.as_secs()) } } @@ -47,9 +45,7 @@ impl FromStr for ExtXTargetDuration { fn from_str(input: &str) -> Result { let input = tag(input, Self::PREFIX)?.parse()?; - Ok(ExtXTargetDuration { - duration: Duration::from_secs(input), - }) + Ok(Self::new(Duration::from_secs(input))) } } @@ -58,11 +54,26 @@ mod test { use super::*; #[test] - fn ext_x_targetduration() { - let tag = ExtXTargetDuration::new(Duration::from_secs(5)); - let text = "#EXT-X-TARGETDURATION:5"; - assert_eq!(text.parse().ok(), Some(tag)); - assert_eq!(tag.to_string(), text); - assert_eq!(tag.requires_version(), ProtocolVersion::V1); + fn test_display() { + assert_eq!( + ExtXTargetDuration::new(Duration::from_secs(5)).to_string(), + "#EXT-X-TARGETDURATION:5".to_string() + ); + } + + #[test] + fn test_required_version() { + assert_eq!( + ExtXTargetDuration::new(Duration::from_secs(5)).required_version(), + ProtocolVersion::V1 + ); + } + + #[test] + fn test_parser() { + assert_eq!( + ExtXTargetDuration::new(Duration::from_secs(5)), + "#EXT-X-TARGETDURATION:5".parse().unwrap() + ); } } diff --git a/src/tags/media_segment/byte_range.rs b/src/tags/media_segment/byte_range.rs index c1bf2ce..e0d02b7 100644 --- a/src/tags/media_segment/byte_range.rs +++ b/src/tags/media_segment/byte_range.rs @@ -2,7 +2,7 @@ use std::fmt; use std::ops::Deref; use std::str::FromStr; -use crate::types::{ByteRange, ProtocolVersion}; +use crate::types::{ByteRange, ProtocolVersion, RequiredVersion}; use crate::utils::tag; use crate::Error; @@ -16,6 +16,7 @@ impl ExtXByteRange { pub(crate) const PREFIX: &'static str = "#EXT-X-BYTERANGE:"; /// Makes a new `ExtXByteRange` tag. + /// /// # Example /// ``` /// use hls_m3u8::tags::ExtXByteRange; @@ -27,6 +28,7 @@ impl ExtXByteRange { } /// Converts the [ExtXByteRange] to a [ByteRange]. + /// /// # Example /// ``` /// use hls_m3u8::tags::ExtXByteRange; @@ -38,17 +40,10 @@ impl ExtXByteRange { pub const fn to_range(&self) -> ByteRange { self.0 } +} - /// Returns the protocol compatibility version that this tag requires. - /// # Example - /// ``` - /// use hls_m3u8::tags::ExtXByteRange; - /// use hls_m3u8::types::ProtocolVersion; - /// - /// let byte_range = ExtXByteRange::new(20, Some(5)); - /// assert_eq!(byte_range.requires_version(), ProtocolVersion::V4); - /// ``` - pub const fn requires_version(&self) -> ProtocolVersion { +impl RequiredVersion for ExtXByteRange { + fn required_version(&self) -> ProtocolVersion { ProtocolVersion::V4 } } @@ -76,6 +71,7 @@ impl FromStr for ExtXByteRange { let input = tag(input, Self::PREFIX)?; let tokens = input.splitn(2, '@').collect::>(); + if tokens.is_empty() { return Err(Error::invalid_input()); } @@ -135,4 +131,12 @@ mod test { assert_eq!(byte_range.length(), 0); assert_eq!(byte_range.start(), Some(22)); } + + #[test] + fn test_required_version() { + assert_eq!( + ExtXByteRange::new(20, Some(5)).required_version(), + ProtocolVersion::V4 + ); + } } diff --git a/src/tags/media_segment/date_range.rs b/src/tags/media_segment/date_range.rs index 4f17db3..2b6619a 100644 --- a/src/tags/media_segment/date_range.rs +++ b/src/tags/media_segment/date_range.rs @@ -6,7 +6,7 @@ use std::time::Duration; use chrono::{DateTime, FixedOffset}; use crate::attribute::AttributePairs; -use crate::types::{DecimalFloatingPoint, ProtocolVersion}; +use crate::types::{DecimalFloatingPoint, ProtocolVersion, RequiredVersion}; use crate::utils::{quote, tag, unquote}; use crate::Error; @@ -63,9 +63,10 @@ pub struct ExtXDateRange { impl ExtXDateRange { pub(crate) const PREFIX: &'static str = "#EXT-X-DATERANGE:"; +} - /// Returns the protocol compatibility version that this tag requires. - pub const fn requires_version(&self) -> ProtocolVersion { +impl RequiredVersion for ExtXDateRange { + fn required_version(&self) -> ProtocolVersion { ProtocolVersion::V1 } } diff --git a/src/tags/media_segment/discontinuity.rs b/src/tags/media_segment/discontinuity.rs index 9571f2c..2289a24 100644 --- a/src/tags/media_segment/discontinuity.rs +++ b/src/tags/media_segment/discontinuity.rs @@ -1,9 +1,9 @@ use std::fmt; use std::str::FromStr; -use crate::types::ProtocolVersion; +use crate::types::{ProtocolVersion, RequiredVersion}; use crate::utils::tag; -use crate::{Error, Result}; +use crate::Error; /// [4.3.2.3. EXT-X-DISCONTINUITY] /// @@ -13,9 +13,10 @@ pub struct ExtXDiscontinuity; impl ExtXDiscontinuity { pub(crate) const PREFIX: &'static str = "#EXT-X-DISCONTINUITY"; +} - /// Returns the protocol compatibility version that this tag requires. - pub const fn requires_version(self) -> ProtocolVersion { +impl RequiredVersion for ExtXDiscontinuity { + fn required_version(&self) -> ProtocolVersion { ProtocolVersion::V1 } } @@ -29,7 +30,7 @@ impl fmt::Display for ExtXDiscontinuity { impl FromStr for ExtXDiscontinuity { type Err = Error; - fn from_str(input: &str) -> Result { + fn from_str(input: &str) -> Result { tag(input, Self::PREFIX)?; Ok(ExtXDiscontinuity) } @@ -40,10 +41,20 @@ mod test { use super::*; #[test] - fn ext_x_discontinuity() { - let tag = ExtXDiscontinuity; - assert_eq!("#EXT-X-DISCONTINUITY".parse().ok(), Some(tag)); - assert_eq!(tag.to_string(), "#EXT-X-DISCONTINUITY"); - assert_eq!(tag.requires_version(), ProtocolVersion::V1); + fn test_display() { + assert_eq!( + ExtXDiscontinuity.to_string(), + "#EXT-X-DISCONTINUITY".to_string(), + ) + } + + #[test] + fn test_parser() { + assert_eq!(ExtXDiscontinuity, "#EXT-X-DISCONTINUITY".parse().unwrap(),) + } + + #[test] + fn test_required_version() { + assert_eq!(ExtXDiscontinuity.required_version(), ProtocolVersion::V1) } } diff --git a/src/tags/media_segment/inf.rs b/src/tags/media_segment/inf.rs index 287864b..af0178a 100644 --- a/src/tags/media_segment/inf.rs +++ b/src/tags/media_segment/inf.rs @@ -2,7 +2,7 @@ use std::fmt; use std::str::FromStr; use std::time::Duration; -use crate::types::{DecimalFloatingPoint, ProtocolVersion}; +use crate::types::{DecimalFloatingPoint, ProtocolVersion, RequiredVersion}; use crate::utils::tag; use crate::Error; @@ -78,9 +78,10 @@ impl ExtInf { pub fn title(&self) -> Option<&String> { self.title.as_ref() } +} - /// Returns the protocol compatibility version that this tag requires. - pub fn requires_version(&self) -> ProtocolVersion { +impl RequiredVersion for ExtInf { + fn required_version(&self) -> ProtocolVersion { if self.duration.subsec_nanos() == 0 { ProtocolVersion::V1 } else { @@ -206,13 +207,13 @@ mod test { } #[test] - fn test_requires_version() { + fn test_required_version() { assert_eq!( - ExtInf::new(Duration::from_secs(4)).requires_version(), + ExtInf::new(Duration::from_secs(4)).required_version(), ProtocolVersion::V1 ); assert_eq!( - ExtInf::new(Duration::from_millis(4400)).requires_version(), + ExtInf::new(Duration::from_millis(4400)).required_version(), ProtocolVersion::V3 ); } diff --git a/src/tags/media_segment/map.rs b/src/tags/media_segment/map.rs index df6659c..d680f56 100644 --- a/src/tags/media_segment/map.rs +++ b/src/tags/media_segment/map.rs @@ -2,7 +2,7 @@ use std::fmt; use std::str::FromStr; use crate::attribute::AttributePairs; -use crate::types::{ByteRange, ProtocolVersion}; +use crate::types::{ByteRange, ProtocolVersion, RequiredVersion}; use crate::utils::{quote, tag, unquote}; use crate::Error; @@ -43,9 +43,10 @@ impl ExtXMap { pub const fn range(&self) -> Option { self.range } +} - /// Returns the protocol compatibility version that this tag requires. - pub const fn requires_version(&self) -> ProtocolVersion { +impl RequiredVersion for ExtXMap { + fn required_version(&self) -> ProtocolVersion { ProtocolVersion::V6 } } @@ -93,19 +94,37 @@ mod test { use super::*; #[test] - fn ext_x_map() { - let tag = ExtXMap::new("foo"); - let text = r#"#EXT-X-MAP:URI="foo""#; - assert_eq!(text.parse().ok(), Some(tag.clone())); - assert_eq!(tag.to_string(), text); - assert_eq!(tag.requires_version(), ProtocolVersion::V6); + fn test_display() { + assert_eq!( + ExtXMap::new("foo").to_string(), + "#EXT-X-MAP:URI=\"foo\"".to_string(), + ); - let tag = ExtXMap::with_range("foo", ByteRange::new(9, Some(2))); - let text = r#"#EXT-X-MAP:URI="foo",BYTERANGE="9@2""#; - ExtXMap::from_str(text).unwrap(); + assert_eq!( + ExtXMap::with_range("foo", ByteRange::new(9, Some(2))).to_string(), + "#EXT-X-MAP:URI=\"foo\",BYTERANGE=\"9@2\"".to_string(), + ); + } - assert_eq!(text.parse().ok(), Some(tag.clone())); - assert_eq!(tag.to_string(), text); - assert_eq!(tag.requires_version(), ProtocolVersion::V6); + #[test] + fn test_parser() { + assert_eq!( + ExtXMap::new("foo"), + "#EXT-X-MAP:URI=\"foo\"".parse().unwrap() + ); + + assert_eq!( + ExtXMap::with_range("foo", ByteRange::new(9, Some(2))), + "#EXT-X-MAP:URI=\"foo\",BYTERANGE=\"9@2\"".parse().unwrap() + ); + } + + #[test] + fn test_required_version() { + assert_eq!(ExtXMap::new("foo").required_version(), ProtocolVersion::V6); + assert_eq!( + ExtXMap::with_range("foo", ByteRange::new(9, Some(2))).required_version(), + ProtocolVersion::V6 + ); } } diff --git a/src/tags/media_segment/program_date_time.rs b/src/tags/media_segment/program_date_time.rs index 643ec4b..2f852f6 100644 --- a/src/tags/media_segment/program_date_time.rs +++ b/src/tags/media_segment/program_date_time.rs @@ -3,7 +3,7 @@ use std::str::FromStr; use chrono::{DateTime, FixedOffset}; -use crate::types::ProtocolVersion; +use crate::types::{ProtocolVersion, RequiredVersion}; use crate::utils::tag; use crate::Error; @@ -25,9 +25,10 @@ impl ExtXProgramDateTime { pub const fn date_time(&self) -> &DateTime { &self.0 } +} - /// Returns the protocol compatibility version that this tag requires. - pub const fn requires_version(&self) -> ProtocolVersion { +impl RequiredVersion for ExtXProgramDateTime { + fn required_version(&self) -> ProtocolVersion { ProtocolVersion::V1 } } @@ -45,7 +46,6 @@ impl FromStr for ExtXProgramDateTime { fn from_str(input: &str) -> Result { let input = tag(input, Self::PREFIX)?; - // TODO: parse with chrono let date_time = DateTime::parse_from_rfc3339(input)?; Ok(Self::new(date_time)) } @@ -54,6 +54,9 @@ impl FromStr for ExtXProgramDateTime { #[cfg(test)] mod test { use super::*; + use chrono::TimeZone; + + const HOURS_IN_SECS: i32 = 3600; // 1 hour = 3600 seconds #[test] fn test_display() { @@ -71,19 +74,26 @@ mod test { #[test] fn test_parser() { - "#EXT-X-PROGRAM-DATE-TIME:2010-02-19T14:54:23.031+08:00" - .parse::() - .unwrap(); + assert_eq!( + ExtXProgramDateTime::new( + FixedOffset::east(8 * HOURS_IN_SECS) + .ymd(2010, 2, 19) + .and_hms_milli(14, 54, 23, 31) + ), + "#EXT-X-PROGRAM-DATE-TIME:2010-02-19T14:54:23.031+08:00" + .parse::() + .unwrap() + ); } #[test] - fn test_requires_version() { - let date_time = "2010-02-19T14:54:23.031+08:00" - .parse::>() - .unwrap(); + fn test_required_version() { + let program_date_time = ExtXProgramDateTime::new( + FixedOffset::east(8 * HOURS_IN_SECS) + .ymd(2010, 2, 19) + .and_hms_milli(14, 54, 23, 31), + ); - let program_date_time = ExtXProgramDateTime::new(date_time); - - assert_eq!(program_date_time.requires_version(), ProtocolVersion::V1); + assert_eq!(program_date_time.required_version(), ProtocolVersion::V1); } } diff --git a/src/tags/shared/independent_segments.rs b/src/tags/shared/independent_segments.rs index 5268dcd..7967a8d 100644 --- a/src/tags/shared/independent_segments.rs +++ b/src/tags/shared/independent_segments.rs @@ -1,7 +1,7 @@ use std::fmt; use std::str::FromStr; -use crate::types::ProtocolVersion; +use crate::types::{ProtocolVersion, RequiredVersion}; use crate::utils::tag; use crate::Error; @@ -10,11 +10,13 @@ use crate::Error; /// [4.3.5.1. EXT-X-INDEPENDENT-SEGMENTS]: https://tools.ietf.org/html/rfc8216#section-4.3.5.1 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct ExtXIndependentSegments; + impl ExtXIndependentSegments { pub(crate) const PREFIX: &'static str = "#EXT-X-INDEPENDENT-SEGMENTS"; +} - /// Returns the protocol compatibility version that this tag requires. - pub const fn requires_version(&self) -> ProtocolVersion { +impl RequiredVersion for ExtXIndependentSegments { + fn required_version(&self) -> ProtocolVersion { ProtocolVersion::V1 } } @@ -39,11 +41,26 @@ mod test { use super::*; #[test] - fn ext_x_independent_segments() { - let tag = ExtXIndependentSegments; - let text = "#EXT-X-INDEPENDENT-SEGMENTS"; - assert_eq!(text.parse().ok(), Some(tag)); - assert_eq!(tag.to_string(), text); - assert_eq!(tag.requires_version(), ProtocolVersion::V1); + fn test_display() { + assert_eq!( + ExtXIndependentSegments.to_string(), + "#EXT-X-INDEPENDENT-SEGMENTS".to_string(), + ) + } + + #[test] + fn test_parser() { + assert_eq!( + ExtXIndependentSegments, + "#EXT-X-INDEPENDENT-SEGMENTS".parse().unwrap(), + ) + } + + #[test] + fn test_required_version() { + assert_eq!( + ExtXIndependentSegments.required_version(), + ProtocolVersion::V1 + ) } } diff --git a/src/tags/shared/start.rs b/src/tags/shared/start.rs index a4c594a..e30fa9c 100644 --- a/src/tags/shared/start.rs +++ b/src/tags/shared/start.rs @@ -2,7 +2,7 @@ use std::fmt; use std::str::FromStr; use crate::attribute::AttributePairs; -use crate::types::{ProtocolVersion, SignedDecimalFloatingPoint}; +use crate::types::{ProtocolVersion, RequiredVersion, SignedDecimalFloatingPoint}; use crate::utils::{parse_yes_or_no, tag}; use crate::Error; @@ -19,6 +19,7 @@ impl ExtXStart { pub(crate) const PREFIX: &'static str = "#EXT-X-START:"; /// Makes a new `ExtXStart` tag. + /// /// # Panic /// Panics if the time_offset value is infinite. pub fn new(time_offset: f64) -> Self { @@ -33,6 +34,7 @@ impl ExtXStart { } /// Makes a new `ExtXStart` tag with the given `precise` flag. + /// /// # Panic /// Panics if the time_offset value is infinite. pub fn with_precise(time_offset: f64, precise: bool) -> Self { @@ -56,9 +58,10 @@ impl ExtXStart { pub const fn precise(&self) -> bool { self.precise } +} - /// Returns the protocol compatibility version that this tag requires. - pub const fn requires_version(&self) -> ProtocolVersion { +impl RequiredVersion for ExtXStart { + fn required_version(&self) -> ProtocolVersion { ProtocolVersion::V1 } } @@ -108,17 +111,41 @@ mod test { use super::*; #[test] - fn ext_x_start() { - let tag = ExtXStart::new(-1.23); - let text = "#EXT-X-START:TIME-OFFSET=-1.23"; - assert_eq!(text.parse().ok(), Some(tag)); - assert_eq!(tag.to_string(), text); - assert_eq!(tag.requires_version(), ProtocolVersion::V1); + fn test_display() { + assert_eq!( + ExtXStart::new(-1.23).to_string(), + "#EXT-X-START:TIME-OFFSET=-1.23".to_string(), + ); - let tag = ExtXStart::with_precise(1.23, true); - let text = "#EXT-X-START:TIME-OFFSET=1.23,PRECISE=YES"; - assert_eq!(text.parse().ok(), Some(tag)); - assert_eq!(tag.to_string(), text); - assert_eq!(tag.requires_version(), ProtocolVersion::V1); + assert_eq!( + ExtXStart::with_precise(1.23, true).to_string(), + "#EXT-X-START:TIME-OFFSET=1.23,PRECISE=YES".to_string(), + ); + } + + #[test] + fn test_required_version() { + assert_eq!( + ExtXStart::new(-1.23).required_version(), + ProtocolVersion::V1, + ); + + assert_eq!( + ExtXStart::with_precise(1.23, true).required_version(), + ProtocolVersion::V1, + ); + } + + #[test] + fn test_parser() { + assert_eq!( + ExtXStart::new(-1.23), + "#EXT-X-START:TIME-OFFSET=-1.23".parse().unwrap(), + ); + + assert_eq!( + ExtXStart::with_precise(1.23, true), + "#EXT-X-START:TIME-OFFSET=1.23,PRECISE=YES".parse().unwrap(), + ); } } diff --git a/src/types/decimal_floating_point.rs b/src/types/decimal_floating_point.rs index af08b24..8cff64a 100644 --- a/src/types/decimal_floating_point.rs +++ b/src/types/decimal_floating_point.rs @@ -70,6 +70,18 @@ impl FromStr for DecimalFloatingPoint { } } +impl From for DecimalFloatingPoint { + fn from(value: f64) -> Self { + Self(value) + } +} + +impl From for DecimalFloatingPoint { + fn from(value: f32) -> Self { + Self(value.into()) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/types/decryption_key.rs b/src/types/decryption_key.rs index 8e2ec86..438eace 100644 --- a/src/types/decryption_key.rs +++ b/src/types/decryption_key.rs @@ -4,7 +4,7 @@ use std::str::FromStr; use derive_builder::Builder; use crate::attribute::AttributePairs; -use crate::types::{EncryptionMethod, InitializationVector, ProtocolVersion}; +use crate::types::{EncryptionMethod, InitializationVector, ProtocolVersion, RequiredVersion}; use crate::utils::{quote, unquote}; use crate::Error; @@ -292,23 +292,10 @@ impl DecryptionKey { pub fn set_key_format_versions(&mut self, value: T) { self.key_format_versions = Some(value.to_string()); } +} - /// Returns the protocol compatibility version that this tag requires. - /// # Example - /// ``` - /// use hls_m3u8::types::{EncryptionMethod, ProtocolVersion, DecryptionKey}; - /// - /// let mut key = DecryptionKey::new( - /// EncryptionMethod::Aes128, - /// "https://www.example.com/" - /// ); - /// - /// assert_eq!( - /// key.requires_version(), - /// ProtocolVersion::V1 - /// ); - /// ``` - pub fn requires_version(&self) -> ProtocolVersion { +impl RequiredVersion for DecryptionKey { + fn required_version(&self) -> ProtocolVersion { if self.key_format.is_some() || self.key_format_versions.is_some() { ProtocolVersion::V5 } else if self.iv.is_some() { @@ -464,4 +451,13 @@ mod test { key ) } + + #[test] + fn test_required_version() { + assert_eq!( + DecryptionKey::new(EncryptionMethod::Aes128, "https://www.example.com/") + .required_version(), + ProtocolVersion::V1 + ) + } } diff --git a/src/types/protocol_version.rs b/src/types/protocol_version.rs index 1d54938..a39747d 100644 --- a/src/types/protocol_version.rs +++ b/src/types/protocol_version.rs @@ -3,6 +3,30 @@ use std::str::FromStr; use crate::Error; +/// # Example +/// Implementing it: +/// ``` +/// # use hls_m3u8::types::{ProtocolVersion, RequiredVersion}; +/// # +/// struct NewTag(u64); +/// +/// impl RequiredVersion for NewTag { +/// fn required_version(&self) -> ProtocolVersion { +/// if self.0 == 5 { +/// ProtocolVersion::V4 +/// } else { +/// ProtocolVersion::V1 +/// } +/// } +/// } +/// assert_eq!(NewTag(5).required_version(), ProtocolVersion::V4); +/// assert_eq!(NewTag(2).required_version(), ProtocolVersion::V1); +/// ``` +pub trait RequiredVersion { + /// Returns the protocol compatibility version that this tag requires. + fn required_version(&self) -> ProtocolVersion; +} + /// [7. Protocol Version Compatibility] /// /// [7. Protocol Version Compatibility]: https://tools.ietf.org/html/rfc8216#section-7 @@ -29,13 +53,13 @@ impl fmt::Display for ProtocolVersion { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let n = { match &self { - ProtocolVersion::V1 => 1, - ProtocolVersion::V2 => 2, - ProtocolVersion::V3 => 3, - ProtocolVersion::V4 => 4, - ProtocolVersion::V5 => 5, - ProtocolVersion::V6 => 6, - ProtocolVersion::V7 => 7, + Self::V1 => 1, + Self::V2 => 2, + Self::V3 => 3, + Self::V4 => 4, + Self::V5 => 5, + Self::V6 => 6, + Self::V7 => 7, } }; write!(f, "{}", n) @@ -47,16 +71,49 @@ impl FromStr for ProtocolVersion { fn from_str(input: &str) -> Result { Ok({ - match input { - "1" => ProtocolVersion::V1, - "2" => ProtocolVersion::V2, - "3" => ProtocolVersion::V3, - "4" => ProtocolVersion::V4, - "5" => ProtocolVersion::V5, - "6" => ProtocolVersion::V6, - "7" => ProtocolVersion::V7, + match input.trim() { + "1" => Self::V1, + "2" => Self::V2, + "3" => Self::V3, + "4" => Self::V4, + "5" => Self::V5, + "6" => Self::V6, + "7" => Self::V7, _ => return Err(Error::unknown_protocol_version(input)), } }) } } + +impl Default for ProtocolVersion { + fn default() -> Self { + Self::V1 + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_display() { + assert_eq!(ProtocolVersion::V1.to_string(), "1".to_string()); + assert_eq!(ProtocolVersion::V2.to_string(), "2".to_string()); + assert_eq!(ProtocolVersion::V3.to_string(), "3".to_string()); + assert_eq!(ProtocolVersion::V4.to_string(), "4".to_string()); + assert_eq!(ProtocolVersion::V5.to_string(), "5".to_string()); + assert_eq!(ProtocolVersion::V6.to_string(), "6".to_string()); + assert_eq!(ProtocolVersion::V7.to_string(), "7".to_string()); + } + + #[test] + fn test_parser() { + assert_eq!(ProtocolVersion::V1, "1".parse().unwrap()); + assert_eq!(ProtocolVersion::V2, "2".parse().unwrap()); + assert_eq!(ProtocolVersion::V3, "3".parse().unwrap()); + assert_eq!(ProtocolVersion::V4, "4".parse().unwrap()); + assert_eq!(ProtocolVersion::V5, "5".parse().unwrap()); + assert_eq!(ProtocolVersion::V6, "6".parse().unwrap()); + assert_eq!(ProtocolVersion::V7, "7".parse().unwrap()); + } +}