1
0
Fork 0
mirror of https://github.com/sile/hls_m3u8.git synced 2024-11-26 00:51:00 +00:00

added RequiredVersion trait

This commit is contained in:
Luro02 2019-09-22 10:57:28 +02:00
parent b2c9f2db36
commit 81f9a421fe
28 changed files with 550 additions and 293 deletions

View file

@ -61,10 +61,10 @@ impl FromStr for AttributePairs {
let key = pair[0].to_uppercase(); let key = pair[0].to_uppercase();
let value = pair[1].to_string(); 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); dbg!(&result);
Ok(result) Ok(result)
} }

View file

@ -10,7 +10,7 @@ use crate::tags::{
ExtM3u, ExtXIFrameStreamInf, ExtXIndependentSegments, ExtXMedia, ExtXSessionData, ExtM3u, ExtXIFrameStreamInf, ExtXIndependentSegments, ExtXMedia, ExtXSessionData,
ExtXSessionKey, ExtXStart, ExtXStreamInf, ExtXVersion, ExtXSessionKey, ExtXStart, ExtXStreamInf, ExtXVersion,
}; };
use crate::types::{ClosedCaptions, MediaType, ProtocolVersion}; use crate::types::{ClosedCaptions, MediaType, ProtocolVersion, RequiredVersion};
use crate::Error; use crate::Error;
/// Master playlist. /// Master playlist.
@ -92,6 +92,12 @@ impl MasterPlaylist {
} }
} }
impl RequiredVersion for MasterPlaylist {
fn required_version(&self) -> ProtocolVersion {
self.version_tag.version()
}
}
impl MasterPlaylistBuilder { impl MasterPlaylistBuilder {
fn validate(&self) -> Result<(), String> { fn validate(&self) -> Result<(), String> {
let required_version = self.required_version(); let required_version = self.required_version();
@ -118,43 +124,43 @@ impl MasterPlaylistBuilder {
.chain( .chain(
self.independent_segments_tag self.independent_segments_tag
.iter() .iter()
.map(|t| t.iter().map(|t| t.requires_version())) .map(|t| t.iter().map(|t| t.required_version()))
.flatten(), .flatten(),
) )
.chain( .chain(
self.start_tag self.start_tag
.iter() .iter()
.map(|t| t.iter().map(|t| t.requires_version())) .map(|t| t.iter().map(|t| t.required_version()))
.flatten(), .flatten(),
) )
.chain( .chain(
self.media_tags self.media_tags
.iter() .iter()
.map(|t| t.iter().map(|t| t.requires_version())) .map(|t| t.iter().map(|t| t.required_version()))
.flatten(), .flatten(),
) )
.chain( .chain(
self.stream_inf_tags self.stream_inf_tags
.iter() .iter()
.map(|t| t.iter().map(|t| t.requires_version())) .map(|t| t.iter().map(|t| t.required_version()))
.flatten(), .flatten(),
) )
.chain( .chain(
self.i_frame_stream_inf_tags self.i_frame_stream_inf_tags
.iter() .iter()
.map(|t| t.iter().map(|t| t.requires_version())) .map(|t| t.iter().map(|t| t.required_version()))
.flatten(), .flatten(),
) )
.chain( .chain(
self.session_data_tags self.session_data_tags
.iter() .iter()
.map(|t| t.iter().map(|t| t.requires_version())) .map(|t| t.iter().map(|t| t.required_version()))
.flatten(), .flatten(),
) )
.chain( .chain(
self.session_key_tags self.session_key_tags
.iter() .iter()
.map(|t| t.iter().map(|t| t.requires_version())) .map(|t| t.iter().map(|t| t.required_version()))
.flatten(), .flatten(),
) )
.max() .max()

View file

@ -11,7 +11,7 @@ use crate::tags::{
ExtM3u, ExtXDiscontinuitySequence, ExtXEndList, ExtXIFramesOnly, ExtXIndependentSegments, ExtM3u, ExtXDiscontinuitySequence, ExtXEndList, ExtXIFramesOnly, ExtXIndependentSegments,
ExtXMediaSequence, ExtXPlaylistType, ExtXStart, ExtXTargetDuration, ExtXVersion, ExtXMediaSequence, ExtXPlaylistType, ExtXStart, ExtXTargetDuration, ExtXVersion,
}; };
use crate::types::ProtocolVersion; use crate::types::{ProtocolVersion, RequiredVersion};
use crate::Error; use crate::Error;
/// Media playlist. /// Media playlist.
@ -142,60 +142,60 @@ impl MediaPlaylistBuilder {
.chain( .chain(
self.target_duration_tag self.target_duration_tag
.iter() .iter()
.map(|t| t.requires_version()), .map(|t| t.required_version()),
) )
.chain(self.media_sequence_tag.iter().map(|t| { .chain(self.media_sequence_tag.iter().map(|t| {
if let Some(p) = t { if let Some(p) = t {
p.requires_version() p.required_version()
} else { } else {
ProtocolVersion::V1 ProtocolVersion::V1
} }
})) }))
.chain(self.discontinuity_sequence_tag.iter().map(|t| { .chain(self.discontinuity_sequence_tag.iter().map(|t| {
if let Some(p) = t { if let Some(p) = t {
p.requires_version() p.required_version()
} else { } else {
ProtocolVersion::V1 ProtocolVersion::V1
} }
})) }))
.chain(self.playlist_type_tag.iter().map(|t| { .chain(self.playlist_type_tag.iter().map(|t| {
if let Some(p) = t { if let Some(p) = t {
p.requires_version() p.required_version()
} else { } else {
ProtocolVersion::V1 ProtocolVersion::V1
} }
})) }))
.chain(self.i_frames_only_tag.iter().map(|t| { .chain(self.i_frames_only_tag.iter().map(|t| {
if let Some(p) = t { if let Some(p) = t {
p.requires_version() p.required_version()
} else { } else {
ProtocolVersion::V1 ProtocolVersion::V1
} }
})) }))
.chain(self.independent_segments_tag.iter().map(|t| { .chain(self.independent_segments_tag.iter().map(|t| {
if let Some(p) = t { if let Some(p) = t {
p.requires_version() p.required_version()
} else { } else {
ProtocolVersion::V1 ProtocolVersion::V1
} }
})) }))
.chain(self.start_tag.iter().map(|t| { .chain(self.start_tag.iter().map(|t| {
if let Some(p) = t { if let Some(p) = t {
p.requires_version() p.required_version()
} else { } else {
ProtocolVersion::V1 ProtocolVersion::V1
} }
})) }))
.chain(self.end_list_tag.iter().map(|t| { .chain(self.end_list_tag.iter().map(|t| {
if let Some(p) = t { if let Some(p) = t {
p.requires_version() p.required_version()
} else { } else {
ProtocolVersion::V1 ProtocolVersion::V1
} }
})) }))
.chain(self.segments.iter().map(|t| { .chain(self.segments.iter().map(|t| {
t.iter() t.iter()
.map(|s| s.requires_version()) .map(|s| s.required_version())
.max() .max()
.unwrap_or(ProtocolVersion::V1) .unwrap_or(ProtocolVersion::V1)
})) }))

View file

@ -6,7 +6,7 @@ use derive_builder::Builder;
use crate::tags::{ use crate::tags::{
ExtInf, ExtXByteRange, ExtXDateRange, ExtXDiscontinuity, ExtXKey, ExtXMap, ExtXProgramDateTime, ExtInf, ExtXByteRange, ExtXDateRange, ExtXDiscontinuity, ExtXKey, ExtXMap, ExtXProgramDateTime,
}; };
use crate::types::ProtocolVersion; use crate::types::{ProtocolVersion, RequiredVersion};
/// Media segment. /// Media segment.
#[derive(Debug, Clone, Builder)] #[derive(Debug, Clone, Builder)]
@ -118,21 +118,22 @@ impl MediaSegment {
pub fn key_tags(&self) -> &[ExtXKey] { pub fn key_tags(&self) -> &[ExtXKey] {
&self.key_tags &self.key_tags
} }
}
/// Returns the protocol compatibility version that this segment requires. impl RequiredVersion for MediaSegment {
pub fn requires_version(&self) -> ProtocolVersion { fn required_version(&self) -> ProtocolVersion {
iter::empty() iter::empty()
.chain(self.key_tags.iter().map(|t| t.requires_version())) .chain(self.key_tags.iter().map(|t| t.required_version()))
.chain(self.map_tag.iter().map(|t| t.requires_version())) .chain(self.map_tag.iter().map(|t| t.required_version()))
.chain(self.byte_range_tag.iter().map(|t| t.requires_version())) .chain(self.byte_range_tag.iter().map(|t| t.required_version()))
.chain(self.date_range_tag.iter().map(|t| t.requires_version())) .chain(self.date_range_tag.iter().map(|t| t.required_version()))
.chain(self.discontinuity_tag.iter().map(|t| t.requires_version())) .chain(self.discontinuity_tag.iter().map(|t| t.required_version()))
.chain( .chain(
self.program_date_time_tag self.program_date_time_tag
.iter() .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() .max()
.unwrap_or(ProtocolVersion::V7) .unwrap_or(ProtocolVersion::V7)
} }

View file

@ -1,7 +1,7 @@
use std::fmt; use std::fmt;
use std::str::FromStr; use std::str::FromStr;
use crate::types::ProtocolVersion; use crate::types::{ProtocolVersion, RequiredVersion};
use crate::utils::tag; use crate::utils::tag;
use crate::Error; use crate::Error;
@ -13,9 +13,10 @@ pub struct ExtM3u;
impl ExtM3u { impl ExtM3u {
pub(crate) const PREFIX: &'static str = "#EXTM3U"; pub(crate) const PREFIX: &'static str = "#EXTM3U";
}
/// Returns the protocol compatibility version that this tag requires. impl RequiredVersion for ExtM3u {
pub const fn requires_version(&self) -> ProtocolVersion { fn required_version(&self) -> ProtocolVersion {
ProtocolVersion::V1 ProtocolVersion::V1
} }
} }
@ -50,7 +51,7 @@ mod test {
} }
#[test] #[test]
fn test_requires_version() { fn test_required_version() {
assert_eq!(ExtM3u.requires_version(), ProtocolVersion::V1); assert_eq!(ExtM3u.required_version(), ProtocolVersion::V1);
} }
} }

View file

@ -1,7 +1,7 @@
use std::fmt; use std::fmt;
use std::str::FromStr; use std::str::FromStr;
use crate::types::ProtocolVersion; use crate::types::{ProtocolVersion, RequiredVersion};
use crate::utils::tag; use crate::utils::tag;
use crate::Error; use crate::Error;
@ -20,12 +20,24 @@ impl ExtXVersion {
} }
/// Returns the protocol compatibility version of the playlist containing this tag. /// 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 { pub const fn version(&self) -> ProtocolVersion {
self.0 self.0
} }
}
/// Returns the protocol compatibility version that this tag requires. impl RequiredVersion for ExtXVersion {
pub const fn requires_version(&self) -> ProtocolVersion { fn required_version(&self) -> ProtocolVersion {
ProtocolVersion::V1 ProtocolVersion::V1
} }
} }
@ -78,18 +90,10 @@ mod test {
} }
#[test] #[test]
fn test_requires_version() { fn test_required_version() {
assert_eq!( assert_eq!(
ExtXVersion::new(ProtocolVersion::V6).requires_version(), ExtXVersion::new(ProtocolVersion::V6).required_version(),
ProtocolVersion::V1 ProtocolVersion::V1
); );
} }
#[test]
fn test_version() {
assert_eq!(
ExtXVersion::new(ProtocolVersion::V6).version(),
ProtocolVersion::V6
);
}
} }

View file

@ -3,12 +3,23 @@ use std::ops::{Deref, DerefMut};
use std::str::FromStr; use std::str::FromStr;
use crate::attribute::AttributePairs; use crate::attribute::AttributePairs;
use crate::types::{ProtocolVersion, StreamInf}; use crate::types::{ProtocolVersion, RequiredVersion, StreamInf};
use crate::utils::{quote, tag, unquote}; use crate::utils::{quote, tag, unquote};
use crate::Error; 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:<attribute-list>
/// ```
///
/// [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 /// [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)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ExtXIFrameStreamInf { pub struct ExtXIFrameStreamInf {
@ -19,7 +30,7 @@ pub struct ExtXIFrameStreamInf {
impl ExtXIFrameStreamInf { impl ExtXIFrameStreamInf {
pub(crate) const PREFIX: &'static str = "#EXT-X-I-FRAME-STREAM-INF:"; 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<T: ToString>(uri: T, bandwidth: u64) -> Self { pub fn new<T: ToString>(uri: T, bandwidth: u64) -> Self {
ExtXIFrameStreamInf { ExtXIFrameStreamInf {
uri: uri.to_string(), uri: uri.to_string(),
@ -55,9 +66,10 @@ impl ExtXIFrameStreamInf {
self.uri = value.to_string(); self.uri = value.to_string();
self self
} }
}
/// Returns the protocol compatibility version that this tag requires. impl RequiredVersion for ExtXIFrameStreamInf {
pub const fn requires_version(&self) -> ProtocolVersion { fn required_version(&self) -> ProtocolVersion {
ProtocolVersion::V1 ProtocolVersion::V1
} }
} }
@ -133,9 +145,9 @@ mod test {
} }
#[test] #[test]
fn test_requires_version() { fn test_required_version() {
assert_eq!( assert_eq!(
ExtXIFrameStreamInf::new("foo", 1000).requires_version(), ExtXIFrameStreamInf::new("foo", 1000).required_version(),
ProtocolVersion::V1 ProtocolVersion::V1
); );
} }

View file

@ -2,7 +2,7 @@ use std::fmt;
use std::str::FromStr; use std::str::FromStr;
use crate::attribute::AttributePairs; 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::utils::{parse_yes_or_no, quote, tag, unquote};
use crate::Error; use crate::Error;
@ -273,9 +273,10 @@ impl ExtXMedia {
pub fn channels(&self) -> Option<&String> { pub fn channels(&self) -> Option<&String> {
self.channels.as_ref() self.channels.as_ref()
} }
}
/// Returns the protocol compatibility version that this tag requires. impl RequiredVersion for ExtXMedia {
pub fn requires_version(&self) -> ProtocolVersion { fn required_version(&self) -> ProtocolVersion {
match self.instream_id { match self.instream_id {
None None
| Some(InStreamId::Cc1) | Some(InStreamId::Cc1)
@ -385,11 +386,28 @@ mod test {
use super::*; use super::*;
#[test] #[test]
fn ext_x_media() { fn test_display() {
let tag = ExtXMedia::new(MediaType::Audio, "foo", "bar"); assert_eq!(
let text = r#"#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="foo",NAME="bar""#; ExtXMedia::new(MediaType::Audio, "foo", "bar").to_string(),
assert_eq!(text.parse().ok(), Some(tag.clone())); "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"foo\",NAME=\"bar\"".to_string()
assert_eq!(tag.to_string(), text); )
assert_eq!(tag.requires_version(), ProtocolVersion::V1); }
#[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
)
} }
} }

View file

@ -4,7 +4,7 @@ use std::str::FromStr;
use derive_builder::Builder; use derive_builder::Builder;
use crate::attribute::AttributePairs; use crate::attribute::AttributePairs;
use crate::types::ProtocolVersion; use crate::types::{ProtocolVersion, RequiredVersion};
use crate::utils::{quote, tag, unquote}; use crate::utils::{quote, tag, unquote};
use crate::Error; use crate::Error;
@ -243,18 +243,10 @@ impl ExtXSessionData {
self.data = value; self.data = value;
self self
} }
}
/// Returns the protocol compatibility version, that this tag requires. impl RequiredVersion for ExtXSessionData {
/// fn required_version(&self) -> ProtocolVersion {
/// # 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 {
ProtocolVersion::V1 ProtocolVersion::V1
} }
} }

View file

@ -2,7 +2,7 @@ use std::fmt;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use std::str::FromStr; use std::str::FromStr;
use crate::types::{DecryptionKey, EncryptionMethod, ProtocolVersion}; use crate::types::{DecryptionKey, EncryptionMethod, ProtocolVersion, RequiredVersion};
use crate::utils::tag; use crate::utils::tag;
use crate::Error; use crate::Error;
@ -25,24 +25,10 @@ impl ExtXSessionKey {
Self(DecryptionKey::new(method, uri)) Self(DecryptionKey::new(method, uri))
} }
}
/// Returns the protocol compatibility version that this tag requires. impl RequiredVersion for ExtXSessionKey {
/// # Example fn required_version(&self) -> ProtocolVersion {
/// ```
/// 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 {
if self.0.key_format.is_some() | self.0.key_format_versions.is_some() { if self.0.key_format.is_some() | self.0.key_format_versions.is_some() {
ProtocolVersion::V5 ProtocolVersion::V5
} else if self.0.iv.is_some() { } else if self.0.iv.is_some() {
@ -143,4 +129,13 @@ mod test {
key key
) )
} }
#[test]
fn test_required_version() {
assert_eq!(
ExtXSessionKey::new(EncryptionMethod::Aes128, "https://www.example.com/")
.required_version(),
ProtocolVersion::V1
);
}
} }

View file

@ -3,7 +3,9 @@ use std::ops::{Deref, DerefMut};
use std::str::FromStr; use std::str::FromStr;
use crate::attribute::AttributePairs; 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::utils::{quote, tag, unquote};
use crate::Error; use crate::Error;
@ -23,7 +25,9 @@ pub struct ExtXStreamInf {
impl ExtXStreamInf { impl ExtXStreamInf {
pub(crate) const PREFIX: &'static str = "#EXT-X-STREAM-INF:"; 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; /// # use hls_m3u8::tags::ExtXStreamInf;
/// # /// #
@ -40,11 +44,21 @@ impl ExtXStreamInf {
} }
} }
pub fn set_uri<T: ToString>(&mut self, value: T) -> &mut Self {
self.uri = value.to_string();
self
}
/// Returns the URI that identifies the associated media playlist. /// Returns the URI that identifies the associated media playlist.
pub const fn uri(&self) -> &String { pub const fn uri(&self) -> &String {
&self.uri &self.uri
} }
pub fn set_frame_rate(&mut self, value: Option<f64>) -> &mut Self {
self.frame_rate = value.map(|v| v.into());
self
}
/// Returns the maximum frame rate for all the video in the variant stream. /// Returns the maximum frame rate for all the video in the variant stream.
pub fn frame_rate(&self) -> Option<f64> { pub fn frame_rate(&self) -> Option<f64> {
self.frame_rate.map_or(None, |v| Some(v.as_f64())) self.frame_rate.map_or(None, |v| Some(v.as_f64()))
@ -64,9 +78,10 @@ impl ExtXStreamInf {
pub fn closed_captions(&self) -> Option<&ClosedCaptions> { pub fn closed_captions(&self) -> Option<&ClosedCaptions> {
self.closed_captions.as_ref() self.closed_captions.as_ref()
} }
}
/// Returns the protocol compatibility version that this tag requires. impl RequiredVersion for ExtXStreamInf {
pub const fn requires_version(&self) -> ProtocolVersion { fn required_version(&self) -> ProtocolVersion {
ProtocolVersion::V1 ProtocolVersion::V1
} }
} }
@ -158,10 +173,10 @@ mod test {
} }
#[test] #[test]
fn test_requires_version() { fn test_required_version() {
assert_eq!( assert_eq!(
ProtocolVersion::V1, ProtocolVersion::V1,
ExtXStreamInf::new("http://www.example.com", 1000).requires_version() ExtXStreamInf::new("http://www.example.com", 1000).required_version()
); );
} }

View file

@ -1,40 +1,39 @@
use std::fmt; use std::fmt;
use std::str::FromStr; use std::str::FromStr;
use crate::types::ProtocolVersion; use crate::types::{ProtocolVersion, RequiredVersion};
use crate::utils::tag; use crate::utils::tag;
/// [4.3.3.3. EXT-X-DISCONTINUITY-SEQUENCE] /// [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 /// [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)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub struct ExtXDiscontinuitySequence { pub struct ExtXDiscontinuitySequence(u64);
seq_num: u64,
}
impl ExtXDiscontinuitySequence { impl ExtXDiscontinuitySequence {
pub(crate) const PREFIX: &'static str = "#EXT-X-DISCONTINUITY-SEQUENCE:"; pub(crate) const PREFIX: &'static str = "#EXT-X-DISCONTINUITY-SEQUENCE:";
/// Makes a new `ExtXDiscontinuitySequence` tag. /// Makes a new `ExtXDiscontinuitySequence` tag.
pub const fn new(seq_num: u64) -> Self { pub const fn new(seq_num: u64) -> Self {
ExtXDiscontinuitySequence { seq_num } Self(seq_num)
} }
/// Returns the discontinuity sequence number of /// Returns the discontinuity sequence number of
/// the first media segment that appears in the associated playlist. /// the first media segment that appears in the associated playlist.
pub const fn seq_num(self) -> u64 { pub const fn seq_num(&self) -> u64 {
self.seq_num self.0
} }
}
/// Returns the protocol compatibility version that this tag requires. impl RequiredVersion for ExtXDiscontinuitySequence {
pub const fn requires_version(self) -> ProtocolVersion { fn required_version(&self) -> ProtocolVersion {
ProtocolVersion::V1 ProtocolVersion::V1
} }
} }
impl fmt::Display for ExtXDiscontinuitySequence { impl fmt::Display for ExtXDiscontinuitySequence {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 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; type Err = crate::Error;
fn from_str(input: &str) -> Result<Self, Self::Err> { fn from_str(input: &str) -> Result<Self, Self::Err> {
let seq_num = tag(input, Self::PREFIX)?.parse().unwrap(); // TODO! let seq_num = tag(input, Self::PREFIX)?.parse()?;
Ok(Self::new(seq_num)) Ok(Self::new(seq_num))
} }
} }
@ -52,11 +51,26 @@ mod test {
use super::*; use super::*;
#[test] #[test]
fn ext_x_discontinuity_sequence() { fn test_display() {
let tag = ExtXDiscontinuitySequence::new(123); assert_eq!(
let text = "#EXT-X-DISCONTINUITY-SEQUENCE:123"; ExtXDiscontinuitySequence::new(123).to_string(),
assert_eq!(text.parse().ok(), Some(tag)); "#EXT-X-DISCONTINUITY-SEQUENCE:123".to_string()
assert_eq!(tag.to_string(), text); );
assert_eq!(tag.requires_version(), ProtocolVersion::V1); }
#[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()
);
} }
} }

View file

@ -1,7 +1,7 @@
use std::fmt; use std::fmt;
use std::str::FromStr; use std::str::FromStr;
use crate::types::ProtocolVersion; use crate::types::{ProtocolVersion, RequiredVersion};
use crate::utils::tag; use crate::utils::tag;
use crate::Error; 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 /// [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)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ExtXEndList; pub struct ExtXEndList;
impl ExtXEndList { impl ExtXEndList {
pub(crate) const PREFIX: &'static str = "#EXT-X-ENDLIST"; pub(crate) const PREFIX: &'static str = "#EXT-X-ENDLIST";
}
/// Returns the protocol compatibility version that this tag requires. impl RequiredVersion for ExtXEndList {
pub const fn requires_version(self) -> ProtocolVersion { fn required_version(&self) -> ProtocolVersion {
ProtocolVersion::V1 ProtocolVersion::V1
} }
} }
@ -39,11 +41,17 @@ mod test {
use super::*; use super::*;
#[test] #[test]
fn ext_x_endlist() { fn test_display() {
let tag = ExtXEndList; assert_eq!(ExtXEndList.to_string(), "#EXT-X-ENDLIST".to_string());
let text = "#EXT-X-ENDLIST"; }
assert_eq!(text.parse().ok(), Some(tag));
assert_eq!(tag.to_string(), text); #[test]
assert_eq!(tag.requires_version(), ProtocolVersion::V1); fn test_parser() {
assert_eq!(ExtXEndList, "#EXT-X-ENDLIST".parse().unwrap());
}
#[test]
fn test_required_version() {
assert_eq!(ExtXEndList.required_version(), ProtocolVersion::V1);
} }
} }

View file

@ -1,7 +1,7 @@
use std::fmt; use std::fmt;
use std::str::FromStr; use std::str::FromStr;
use crate::types::ProtocolVersion; use crate::types::{ProtocolVersion, RequiredVersion};
use crate::utils::tag; use crate::utils::tag;
use crate::Error; use crate::Error;
@ -13,9 +13,10 @@ pub struct ExtXIFramesOnly;
impl ExtXIFramesOnly { impl ExtXIFramesOnly {
pub(crate) const PREFIX: &'static str = "#EXT-X-I-FRAMES-ONLY"; pub(crate) const PREFIX: &'static str = "#EXT-X-I-FRAMES-ONLY";
}
/// Returns the protocol compatibility version that this tag requires. impl RequiredVersion for ExtXIFramesOnly {
pub const fn requires_version(self) -> ProtocolVersion { fn required_version(&self) -> ProtocolVersion {
ProtocolVersion::V4 ProtocolVersion::V4
} }
} }
@ -40,11 +41,20 @@ mod test {
use super::*; use super::*;
#[test] #[test]
fn ext_i_frames_only() { fn test_display() {
let tag = ExtXIFramesOnly; assert_eq!(
let text = "#EXT-X-I-FRAMES-ONLY"; ExtXIFramesOnly.to_string(),
assert_eq!(text.parse().ok(), Some(tag)); "#EXT-X-I-FRAMES-ONLY".to_string(),
assert_eq!(tag.to_string(), text); )
assert_eq!(tag.requires_version(), ProtocolVersion::V4); }
#[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)
} }
} }

View file

@ -1,7 +1,7 @@
use std::fmt; use std::fmt;
use std::str::FromStr; use std::str::FromStr;
use crate::types::ProtocolVersion; use crate::types::{ProtocolVersion, RequiredVersion};
use crate::utils::tag; use crate::utils::tag;
use crate::Error; 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 /// [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)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ExtXMediaSequence { pub struct ExtXMediaSequence(u64);
seq_num: u64,
}
impl ExtXMediaSequence { impl ExtXMediaSequence {
pub(crate) const PREFIX: &'static str = "#EXT-X-MEDIA-SEQUENCE:"; pub(crate) const PREFIX: &'static str = "#EXT-X-MEDIA-SEQUENCE:";
/// Makes a new `ExtXMediaSequence` tag. /// Makes a new `ExtXMediaSequence` tag.
pub const fn new(seq_num: u64) -> Self { 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. /// Returns the sequence number of the first media segment,
pub const fn seq_num(self) -> u64 { /// that appears in the associated playlist.
self.seq_num pub const fn seq_num(&self) -> u64 {
self.0
} }
}
/// Returns the protocol compatibility version that this tag requires. impl RequiredVersion for ExtXMediaSequence {
pub const fn requires_version(self) -> ProtocolVersion { fn required_version(&self) -> ProtocolVersion {
ProtocolVersion::V1 ProtocolVersion::V1
} }
} }
impl fmt::Display for ExtXMediaSequence { impl fmt::Display for ExtXMediaSequence {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 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<Self, Self::Err> { fn from_str(input: &str) -> Result<Self, Self::Err> {
let seq_num = tag(input, Self::PREFIX)?.parse()?; let seq_num = tag(input, Self::PREFIX)?.parse()?;
Ok(ExtXMediaSequence::new(seq_num)) Ok(ExtXMediaSequence::new(seq_num))
} }
} }
@ -53,11 +52,26 @@ mod test {
use super::*; use super::*;
#[test] #[test]
fn ext_x_media_sequence() { fn test_display() {
let tag = ExtXMediaSequence::new(123); assert_eq!(
let text = "#EXT-X-MEDIA-SEQUENCE:123"; ExtXMediaSequence::new(123).to_string(),
assert_eq!(text.parse().ok(), Some(tag)); "#EXT-X-MEDIA-SEQUENCE:123".to_string()
assert_eq!(tag.to_string(), text); );
assert_eq!(tag.requires_version(), ProtocolVersion::V1); }
#[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()
);
} }
} }

View file

@ -1,7 +1,7 @@
use std::fmt; use std::fmt;
use std::str::FromStr; use std::str::FromStr;
use crate::types::ProtocolVersion; use crate::types::{ProtocolVersion, RequiredVersion};
use crate::utils::tag; use crate::utils::tag;
use crate::Error; use crate::Error;
@ -31,9 +31,10 @@ pub enum ExtXPlaylistType {
impl ExtXPlaylistType { impl ExtXPlaylistType {
pub(crate) const PREFIX: &'static str = "#EXT-X-PLAYLIST-TYPE:"; pub(crate) const PREFIX: &'static str = "#EXT-X-PLAYLIST-TYPE:";
}
/// Returns the protocol compatibility version that this tag requires. impl RequiredVersion for ExtXPlaylistType {
pub const fn requires_version(&self) -> ProtocolVersion { fn required_version(&self) -> ProtocolVersion {
ProtocolVersion::V1 ProtocolVersion::V1
} }
} }
@ -99,13 +100,13 @@ mod test {
} }
#[test] #[test]
fn test_requires_version() { fn test_required_version() {
assert_eq!( assert_eq!(
ExtXPlaylistType::Vod.requires_version(), ExtXPlaylistType::Vod.required_version(),
ProtocolVersion::V1 ProtocolVersion::V1
); );
assert_eq!( assert_eq!(
ExtXPlaylistType::Event.requires_version(), ExtXPlaylistType::Event.required_version(),
ProtocolVersion::V1 ProtocolVersion::V1
); );
} }

View file

@ -2,7 +2,7 @@ use std::fmt;
use std::str::FromStr; use std::str::FromStr;
use std::time::Duration; use std::time::Duration;
use crate::types::ProtocolVersion; use crate::types::{ProtocolVersion, RequiredVersion};
use crate::utils::tag; use crate::utils::tag;
use crate::Error; 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 /// [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)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct ExtXTargetDuration { pub struct ExtXTargetDuration(Duration);
duration: Duration,
}
impl ExtXTargetDuration { impl ExtXTargetDuration {
pub(crate) const PREFIX: &'static str = "#EXT-X-TARGETDURATION:"; 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. /// Note that the nanoseconds part of the `duration` will be discarded.
pub const fn new(duration: Duration) -> Self { pub const fn new(duration: Duration) -> Self {
let duration = Duration::from_secs(duration.as_secs()); Self(Duration::from_secs(duration.as_secs()))
ExtXTargetDuration { duration }
} }
/// Returns the maximum media segment duration in the associated playlist. /// Returns the maximum media segment duration in the associated playlist.
pub const fn duration(&self) -> Duration { pub const fn duration(&self) -> Duration {
self.duration self.0
} }
}
/// Returns the protocol compatibility version that this tag requires. impl RequiredVersion for ExtXTargetDuration {
pub const fn requires_version(&self) -> ProtocolVersion { fn required_version(&self) -> ProtocolVersion {
ProtocolVersion::V1 ProtocolVersion::V1
} }
} }
impl fmt::Display for ExtXTargetDuration { impl fmt::Display for ExtXTargetDuration {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 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<Self, Self::Err> { fn from_str(input: &str) -> Result<Self, Self::Err> {
let input = tag(input, Self::PREFIX)?.parse()?; let input = tag(input, Self::PREFIX)?.parse()?;
Ok(ExtXTargetDuration { Ok(Self::new(Duration::from_secs(input)))
duration: Duration::from_secs(input),
})
} }
} }
@ -58,11 +54,26 @@ mod test {
use super::*; use super::*;
#[test] #[test]
fn ext_x_targetduration() { fn test_display() {
let tag = ExtXTargetDuration::new(Duration::from_secs(5)); assert_eq!(
let text = "#EXT-X-TARGETDURATION:5"; ExtXTargetDuration::new(Duration::from_secs(5)).to_string(),
assert_eq!(text.parse().ok(), Some(tag)); "#EXT-X-TARGETDURATION:5".to_string()
assert_eq!(tag.to_string(), text); );
assert_eq!(tag.requires_version(), ProtocolVersion::V1); }
#[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()
);
} }
} }

View file

@ -2,7 +2,7 @@ use std::fmt;
use std::ops::Deref; use std::ops::Deref;
use std::str::FromStr; use std::str::FromStr;
use crate::types::{ByteRange, ProtocolVersion}; use crate::types::{ByteRange, ProtocolVersion, RequiredVersion};
use crate::utils::tag; use crate::utils::tag;
use crate::Error; use crate::Error;
@ -16,6 +16,7 @@ impl ExtXByteRange {
pub(crate) const PREFIX: &'static str = "#EXT-X-BYTERANGE:"; pub(crate) const PREFIX: &'static str = "#EXT-X-BYTERANGE:";
/// Makes a new `ExtXByteRange` tag. /// Makes a new `ExtXByteRange` tag.
///
/// # Example /// # Example
/// ``` /// ```
/// use hls_m3u8::tags::ExtXByteRange; /// use hls_m3u8::tags::ExtXByteRange;
@ -27,6 +28,7 @@ impl ExtXByteRange {
} }
/// Converts the [ExtXByteRange] to a [ByteRange]. /// Converts the [ExtXByteRange] to a [ByteRange].
///
/// # Example /// # Example
/// ``` /// ```
/// use hls_m3u8::tags::ExtXByteRange; /// use hls_m3u8::tags::ExtXByteRange;
@ -38,17 +40,10 @@ impl ExtXByteRange {
pub const fn to_range(&self) -> ByteRange { pub const fn to_range(&self) -> ByteRange {
self.0 self.0
} }
}
/// Returns the protocol compatibility version that this tag requires. impl RequiredVersion for ExtXByteRange {
/// # Example fn required_version(&self) -> ProtocolVersion {
/// ```
/// 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 {
ProtocolVersion::V4 ProtocolVersion::V4
} }
} }
@ -76,6 +71,7 @@ impl FromStr for ExtXByteRange {
let input = tag(input, Self::PREFIX)?; let input = tag(input, Self::PREFIX)?;
let tokens = input.splitn(2, '@').collect::<Vec<_>>(); let tokens = input.splitn(2, '@').collect::<Vec<_>>();
if tokens.is_empty() { if tokens.is_empty() {
return Err(Error::invalid_input()); return Err(Error::invalid_input());
} }
@ -135,4 +131,12 @@ mod test {
assert_eq!(byte_range.length(), 0); assert_eq!(byte_range.length(), 0);
assert_eq!(byte_range.start(), Some(22)); assert_eq!(byte_range.start(), Some(22));
} }
#[test]
fn test_required_version() {
assert_eq!(
ExtXByteRange::new(20, Some(5)).required_version(),
ProtocolVersion::V4
);
}
} }

View file

@ -6,7 +6,7 @@ use std::time::Duration;
use chrono::{DateTime, FixedOffset}; use chrono::{DateTime, FixedOffset};
use crate::attribute::AttributePairs; use crate::attribute::AttributePairs;
use crate::types::{DecimalFloatingPoint, ProtocolVersion}; use crate::types::{DecimalFloatingPoint, ProtocolVersion, RequiredVersion};
use crate::utils::{quote, tag, unquote}; use crate::utils::{quote, tag, unquote};
use crate::Error; use crate::Error;
@ -63,9 +63,10 @@ pub struct ExtXDateRange {
impl ExtXDateRange { impl ExtXDateRange {
pub(crate) const PREFIX: &'static str = "#EXT-X-DATERANGE:"; pub(crate) const PREFIX: &'static str = "#EXT-X-DATERANGE:";
}
/// Returns the protocol compatibility version that this tag requires. impl RequiredVersion for ExtXDateRange {
pub const fn requires_version(&self) -> ProtocolVersion { fn required_version(&self) -> ProtocolVersion {
ProtocolVersion::V1 ProtocolVersion::V1
} }
} }

View file

@ -1,9 +1,9 @@
use std::fmt; use std::fmt;
use std::str::FromStr; use std::str::FromStr;
use crate::types::ProtocolVersion; use crate::types::{ProtocolVersion, RequiredVersion};
use crate::utils::tag; use crate::utils::tag;
use crate::{Error, Result}; use crate::Error;
/// [4.3.2.3. EXT-X-DISCONTINUITY] /// [4.3.2.3. EXT-X-DISCONTINUITY]
/// ///
@ -13,9 +13,10 @@ pub struct ExtXDiscontinuity;
impl ExtXDiscontinuity { impl ExtXDiscontinuity {
pub(crate) const PREFIX: &'static str = "#EXT-X-DISCONTINUITY"; pub(crate) const PREFIX: &'static str = "#EXT-X-DISCONTINUITY";
}
/// Returns the protocol compatibility version that this tag requires. impl RequiredVersion for ExtXDiscontinuity {
pub const fn requires_version(self) -> ProtocolVersion { fn required_version(&self) -> ProtocolVersion {
ProtocolVersion::V1 ProtocolVersion::V1
} }
} }
@ -29,7 +30,7 @@ impl fmt::Display for ExtXDiscontinuity {
impl FromStr for ExtXDiscontinuity { impl FromStr for ExtXDiscontinuity {
type Err = Error; type Err = Error;
fn from_str(input: &str) -> Result<Self> { fn from_str(input: &str) -> Result<Self, Self::Err> {
tag(input, Self::PREFIX)?; tag(input, Self::PREFIX)?;
Ok(ExtXDiscontinuity) Ok(ExtXDiscontinuity)
} }
@ -40,10 +41,20 @@ mod test {
use super::*; use super::*;
#[test] #[test]
fn ext_x_discontinuity() { fn test_display() {
let tag = ExtXDiscontinuity; assert_eq!(
assert_eq!("#EXT-X-DISCONTINUITY".parse().ok(), Some(tag)); ExtXDiscontinuity.to_string(),
assert_eq!(tag.to_string(), "#EXT-X-DISCONTINUITY"); "#EXT-X-DISCONTINUITY".to_string(),
assert_eq!(tag.requires_version(), ProtocolVersion::V1); )
}
#[test]
fn test_parser() {
assert_eq!(ExtXDiscontinuity, "#EXT-X-DISCONTINUITY".parse().unwrap(),)
}
#[test]
fn test_required_version() {
assert_eq!(ExtXDiscontinuity.required_version(), ProtocolVersion::V1)
} }
} }

View file

@ -2,7 +2,7 @@ use std::fmt;
use std::str::FromStr; use std::str::FromStr;
use std::time::Duration; use std::time::Duration;
use crate::types::{DecimalFloatingPoint, ProtocolVersion}; use crate::types::{DecimalFloatingPoint, ProtocolVersion, RequiredVersion};
use crate::utils::tag; use crate::utils::tag;
use crate::Error; use crate::Error;
@ -78,9 +78,10 @@ impl ExtInf {
pub fn title(&self) -> Option<&String> { pub fn title(&self) -> Option<&String> {
self.title.as_ref() self.title.as_ref()
} }
}
/// Returns the protocol compatibility version that this tag requires. impl RequiredVersion for ExtInf {
pub fn requires_version(&self) -> ProtocolVersion { fn required_version(&self) -> ProtocolVersion {
if self.duration.subsec_nanos() == 0 { if self.duration.subsec_nanos() == 0 {
ProtocolVersion::V1 ProtocolVersion::V1
} else { } else {
@ -206,13 +207,13 @@ mod test {
} }
#[test] #[test]
fn test_requires_version() { fn test_required_version() {
assert_eq!( assert_eq!(
ExtInf::new(Duration::from_secs(4)).requires_version(), ExtInf::new(Duration::from_secs(4)).required_version(),
ProtocolVersion::V1 ProtocolVersion::V1
); );
assert_eq!( assert_eq!(
ExtInf::new(Duration::from_millis(4400)).requires_version(), ExtInf::new(Duration::from_millis(4400)).required_version(),
ProtocolVersion::V3 ProtocolVersion::V3
); );
} }

View file

@ -2,7 +2,7 @@ use std::fmt;
use std::str::FromStr; use std::str::FromStr;
use crate::attribute::AttributePairs; use crate::attribute::AttributePairs;
use crate::types::{ByteRange, ProtocolVersion}; use crate::types::{ByteRange, ProtocolVersion, RequiredVersion};
use crate::utils::{quote, tag, unquote}; use crate::utils::{quote, tag, unquote};
use crate::Error; use crate::Error;
@ -43,9 +43,10 @@ impl ExtXMap {
pub const fn range(&self) -> Option<ByteRange> { pub const fn range(&self) -> Option<ByteRange> {
self.range self.range
} }
}
/// Returns the protocol compatibility version that this tag requires. impl RequiredVersion for ExtXMap {
pub const fn requires_version(&self) -> ProtocolVersion { fn required_version(&self) -> ProtocolVersion {
ProtocolVersion::V6 ProtocolVersion::V6
} }
} }
@ -93,19 +94,37 @@ mod test {
use super::*; use super::*;
#[test] #[test]
fn ext_x_map() { fn test_display() {
let tag = ExtXMap::new("foo"); assert_eq!(
let text = r#"#EXT-X-MAP:URI="foo""#; ExtXMap::new("foo").to_string(),
assert_eq!(text.parse().ok(), Some(tag.clone())); "#EXT-X-MAP:URI=\"foo\"".to_string(),
assert_eq!(tag.to_string(), text); );
assert_eq!(tag.requires_version(), ProtocolVersion::V6);
let tag = ExtXMap::with_range("foo", ByteRange::new(9, Some(2))); assert_eq!(
let text = r#"#EXT-X-MAP:URI="foo",BYTERANGE="9@2""#; ExtXMap::with_range("foo", ByteRange::new(9, Some(2))).to_string(),
ExtXMap::from_str(text).unwrap(); "#EXT-X-MAP:URI=\"foo\",BYTERANGE=\"9@2\"".to_string(),
);
}
assert_eq!(text.parse().ok(), Some(tag.clone())); #[test]
assert_eq!(tag.to_string(), text); fn test_parser() {
assert_eq!(tag.requires_version(), ProtocolVersion::V6); 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
);
} }
} }

View file

@ -3,7 +3,7 @@ use std::str::FromStr;
use chrono::{DateTime, FixedOffset}; use chrono::{DateTime, FixedOffset};
use crate::types::ProtocolVersion; use crate::types::{ProtocolVersion, RequiredVersion};
use crate::utils::tag; use crate::utils::tag;
use crate::Error; use crate::Error;
@ -25,9 +25,10 @@ impl ExtXProgramDateTime {
pub const fn date_time(&self) -> &DateTime<FixedOffset> { pub const fn date_time(&self) -> &DateTime<FixedOffset> {
&self.0 &self.0
} }
}
/// Returns the protocol compatibility version that this tag requires. impl RequiredVersion for ExtXProgramDateTime {
pub const fn requires_version(&self) -> ProtocolVersion { fn required_version(&self) -> ProtocolVersion {
ProtocolVersion::V1 ProtocolVersion::V1
} }
} }
@ -45,7 +46,6 @@ impl FromStr for ExtXProgramDateTime {
fn from_str(input: &str) -> Result<Self, Self::Err> { fn from_str(input: &str) -> Result<Self, Self::Err> {
let input = tag(input, Self::PREFIX)?; let input = tag(input, Self::PREFIX)?;
// TODO: parse with chrono
let date_time = DateTime::parse_from_rfc3339(input)?; let date_time = DateTime::parse_from_rfc3339(input)?;
Ok(Self::new(date_time)) Ok(Self::new(date_time))
} }
@ -54,6 +54,9 @@ impl FromStr for ExtXProgramDateTime {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
use chrono::TimeZone;
const HOURS_IN_SECS: i32 = 3600; // 1 hour = 3600 seconds
#[test] #[test]
fn test_display() { fn test_display() {
@ -71,19 +74,26 @@ mod test {
#[test] #[test]
fn test_parser() { fn test_parser() {
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" "#EXT-X-PROGRAM-DATE-TIME:2010-02-19T14:54:23.031+08:00"
.parse::<ExtXProgramDateTime>() .parse::<ExtXProgramDateTime>()
.unwrap(); .unwrap()
);
} }
#[test] #[test]
fn test_requires_version() { fn test_required_version() {
let date_time = "2010-02-19T14:54:23.031+08:00" let program_date_time = ExtXProgramDateTime::new(
.parse::<DateTime<FixedOffset>>() FixedOffset::east(8 * HOURS_IN_SECS)
.unwrap(); .ymd(2010, 2, 19)
.and_hms_milli(14, 54, 23, 31),
);
let program_date_time = ExtXProgramDateTime::new(date_time); assert_eq!(program_date_time.required_version(), ProtocolVersion::V1);
assert_eq!(program_date_time.requires_version(), ProtocolVersion::V1);
} }
} }

View file

@ -1,7 +1,7 @@
use std::fmt; use std::fmt;
use std::str::FromStr; use std::str::FromStr;
use crate::types::ProtocolVersion; use crate::types::{ProtocolVersion, RequiredVersion};
use crate::utils::tag; use crate::utils::tag;
use crate::Error; 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 /// [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)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ExtXIndependentSegments; pub struct ExtXIndependentSegments;
impl ExtXIndependentSegments { impl ExtXIndependentSegments {
pub(crate) const PREFIX: &'static str = "#EXT-X-INDEPENDENT-SEGMENTS"; pub(crate) const PREFIX: &'static str = "#EXT-X-INDEPENDENT-SEGMENTS";
}
/// Returns the protocol compatibility version that this tag requires. impl RequiredVersion for ExtXIndependentSegments {
pub const fn requires_version(&self) -> ProtocolVersion { fn required_version(&self) -> ProtocolVersion {
ProtocolVersion::V1 ProtocolVersion::V1
} }
} }
@ -39,11 +41,26 @@ mod test {
use super::*; use super::*;
#[test] #[test]
fn ext_x_independent_segments() { fn test_display() {
let tag = ExtXIndependentSegments; assert_eq!(
let text = "#EXT-X-INDEPENDENT-SEGMENTS"; ExtXIndependentSegments.to_string(),
assert_eq!(text.parse().ok(), Some(tag)); "#EXT-X-INDEPENDENT-SEGMENTS".to_string(),
assert_eq!(tag.to_string(), text); )
assert_eq!(tag.requires_version(), ProtocolVersion::V1); }
#[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
)
} }
} }

View file

@ -2,7 +2,7 @@ use std::fmt;
use std::str::FromStr; use std::str::FromStr;
use crate::attribute::AttributePairs; 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::utils::{parse_yes_or_no, tag};
use crate::Error; use crate::Error;
@ -19,6 +19,7 @@ impl ExtXStart {
pub(crate) const PREFIX: &'static str = "#EXT-X-START:"; pub(crate) const PREFIX: &'static str = "#EXT-X-START:";
/// Makes a new `ExtXStart` tag. /// Makes a new `ExtXStart` tag.
///
/// # Panic /// # Panic
/// Panics if the time_offset value is infinite. /// Panics if the time_offset value is infinite.
pub fn new(time_offset: f64) -> Self { pub fn new(time_offset: f64) -> Self {
@ -33,6 +34,7 @@ impl ExtXStart {
} }
/// Makes a new `ExtXStart` tag with the given `precise` flag. /// Makes a new `ExtXStart` tag with the given `precise` flag.
///
/// # Panic /// # Panic
/// Panics if the time_offset value is infinite. /// Panics if the time_offset value is infinite.
pub fn with_precise(time_offset: f64, precise: bool) -> Self { pub fn with_precise(time_offset: f64, precise: bool) -> Self {
@ -56,9 +58,10 @@ impl ExtXStart {
pub const fn precise(&self) -> bool { pub const fn precise(&self) -> bool {
self.precise self.precise
} }
}
/// Returns the protocol compatibility version that this tag requires. impl RequiredVersion for ExtXStart {
pub const fn requires_version(&self) -> ProtocolVersion { fn required_version(&self) -> ProtocolVersion {
ProtocolVersion::V1 ProtocolVersion::V1
} }
} }
@ -108,17 +111,41 @@ mod test {
use super::*; use super::*;
#[test] #[test]
fn ext_x_start() { fn test_display() {
let tag = ExtXStart::new(-1.23); assert_eq!(
let text = "#EXT-X-START:TIME-OFFSET=-1.23"; ExtXStart::new(-1.23).to_string(),
assert_eq!(text.parse().ok(), Some(tag)); "#EXT-X-START:TIME-OFFSET=-1.23".to_string(),
assert_eq!(tag.to_string(), text); );
assert_eq!(tag.requires_version(), ProtocolVersion::V1);
let tag = ExtXStart::with_precise(1.23, true); assert_eq!(
let text = "#EXT-X-START:TIME-OFFSET=1.23,PRECISE=YES"; ExtXStart::with_precise(1.23, true).to_string(),
assert_eq!(text.parse().ok(), Some(tag)); "#EXT-X-START:TIME-OFFSET=1.23,PRECISE=YES".to_string(),
assert_eq!(tag.to_string(), text); );
assert_eq!(tag.requires_version(), ProtocolVersion::V1); }
#[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(),
);
} }
} }

View file

@ -70,6 +70,18 @@ impl FromStr for DecimalFloatingPoint {
} }
} }
impl From<f64> for DecimalFloatingPoint {
fn from(value: f64) -> Self {
Self(value)
}
}
impl From<f32> for DecimalFloatingPoint {
fn from(value: f32) -> Self {
Self(value.into())
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View file

@ -4,7 +4,7 @@ use std::str::FromStr;
use derive_builder::Builder; use derive_builder::Builder;
use crate::attribute::AttributePairs; use crate::attribute::AttributePairs;
use crate::types::{EncryptionMethod, InitializationVector, ProtocolVersion}; use crate::types::{EncryptionMethod, InitializationVector, ProtocolVersion, RequiredVersion};
use crate::utils::{quote, unquote}; use crate::utils::{quote, unquote};
use crate::Error; use crate::Error;
@ -292,23 +292,10 @@ impl DecryptionKey {
pub fn set_key_format_versions<T: ToString>(&mut self, value: T) { pub fn set_key_format_versions<T: ToString>(&mut self, value: T) {
self.key_format_versions = Some(value.to_string()); self.key_format_versions = Some(value.to_string());
} }
}
/// Returns the protocol compatibility version that this tag requires. impl RequiredVersion for DecryptionKey {
/// # Example fn required_version(&self) -> ProtocolVersion {
/// ```
/// 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 {
if self.key_format.is_some() || self.key_format_versions.is_some() { if self.key_format.is_some() || self.key_format_versions.is_some() {
ProtocolVersion::V5 ProtocolVersion::V5
} else if self.iv.is_some() { } else if self.iv.is_some() {
@ -464,4 +451,13 @@ mod test {
key key
) )
} }
#[test]
fn test_required_version() {
assert_eq!(
DecryptionKey::new(EncryptionMethod::Aes128, "https://www.example.com/")
.required_version(),
ProtocolVersion::V1
)
}
} }

View file

@ -3,6 +3,30 @@ use std::str::FromStr;
use crate::Error; 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]
/// ///
/// [7. Protocol Version Compatibility]: https://tools.ietf.org/html/rfc8216#section-7 /// [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 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let n = { let n = {
match &self { match &self {
ProtocolVersion::V1 => 1, Self::V1 => 1,
ProtocolVersion::V2 => 2, Self::V2 => 2,
ProtocolVersion::V3 => 3, Self::V3 => 3,
ProtocolVersion::V4 => 4, Self::V4 => 4,
ProtocolVersion::V5 => 5, Self::V5 => 5,
ProtocolVersion::V6 => 6, Self::V6 => 6,
ProtocolVersion::V7 => 7, Self::V7 => 7,
} }
}; };
write!(f, "{}", n) write!(f, "{}", n)
@ -47,16 +71,49 @@ impl FromStr for ProtocolVersion {
fn from_str(input: &str) -> Result<Self, Self::Err> { fn from_str(input: &str) -> Result<Self, Self::Err> {
Ok({ Ok({
match input { match input.trim() {
"1" => ProtocolVersion::V1, "1" => Self::V1,
"2" => ProtocolVersion::V2, "2" => Self::V2,
"3" => ProtocolVersion::V3, "3" => Self::V3,
"4" => ProtocolVersion::V4, "4" => Self::V4,
"5" => ProtocolVersion::V5, "5" => Self::V5,
"6" => ProtocolVersion::V6, "6" => Self::V6,
"7" => ProtocolVersion::V7, "7" => Self::V7,
_ => return Err(Error::unknown_protocol_version(input)), _ => 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());
}
}