2019-09-06 10:55:00 +00:00
|
|
|
use std::collections::BTreeMap;
|
|
|
|
use std::fmt;
|
|
|
|
use std::str::FromStr;
|
|
|
|
use std::time::Duration;
|
|
|
|
|
2019-09-13 14:06:52 +00:00
|
|
|
use crate::attribute::AttributePairs;
|
|
|
|
use crate::types::{DecimalFloatingPoint, ProtocolVersion};
|
|
|
|
use crate::utils::{quote, tag, unquote};
|
|
|
|
use crate::Error;
|
|
|
|
|
2019-09-06 10:55:00 +00:00
|
|
|
/// [4.3.2.7. EXT-X-DATERANGE]
|
|
|
|
///
|
|
|
|
/// [4.3.2.7. EXT-X-DATERANGE]: https://tools.ietf.org/html/rfc8216#section-4.3.2.7
|
|
|
|
///
|
|
|
|
/// TODO: Implement properly
|
|
|
|
#[allow(missing_docs)]
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
|
|
pub struct ExtXDateRange {
|
2019-09-08 09:30:52 +00:00
|
|
|
pub id: String,
|
|
|
|
pub class: Option<String>,
|
|
|
|
pub start_date: String,
|
|
|
|
pub end_date: Option<String>,
|
2019-09-06 10:55:00 +00:00
|
|
|
pub duration: Option<Duration>,
|
|
|
|
pub planned_duration: Option<Duration>,
|
2019-09-08 09:30:52 +00:00
|
|
|
pub scte35_cmd: Option<String>,
|
|
|
|
pub scte35_out: Option<String>,
|
|
|
|
pub scte35_in: Option<String>,
|
2019-09-06 10:55:00 +00:00
|
|
|
pub end_on_next: bool,
|
|
|
|
pub client_attributes: BTreeMap<String, String>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ExtXDateRange {
|
|
|
|
pub(crate) const PREFIX: &'static str = "#EXT-X-DATERANGE:";
|
|
|
|
|
|
|
|
/// Returns the protocol compatibility version that this tag requires.
|
2019-09-08 10:23:33 +00:00
|
|
|
pub const fn requires_version(&self) -> ProtocolVersion {
|
2019-09-06 10:55:00 +00:00
|
|
|
ProtocolVersion::V1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl fmt::Display for ExtXDateRange {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
write!(f, "{}", Self::PREFIX)?;
|
2019-09-08 09:30:52 +00:00
|
|
|
write!(f, "ID={}", quote(&self.id))?;
|
2019-09-06 10:55:00 +00:00
|
|
|
if let Some(ref x) = self.class {
|
2019-09-08 09:30:52 +00:00
|
|
|
write!(f, ",CLASS={}", quote(x))?;
|
2019-09-06 10:55:00 +00:00
|
|
|
}
|
2019-09-08 09:30:52 +00:00
|
|
|
write!(f, ",START-DATE={}", quote(&self.start_date))?;
|
2019-09-06 10:55:00 +00:00
|
|
|
if let Some(ref x) = self.end_date {
|
2019-09-08 09:30:52 +00:00
|
|
|
write!(f, ",END-DATE={}", quote(x))?;
|
2019-09-06 10:55:00 +00:00
|
|
|
}
|
|
|
|
if let Some(x) = self.duration {
|
|
|
|
write!(f, ",DURATION={}", DecimalFloatingPoint::from_duration(x))?;
|
|
|
|
}
|
|
|
|
if let Some(x) = self.planned_duration {
|
|
|
|
write!(
|
|
|
|
f,
|
|
|
|
",PLANNED-DURATION={}",
|
|
|
|
DecimalFloatingPoint::from_duration(x)
|
|
|
|
)?;
|
|
|
|
}
|
|
|
|
if let Some(ref x) = self.scte35_cmd {
|
2019-09-08 09:30:52 +00:00
|
|
|
write!(f, ",SCTE35-CMD={}", quote(x))?;
|
2019-09-06 10:55:00 +00:00
|
|
|
}
|
|
|
|
if let Some(ref x) = self.scte35_out {
|
2019-09-08 09:30:52 +00:00
|
|
|
write!(f, ",SCTE35-OUT={}", quote(x))?;
|
2019-09-06 10:55:00 +00:00
|
|
|
}
|
|
|
|
if let Some(ref x) = self.scte35_in {
|
2019-09-08 09:30:52 +00:00
|
|
|
write!(f, ",SCTE35-IN={}", quote(x))?;
|
2019-09-06 10:55:00 +00:00
|
|
|
}
|
|
|
|
if self.end_on_next {
|
|
|
|
write!(f, ",END-ON-NEXT=YES",)?;
|
|
|
|
}
|
|
|
|
for (k, v) in &self.client_attributes {
|
|
|
|
write!(f, ",{}={}", k, v)?;
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl FromStr for ExtXDateRange {
|
|
|
|
type Err = Error;
|
2019-09-13 14:06:52 +00:00
|
|
|
|
|
|
|
fn from_str(input: &str) -> Result<Self, Self::Err> {
|
|
|
|
let input = tag(input, Self::PREFIX)?;
|
2019-09-06 10:55:00 +00:00
|
|
|
|
|
|
|
let mut id = None;
|
|
|
|
let mut class = None;
|
|
|
|
let mut start_date = None;
|
|
|
|
let mut end_date = None;
|
|
|
|
let mut duration = None;
|
|
|
|
let mut planned_duration = None;
|
|
|
|
let mut scte35_cmd = None;
|
|
|
|
let mut scte35_out = None;
|
|
|
|
let mut scte35_in = None;
|
|
|
|
let mut end_on_next = false;
|
2019-09-13 14:06:52 +00:00
|
|
|
|
2019-09-06 10:55:00 +00:00
|
|
|
let mut client_attributes = BTreeMap::new();
|
2019-09-13 14:06:52 +00:00
|
|
|
|
2019-09-14 09:31:16 +00:00
|
|
|
for (key, value) in input.parse::<AttributePairs>()? {
|
|
|
|
match key.as_str() {
|
2019-09-08 09:30:52 +00:00
|
|
|
"ID" => id = Some(unquote(value)),
|
|
|
|
"CLASS" => class = Some(unquote(value)),
|
|
|
|
"START-DATE" => start_date = Some(unquote(value)),
|
|
|
|
"END-DATE" => end_date = Some(unquote(value)),
|
2019-09-06 10:55:00 +00:00
|
|
|
"DURATION" => {
|
2019-09-13 14:06:52 +00:00
|
|
|
let seconds: DecimalFloatingPoint = (value.parse())?;
|
2019-09-06 10:55:00 +00:00
|
|
|
duration = Some(seconds.to_duration());
|
|
|
|
}
|
|
|
|
"PLANNED-DURATION" => {
|
2019-09-13 14:06:52 +00:00
|
|
|
let seconds: DecimalFloatingPoint = (value.parse())?;
|
2019-09-06 10:55:00 +00:00
|
|
|
planned_duration = Some(seconds.to_duration());
|
|
|
|
}
|
2019-09-08 09:30:52 +00:00
|
|
|
"SCTE35-CMD" => scte35_cmd = Some(unquote(value)),
|
|
|
|
"SCTE35-OUT" => scte35_out = Some(unquote(value)),
|
|
|
|
"SCTE35-IN" => scte35_in = Some(unquote(value)),
|
2019-09-06 10:55:00 +00:00
|
|
|
"END-ON-NEXT" => {
|
2019-09-13 14:06:52 +00:00
|
|
|
if value != "YES" {
|
|
|
|
return Err(Error::invalid_input());
|
|
|
|
}
|
2019-09-06 10:55:00 +00:00
|
|
|
end_on_next = true;
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
if key.starts_with("X-") {
|
|
|
|
client_attributes.insert(key.split_at(2).1.to_owned(), value.to_owned());
|
|
|
|
} else {
|
|
|
|
// [6.3.1. General Client Responsibilities]
|
|
|
|
// > ignore any attribute/value pair with an unrecognized AttributeName.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-13 14:06:52 +00:00
|
|
|
let id = id.ok_or(Error::missing_value("EXT-X-ID"))?;
|
|
|
|
let start_date = start_date.ok_or(Error::missing_value("EXT-X-START-DATE"))?;
|
2019-09-06 10:55:00 +00:00
|
|
|
if end_on_next {
|
2019-09-13 14:06:52 +00:00
|
|
|
if class.is_none() {
|
|
|
|
return Err(Error::invalid_input());
|
|
|
|
}
|
2019-09-06 10:55:00 +00:00
|
|
|
}
|
|
|
|
Ok(ExtXDateRange {
|
|
|
|
id,
|
|
|
|
class,
|
|
|
|
start_date,
|
|
|
|
end_date,
|
|
|
|
duration,
|
|
|
|
planned_duration,
|
|
|
|
scte35_cmd,
|
|
|
|
scte35_out,
|
|
|
|
scte35_in,
|
|
|
|
end_on_next,
|
|
|
|
client_attributes,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test] // TODO; write some tests
|
2019-09-06 11:21:05 +00:00
|
|
|
fn it_works() {}
|
2019-09-06 10:55:00 +00:00
|
|
|
}
|