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

Refactor types module

This commit is contained in:
Takeru Ohta 2018-02-14 23:11:25 +09:00
parent 80537c3e43
commit 9f6d4e7ed7
5 changed files with 169 additions and 136 deletions

View file

@ -4,8 +4,9 @@ use std::str::FromStr;
use {Error, ErrorKind, Result}; use {Error, ErrorKind, Result};
use attribute::{AttributePairs, DecimalFloatingPoint, DecimalInteger, DecimalResolution, use attribute::{AttributePairs, DecimalFloatingPoint, DecimalInteger, DecimalResolution,
QuotedString}; QuotedString};
use types::{ClosedCaptions, DecryptionKey, HdcpLevel, InStreamId, M3u8String, MediaType, use types::{ClosedCaptions, DecryptionKey, HdcpLevel, InStreamId, MediaType, ProtocolVersion,
ProtocolVersion, SessionData, YesOrNo}; SessionData, SingleLineString};
use super::parse_yes_or_no;
/// [4.3.4.1. EXT-X-MEDIA] /// [4.3.4.1. EXT-X-MEDIA]
/// ///
@ -20,9 +21,9 @@ pub struct ExtXMedia {
language: Option<QuotedString>, language: Option<QuotedString>,
assoc_language: Option<QuotedString>, assoc_language: Option<QuotedString>,
name: QuotedString, name: QuotedString,
default: YesOrNo, default: bool,
autoselect: YesOrNo, autoselect: bool,
forced: YesOrNo, forced: bool,
instream_id: Option<InStreamId>, instream_id: Option<InStreamId>,
characteristics: Option<QuotedString>, characteristics: Option<QuotedString>,
channels: Option<QuotedString>, channels: Option<QuotedString>,
@ -39,9 +40,9 @@ impl ExtXMedia {
language: None, language: None,
assoc_language: None, assoc_language: None,
name, name,
default: YesOrNo::No, default: false,
autoselect: YesOrNo::No, autoselect: false,
forced: YesOrNo::No, forced: false,
instream_id: None, instream_id: None,
characteristics: None, characteristics: None,
channels: None, channels: None,
@ -79,18 +80,18 @@ impl ExtXMedia {
} }
/// Returns whether this is the default rendition. /// Returns whether this is the default rendition.
pub fn default(&self) -> YesOrNo { pub fn default(&self) -> bool {
self.default self.default
} }
/// Returns whether the client may choose to /// Returns whether the client may choose to
/// play this rendition in the absence of explicit user preference. /// play this rendition in the absence of explicit user preference.
pub fn autoselect(&self) -> YesOrNo { pub fn autoselect(&self) -> bool {
self.autoselect self.autoselect
} }
/// Returns whether the rendition contains content that is considered essential to play. /// Returns whether the rendition contains content that is considered essential to play.
pub fn forced(&self) -> YesOrNo { pub fn forced(&self) -> bool {
self.forced self.forced
} }
@ -138,13 +139,13 @@ impl fmt::Display for ExtXMedia {
write!(f, ",ASSOC-LANGUAGE={}", x)?; write!(f, ",ASSOC-LANGUAGE={}", x)?;
} }
write!(f, ",NAME={}", self.name)?; write!(f, ",NAME={}", self.name)?;
if YesOrNo::Yes == self.default { if self.default {
write!(f, ",DEFAULT=YES")?; write!(f, ",DEFAULT=YES")?;
} }
if YesOrNo::Yes == self.autoselect { if self.autoselect {
write!(f, ",AUTOSELECT=YES")?; write!(f, ",AUTOSELECT=YES")?;
} }
if YesOrNo::Yes == self.forced { if self.forced {
write!(f, ",FORCED=YES")?; write!(f, ",FORCED=YES")?;
} }
if let Some(ref x) = self.instream_id { if let Some(ref x) = self.instream_id {
@ -170,7 +171,7 @@ impl FromStr for ExtXMedia {
let mut language = None; let mut language = None;
let mut assoc_language = None; let mut assoc_language = None;
let mut name = None; let mut name = None;
let mut default = None; let mut default = false;
let mut autoselect = None; let mut autoselect = None;
let mut forced = None; let mut forced = None;
let mut instream_id = None; let mut instream_id = None;
@ -186,9 +187,9 @@ impl FromStr for ExtXMedia {
"LANGUAGE" => language = Some(track!(value.parse())?), "LANGUAGE" => language = Some(track!(value.parse())?),
"ASSOC-LANGUAGE" => assoc_language = Some(track!(value.parse())?), "ASSOC-LANGUAGE" => assoc_language = Some(track!(value.parse())?),
"NAME" => name = Some(track!(value.parse())?), "NAME" => name = Some(track!(value.parse())?),
"DEFAULT" => default = Some(track!(value.parse())?), "DEFAULT" => default = track!(parse_yes_or_no(value))?,
"AUTOSELECT" => autoselect = Some(track!(value.parse())?), "AUTOSELECT" => autoselect = Some(track!(parse_yes_or_no(value))?),
"FORCED" => forced = Some(track!(value.parse())?), "FORCED" => forced = Some(track!(parse_yes_or_no(value))?),
"INSTREAM-ID" => { "INSTREAM-ID" => {
let s: QuotedString = track!(value.parse())?; let s: QuotedString = track!(value.parse())?;
instream_id = Some(track!(s.as_str().parse())?); instream_id = Some(track!(s.as_str().parse())?);
@ -210,8 +211,8 @@ impl FromStr for ExtXMedia {
} else { } else {
track_assert!(instream_id.is_none(), ErrorKind::InvalidInput); track_assert!(instream_id.is_none(), ErrorKind::InvalidInput);
} }
if default == Some(YesOrNo::Yes) && autoselect.is_some() { if default && autoselect.is_some() {
track_assert_eq!(autoselect, Some(YesOrNo::Yes), ErrorKind::InvalidInput); track_assert_eq!(autoselect, Some(true), ErrorKind::InvalidInput);
} }
if MediaType::Subtitles != media_type { if MediaType::Subtitles != media_type {
track_assert_eq!(forced, None, ErrorKind::InvalidInput); track_assert_eq!(forced, None, ErrorKind::InvalidInput);
@ -223,9 +224,9 @@ impl FromStr for ExtXMedia {
language, language,
assoc_language, assoc_language,
name, name,
default: default.unwrap_or(YesOrNo::No), default,
autoselect: autoselect.unwrap_or(YesOrNo::No), autoselect: autoselect.unwrap_or(false),
forced: forced.unwrap_or(YesOrNo::No), forced: forced.unwrap_or(false),
instream_id, instream_id,
characteristics, characteristics,
channels, channels,
@ -238,7 +239,7 @@ impl FromStr for ExtXMedia {
/// [4.3.4.2. EXT-X-STREAM-INF]: https://tools.ietf.org/html/rfc8216#section-4.3.4.2 /// [4.3.4.2. EXT-X-STREAM-INF]: https://tools.ietf.org/html/rfc8216#section-4.3.4.2
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct ExtXStreamInf { pub struct ExtXStreamInf {
uri: M3u8String, uri: SingleLineString,
bandwidth: DecimalInteger, bandwidth: DecimalInteger,
average_bandwidth: Option<DecimalInteger>, average_bandwidth: Option<DecimalInteger>,
codecs: Option<QuotedString>, codecs: Option<QuotedString>,
@ -254,7 +255,7 @@ 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. /// Makes a new `ExtXStreamInf` tag.
pub fn new(uri: M3u8String, bandwidth: DecimalInteger) -> Self { pub fn new(uri: SingleLineString, bandwidth: DecimalInteger) -> Self {
ExtXStreamInf { ExtXStreamInf {
uri, uri,
bandwidth, bandwidth,
@ -271,7 +272,7 @@ impl ExtXStreamInf {
} }
/// Returns the URI that identifies the associated media playlist. /// Returns the URI that identifies the associated media playlist.
pub fn uri(&self) -> &M3u8String { pub fn uri(&self) -> &SingleLineString {
&self.uri &self.uri
} }
@ -376,7 +377,7 @@ impl FromStr for ExtXStreamInf {
first_line.starts_with(Self::PREFIX), first_line.starts_with(Self::PREFIX),
ErrorKind::InvalidInput ErrorKind::InvalidInput
); );
let uri = track!(M3u8String::new(second_line))?; let uri = track!(SingleLineString::new(second_line))?;
let mut bandwidth = None; let mut bandwidth = None;
let mut average_bandwidth = None; let mut average_bandwidth = None;
let mut codecs = None; let mut codecs = None;
@ -722,7 +723,7 @@ mod test {
#[test] #[test]
fn ext_x_stream_inf() { fn ext_x_stream_inf() {
let tag = ExtXStreamInf::new(M3u8String::new("foo").unwrap(), DecimalInteger(1000)); let tag = ExtXStreamInf::new(SingleLineString::new("foo").unwrap(), DecimalInteger(1000));
let text = "#EXT-X-STREAM-INF:BANDWIDTH=1000\nfoo"; let text = "#EXT-X-STREAM-INF:BANDWIDTH=1000\nfoo";
assert_eq!(text.parse().ok(), Some(tag.clone())); assert_eq!(text.parse().ok(), Some(tag.clone()));
assert_eq!(tag.to_string(), text); assert_eq!(tag.to_string(), text);

View file

@ -3,7 +3,8 @@ use std::str::FromStr;
use {Error, ErrorKind, Result}; use {Error, ErrorKind, Result};
use attribute::{AttributePairs, SignedDecimalFloatingPoint}; use attribute::{AttributePairs, SignedDecimalFloatingPoint};
use types::{ProtocolVersion, YesOrNo}; use types::ProtocolVersion;
use super::parse_yes_or_no;
/// [4.3.5.1. EXT-X-INDEPENDENT-SEGMENTS] /// [4.3.5.1. EXT-X-INDEPENDENT-SEGMENTS]
/// ///
@ -37,7 +38,7 @@ impl FromStr for ExtXIndependentSegments {
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ExtXStart { pub struct ExtXStart {
time_offset: SignedDecimalFloatingPoint, time_offset: SignedDecimalFloatingPoint,
precise: YesOrNo, precise: bool,
} }
impl ExtXStart { impl ExtXStart {
pub(crate) const PREFIX: &'static str = "#EXT-X-START:"; pub(crate) const PREFIX: &'static str = "#EXT-X-START:";
@ -46,12 +47,12 @@ impl ExtXStart {
pub fn new(time_offset: SignedDecimalFloatingPoint) -> Self { pub fn new(time_offset: SignedDecimalFloatingPoint) -> Self {
ExtXStart { ExtXStart {
time_offset, time_offset,
precise: YesOrNo::No, precise: false,
} }
} }
/// Makes a new `ExtXStart` tag with the given `precise` flag. /// Makes a new `ExtXStart` tag with the given `precise` flag.
pub fn with_precise(time_offset: SignedDecimalFloatingPoint, precise: YesOrNo) -> Self { pub fn with_precise(time_offset: SignedDecimalFloatingPoint, precise: bool) -> Self {
ExtXStart { ExtXStart {
time_offset, time_offset,
precise, precise,
@ -65,7 +66,7 @@ impl ExtXStart {
/// Returns whether clients should not render media stream whose presentation times are /// Returns whether clients should not render media stream whose presentation times are
/// prior to the specified time offset. /// prior to the specified time offset.
pub fn precise(&self) -> YesOrNo { pub fn precise(&self) -> bool {
self.precise self.precise
} }
@ -78,8 +79,8 @@ impl fmt::Display for ExtXStart {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", Self::PREFIX)?; write!(f, "{}", Self::PREFIX)?;
write!(f, "TIME-OFFSET={}", self.time_offset)?; write!(f, "TIME-OFFSET={}", self.time_offset)?;
if self.precise == YesOrNo::Yes { if self.precise {
write!(f, ",PRECISE={}", self.precise)?; write!(f, ",PRECISE=YES")?;
} }
Ok(()) Ok(())
} }
@ -90,13 +91,13 @@ impl FromStr for ExtXStart {
track_assert!(s.starts_with(Self::PREFIX), ErrorKind::InvalidInput); track_assert!(s.starts_with(Self::PREFIX), ErrorKind::InvalidInput);
let mut time_offset = None; let mut time_offset = None;
let mut precise = None; let mut precise = false;
let attrs = AttributePairs::parse(s.split_at(Self::PREFIX.len()).1); let attrs = AttributePairs::parse(s.split_at(Self::PREFIX.len()).1);
for attr in attrs { for attr in attrs {
let (key, value) = track!(attr)?; let (key, value) = track!(attr)?;
match key { match key {
"TIME-OFFSET" => time_offset = Some(track!(value.parse())?), "TIME-OFFSET" => time_offset = Some(track!(value.parse())?),
"PRECISE" => precise = Some(track!(value.parse())?), "PRECISE" => precise = track!(parse_yes_or_no(value))?,
_ => { _ => {
// [6.3.1. General Client Responsibilities] // [6.3.1. General Client Responsibilities]
// > ignore any attribute/value pair with an unrecognized AttributeName. // > ignore any attribute/value pair with an unrecognized AttributeName.
@ -107,7 +108,7 @@ impl FromStr for ExtXStart {
let time_offset = track_assert_some!(time_offset, ErrorKind::InvalidInput); let time_offset = track_assert_some!(time_offset, ErrorKind::InvalidInput);
Ok(ExtXStart { Ok(ExtXStart {
time_offset, time_offset,
precise: precise.unwrap_or(YesOrNo::No), precise,
}) })
} }
} }

View file

@ -7,7 +7,7 @@ use trackable::error::ErrorKindExt;
use {Error, ErrorKind, Result}; use {Error, ErrorKind, Result};
use attribute::{AttributePairs, DecimalFloatingPoint, QuotedString}; use attribute::{AttributePairs, DecimalFloatingPoint, QuotedString};
use types::{ByteRange, DecryptionKey, M3u8String, ProtocolVersion, Yes}; use types::{ByteRange, DecryptionKey, ProtocolVersion, SingleLineString};
/// [4.3.2.1. EXTINF] /// [4.3.2.1. EXTINF]
/// ///
@ -15,7 +15,7 @@ use types::{ByteRange, DecryptionKey, M3u8String, ProtocolVersion, Yes};
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ExtInf { pub struct ExtInf {
duration: Duration, duration: Duration,
title: Option<M3u8String>, title: Option<SingleLineString>,
} }
impl ExtInf { impl ExtInf {
pub(crate) const PREFIX: &'static str = "#EXTINF:"; pub(crate) const PREFIX: &'static str = "#EXTINF:";
@ -29,7 +29,7 @@ impl ExtInf {
} }
/// Makes a new `ExtInf` tag with the given title. /// Makes a new `ExtInf` tag with the given title.
pub fn with_title(duration: Duration, title: M3u8String) -> Self { pub fn with_title(duration: Duration, title: SingleLineString) -> Self {
ExtInf { ExtInf {
duration, duration,
title: Some(title), title: Some(title),
@ -42,7 +42,7 @@ impl ExtInf {
} }
/// Returns the title of the associated media segment. /// Returns the title of the associated media segment.
pub fn title(&self) -> Option<&M3u8String> { pub fn title(&self) -> Option<&SingleLineString> {
self.title.as_ref() self.title.as_ref()
} }
@ -80,7 +80,7 @@ impl FromStr for ExtInf {
let duration = seconds.to_duration(); let duration = seconds.to_duration();
let title = if let Some(title) = tokens.next() { let title = if let Some(title) = tokens.next() {
Some(track!(M3u8String::new(title))?) Some(track!(SingleLineString::new(title))?)
} else { } else {
None None
}; };
@ -355,7 +355,7 @@ pub struct ExtXDateRange {
pub scte35_cmd: Option<QuotedString>, pub scte35_cmd: Option<QuotedString>,
pub scte35_out: Option<QuotedString>, pub scte35_out: Option<QuotedString>,
pub scte35_in: Option<QuotedString>, pub scte35_in: Option<QuotedString>,
pub end_on_next: Option<Yes>, pub end_on_next: bool,
pub client_attributes: BTreeMap<String, String>, pub client_attributes: BTreeMap<String, String>,
} }
impl ExtXDateRange { impl ExtXDateRange {
@ -375,11 +375,11 @@ impl fmt::Display for ExtXDateRange {
} }
write!( write!(
f, f,
",START_DATE={:?}", ",START-DATE={:?}",
self.start_date.format("%Y-%m-%d").to_string() self.start_date.format("%Y-%m-%d").to_string()
)?; )?;
if let Some(ref x) = self.end_date { if let Some(ref x) = self.end_date {
write!(f, ",END_DATE={:?}", x.format("%Y-%m-%d").to_string())?; write!(f, ",END-DATE={:?}", x.format("%Y-%m-%d").to_string())?;
} }
if let Some(x) = self.duration { if let Some(x) = self.duration {
write!(f, ",DURATION={}", DecimalFloatingPoint::from_duration(x))?; write!(f, ",DURATION={}", DecimalFloatingPoint::from_duration(x))?;
@ -387,21 +387,21 @@ impl fmt::Display for ExtXDateRange {
if let Some(x) = self.planned_duration { if let Some(x) = self.planned_duration {
write!( write!(
f, f,
",PLANNED_DURATION={}", ",PLANNED-DURATION={}",
DecimalFloatingPoint::from_duration(x) DecimalFloatingPoint::from_duration(x)
)?; )?;
} }
if let Some(ref x) = self.scte35_cmd { if let Some(ref x) = self.scte35_cmd {
write!(f, ",SCTE35_CMD={}", x)?; write!(f, ",SCTE35-CMD={}", x)?;
} }
if let Some(ref x) = self.scte35_out { if let Some(ref x) = self.scte35_out {
write!(f, ",SCTE35_OUT={}", x)?; write!(f, ",SCTE35-OUT={}", x)?;
} }
if let Some(ref x) = self.scte35_in { if let Some(ref x) = self.scte35_in {
write!(f, ",SCTE35_IN={}", x)?; write!(f, ",SCTE35-IN={}", x)?;
} }
if let Some(ref x) = self.end_on_next { if self.end_on_next {
write!(f, ",END_ON_NEXT={}", x)?; write!(f, ",END-ON-NEXT=YES",)?;
} }
for (k, v) in &self.client_attributes { for (k, v) in &self.client_attributes {
write!(f, ",{}={}", k, v)?; write!(f, ",{}={}", k, v)?;
@ -423,7 +423,7 @@ impl FromStr for ExtXDateRange {
let mut scte35_cmd = None; let mut scte35_cmd = None;
let mut scte35_out = None; let mut scte35_out = None;
let mut scte35_in = None; let mut scte35_in = None;
let mut end_on_next = None; let mut end_on_next = false;
let mut client_attributes = BTreeMap::new(); let mut client_attributes = BTreeMap::new();
let attrs = AttributePairs::parse(s.split_at(Self::PREFIX.len()).1); let attrs = AttributePairs::parse(s.split_at(Self::PREFIX.len()).1);
for attr in attrs { for attr in attrs {
@ -456,7 +456,10 @@ impl FromStr for ExtXDateRange {
"SCTE35-CMD" => scte35_cmd = Some(track!(value.parse())?), "SCTE35-CMD" => scte35_cmd = Some(track!(value.parse())?),
"SCTE35-OUT" => scte35_out = Some(track!(value.parse())?), "SCTE35-OUT" => scte35_out = Some(track!(value.parse())?),
"SCTE35-IN" => scte35_in = Some(track!(value.parse())?), "SCTE35-IN" => scte35_in = Some(track!(value.parse())?),
"END-ON-NEXT" => end_on_next = Some(track!(value.parse())?), "END-ON-NEXT" => {
track_assert_eq!(value, "YES", ErrorKind::InvalidInput);
end_on_next = true;
}
_ => { _ => {
if key.starts_with("X-") { if key.starts_with("X-") {
client_attributes.insert(key.split_at(2).1.to_owned(), value.to_owned()); client_attributes.insert(key.split_at(2).1.to_owned(), value.to_owned());
@ -470,7 +473,7 @@ impl FromStr for ExtXDateRange {
let id = track_assert_some!(id, ErrorKind::InvalidInput); let id = track_assert_some!(id, ErrorKind::InvalidInput);
let start_date = track_assert_some!(start_date, ErrorKind::InvalidInput); let start_date = track_assert_some!(start_date, ErrorKind::InvalidInput);
if end_on_next.is_some() { if end_on_next {
track_assert!(class.is_some(), ErrorKind::InvalidInput); track_assert!(class.is_some(), ErrorKind::InvalidInput);
} }
Ok(ExtXDateRange { Ok(ExtXDateRange {
@ -504,7 +507,10 @@ mod test {
assert_eq!(tag.to_string(), "#EXTINF:5"); assert_eq!(tag.to_string(), "#EXTINF:5");
assert_eq!(tag.requires_version(), ProtocolVersion::V1); assert_eq!(tag.requires_version(), ProtocolVersion::V1);
let tag = ExtInf::with_title(Duration::from_secs(5), M3u8String::new("foo").unwrap()); let tag = ExtInf::with_title(
Duration::from_secs(5),
SingleLineString::new("foo").unwrap(),
);
assert_eq!("#EXTINF:5,foo".parse().ok(), Some(tag.clone())); assert_eq!("#EXTINF:5,foo".parse().ok(), Some(tag.clone()));
assert_eq!(tag.to_string(), "#EXTINF:5,foo"); assert_eq!(tag.to_string(), "#EXTINF:5,foo");
assert_eq!(tag.requires_version(), ProtocolVersion::V1); assert_eq!(tag.requires_version(), ProtocolVersion::V1);

View file

@ -229,3 +229,11 @@ impl FromStr for Tag {
} }
} }
} }
fn parse_yes_or_no(s: &str) -> Result<bool> {
match s {
"YES" => Ok(true),
"NO" => Ok(false),
_ => track_panic!(ErrorKind::InvalidInput, "Unexpected value: {:?}", s),
}
}

View file

@ -7,51 +7,47 @@ use trackable::error::ErrorKindExt;
use {Error, ErrorKind, Result}; use {Error, ErrorKind, Result};
use attribute::{AttributePairs, HexadecimalSequence, QuotedString}; use attribute::{AttributePairs, HexadecimalSequence, QuotedString};
// TODO: rename /// String that represents a single line in a playlist file.
///
/// See: [4.1. Definition of a Playlist]
///
/// [4.1. Definition of a Playlist]: https://tools.ietf.org/html/rfc8216#section-4.1
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct M3u8String(String); pub struct SingleLineString(String);
impl M3u8String { impl SingleLineString {
/// Makes a new `SingleLineString` instance.
///
/// # Errors
///
/// If the given string contains any control characters,
/// this function will return an error which has the kind `ErrorKind::InvalidInput`.
pub fn new<T: Into<String>>(s: T) -> Result<Self> { pub fn new<T: Into<String>>(s: T) -> Result<Self> {
// TODO: validate let s = s.into();
Ok(M3u8String(s.into())) track_assert!(!s.chars().any(|c| c.is_control()), ErrorKind::InvalidInput);
} Ok(SingleLineString(s))
pub unsafe fn new_unchecked<T: Into<String>>(s: T) -> Self {
M3u8String(s.into())
} }
} }
impl Deref for M3u8String { impl Deref for SingleLineString {
type Target = str; type Target = str;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.0 &self.0
} }
} }
impl AsRef<str> for M3u8String { impl AsRef<str> for SingleLineString {
fn as_ref(&self) -> &str { fn as_ref(&self) -> &str {
&self.0 &self.0
} }
} }
impl fmt::Display for M3u8String { impl fmt::Display for SingleLineString {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f) self.0.fmt(f)
} }
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] /// [7. Protocol Version Compatibility]
pub struct Yes; ///
impl fmt::Display for Yes { /// [7. Protocol Version Compatibility]: https://tools.ietf.org/html/rfc8216#section-7
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { #[allow(missing_docs)]
"YES".fmt(f)
}
}
impl FromStr for Yes {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
track_assert_eq!(s, "YES", ErrorKind::InvalidInput);
Ok(Yes)
}
}
// https://tools.ietf.org/html/rfc8216#section-7
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ProtocolVersion { pub enum ProtocolVersion {
V1, V1,
@ -92,6 +88,12 @@ impl FromStr for ProtocolVersion {
} }
} }
/// Byte range.
///
/// See: [4.3.2.2. EXT-X-BYTERANGE]
///
/// [4.3.2.2. EXT-X-BYTERANGE]: https://tools.ietf.org/html/rfc8216#section-4.3.2.2
#[allow(missing_docs)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ByteRange { pub struct ByteRange {
pub length: usize, pub length: usize,
@ -125,16 +127,22 @@ impl FromStr for ByteRange {
} }
} }
/// Decryption key.
///
/// See: [4.3.2.4. EXT-X-KEY]
///
/// [4.3.2.4. EXT-X-KEY]: https://tools.ietf.org/html/rfc8216#section-4.3.2.4
#[allow(missing_docs)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct DecryptionKey { pub struct DecryptionKey {
pub method: EncryptionMethod, pub method: EncryptionMethod,
pub uri: QuotedString, pub uri: QuotedString,
pub iv: Option<HexadecimalSequence>, pub iv: Option<HexadecimalSequence>, // TODO: iv
pub key_format: Option<QuotedString>, pub key_format: Option<QuotedString>,
pub key_format_versions: Option<QuotedString>, pub key_format_versions: Option<QuotedString>,
} }
impl DecryptionKey { impl DecryptionKey {
pub fn requires_version(&self) -> ProtocolVersion { pub(crate) 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() {
@ -172,29 +180,14 @@ impl FromStr for DecryptionKey {
for attr in attrs { for attr in attrs {
let (key, value) = track!(attr)?; let (key, value) = track!(attr)?;
match key { match key {
"METHOD" => { "METHOD" => method = Some(track!(value.parse())?),
track_assert_eq!(method, None, ErrorKind::InvalidInput); "URI" => uri = Some(track!(value.parse())?),
method = Some(track!(value.parse())?); "IV" => iv = Some(track!(value.parse())?),
} "KEYFORMAT" => key_format = Some(track!(value.parse())?),
"URI" => { "KEYFORMATVERSIONS" => key_format_versions = Some(track!(value.parse())?),
track_assert_eq!(uri, None, ErrorKind::InvalidInput);
uri = Some(track!(value.parse())?);
}
"IV" => {
// TODO: validate length(128-bit)
track_assert_eq!(iv, None, ErrorKind::InvalidInput);
iv = Some(track!(value.parse())?);
}
"KEYFORMAT" => {
track_assert_eq!(key_format, None, ErrorKind::InvalidInput);
key_format = Some(track!(value.parse())?);
}
"KEYFORMATVERSIONS" => {
track_assert_eq!(key_format_versions, None, ErrorKind::InvalidInput);
key_format_versions = Some(track!(value.parse())?);
}
_ => { _ => {
// [6.3.1] ignore any attribute/value pair with an unrecognized AttributeName. // [6.3.1. General Client Responsibilities]
// > ignore any attribute/value pair with an unrecognized AttributeName.
} }
} }
} }
@ -210,6 +203,12 @@ impl FromStr for DecryptionKey {
} }
} }
/// Encryption method.
///
/// See: [4.3.2.4. EXT-X-KEY]
///
/// [4.3.2.4. EXT-X-KEY]: https://tools.ietf.org/html/rfc8216#section-4.3.2.4
#[allow(missing_docs)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum EncryptionMethod { pub enum EncryptionMethod {
Aes128, Aes128,
@ -238,6 +237,12 @@ impl FromStr for EncryptionMethod {
} }
} }
/// Playlist type.
///
/// See: [4.3.3.5. EXT-X-PLAYLIST-TYPE]
///
/// [4.3.3.5. EXT-X-PLAYLIST-TYPE]: https://tools.ietf.org/html/rfc8216#section-4.3.3.5
#[allow(missing_docs)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PlaylistType { pub enum PlaylistType {
Event, Event,
@ -262,6 +267,12 @@ impl FromStr for PlaylistType {
} }
} }
/// Media type.
///
/// See: [4.3.4.1. EXT-X-MEDIA]
///
/// [4.3.4.1. EXT-X-MEDIA]: https://tools.ietf.org/html/rfc8216#section-4.3.4.1
#[allow(missing_docs)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum MediaType { pub enum MediaType {
Audio, Audio,
@ -292,35 +303,12 @@ impl FromStr for MediaType {
} }
} }
// TODO: remove /// Identifier of a rendition within the segments in a media playlist.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] ///
pub enum YesOrNo { /// See: [4.3.4.1. EXT-X-MEDIA]
Yes, ///
No, /// [4.3.4.1. EXT-X-MEDIA]: https://tools.ietf.org/html/rfc8216#section-4.3.4.1
} #[allow(missing_docs)]
impl fmt::Display for YesOrNo {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
YesOrNo::Yes => "YES".fmt(f),
YesOrNo::No => "NO".fmt(f),
}
}
}
impl FromStr for YesOrNo {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
match s {
"YES" => Ok(YesOrNo::Yes),
"NO" => Ok(YesOrNo::No),
_ => track_panic!(
ErrorKind::InvalidInput,
"Unexpected enumerated-string: {:?}",
s
),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum InStreamId { pub enum InStreamId {
Cc1, Cc1,
@ -472,6 +460,12 @@ impl FromStr for InStreamId {
} }
} }
/// HDCP level.
///
/// See: [4.3.4.2. EXT-X-STREAM-INF]
///
/// [4.3.4.2. EXT-X-STREAM-INF]: https://tools.ietf.org/html/rfc8216#section-4.3.4.2
#[allow(missing_docs)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum HdcpLevel { pub enum HdcpLevel {
Type0, Type0,
@ -496,6 +490,12 @@ impl FromStr for HdcpLevel {
} }
} }
/// The identifier of a closed captions group or its absence.
///
/// See: [4.3.4.2. EXT-X-STREAM-INF]
///
/// [4.3.4.2. EXT-X-STREAM-INF]: https://tools.ietf.org/html/rfc8216#section-4.3.4.2
#[allow(missing_docs)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ClosedCaptions { pub enum ClosedCaptions {
GroupId(QuotedString), GroupId(QuotedString),
@ -520,8 +520,25 @@ impl FromStr for ClosedCaptions {
} }
} }
/// Session data.
///
/// See: [4.3.4.4. EXT-X-SESSION-DATA]
///
/// [4.3.4.4. EXT-X-SESSION-DATA]: https://tools.ietf.org/html/rfc8216#section-4.3.4.4
#[allow(missing_docs)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum SessionData { pub enum SessionData {
Value(QuotedString), Value(QuotedString),
Uri(QuotedString), Uri(QuotedString),
} }
#[cfg(test)]
mod test {
use super::*;
#[test]
fn single_line_string() {
assert!(SingleLineString::new("foo").is_ok());
assert!(SingleLineString::new("b\rar").is_err());
}
}