1
0
Fork 0
mirror of https://github.com/sile/hls_m3u8.git synced 2024-06-16 12:00:33 +00:00

parse dates with chrono #9

This commit is contained in:
Luro02 2019-09-15 12:51:51 +02:00
parent fa96a76ca9
commit db6961d19f
4 changed files with 106 additions and 28 deletions

View file

@ -20,6 +20,7 @@ getset = "0.0.8"
failure = "0.1.5" failure = "0.1.5"
derive_builder = "0.7.2" derive_builder = "0.7.2"
url = "2.1.0" url = "2.1.0"
chrono = "0.4.9"
[dev-dependencies] [dev-dependencies]
clap = "2" clap = "2"

View file

@ -9,6 +9,9 @@ pub type Result<T> = std::result::Result<T, Error>;
/// The ErrorKind. /// The ErrorKind.
#[derive(Debug, Fail, Clone, PartialEq, Eq)] #[derive(Debug, Fail, Clone, PartialEq, Eq)]
pub enum ErrorKind { pub enum ErrorKind {
#[fail(display = "ChronoParseError: {}", _0)]
/// An error from the [Chrono](chrono) crate.
ChronoParseError(String),
#[fail(display = "UrlParseError: {}", _0)] #[fail(display = "UrlParseError: {}", _0)]
/// An error from the [Url](url) crate. /// An error from the [Url](url) crate.
UrlParseError(String), UrlParseError(String),
@ -184,6 +187,10 @@ impl Error {
pub(crate) fn url<T: ToString>(value: T) -> Self { pub(crate) fn url<T: ToString>(value: T) -> Self {
Self::from(ErrorKind::UrlParseError(value.to_string())) Self::from(ErrorKind::UrlParseError(value.to_string()))
} }
pub(crate) fn chrono<T: ToString>(value: T) -> Self {
Self::from(ErrorKind::ChronoParseError(value.to_string()))
}
} }
impl From<::std::num::ParseIntError> for Error { impl From<::std::num::ParseIntError> for Error {
@ -209,3 +216,9 @@ impl From<::url::ParseError> for Error {
Error::url(value) Error::url(value)
} }
} }
impl From<::chrono::ParseError> for Error {
fn from(value: ::chrono::ParseError) -> Self {
Error::chrono(value)
}
}

View file

@ -3,6 +3,9 @@ use std::fmt;
use std::str::FromStr; use std::str::FromStr;
use std::time::Duration; use std::time::Duration;
use chrono::{DateTime, FixedOffset};
use getset::{Getters, MutGetters, Setters};
use crate::attribute::AttributePairs; use crate::attribute::AttributePairs;
use crate::types::{DecimalFloatingPoint, ProtocolVersion}; use crate::types::{DecimalFloatingPoint, ProtocolVersion};
use crate::utils::{quote, tag, unquote}; use crate::utils::{quote, tag, unquote};
@ -14,19 +17,52 @@ use crate::Error;
/// ///
/// TODO: Implement properly /// TODO: Implement properly
#[allow(missing_docs)] #[allow(missing_docs)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash, Getters, MutGetters, Setters)]
#[get = "pub"]
#[set = "pub"]
#[get_mut = "pub"]
pub struct ExtXDateRange { pub struct ExtXDateRange {
pub id: String, /// A string that uniquely identifies a Date Range in the Playlist.
pub class: Option<String>, /// This attribute is REQUIRED.
pub start_date: String, id: String,
pub end_date: Option<String>, /// A client-defined string that specifies some set of attributes and their associated value
pub duration: Option<Duration>, /// semantics. All Date Ranges with the same CLASS attribute value MUST adhere to these
pub planned_duration: Option<Duration>, /// semantics. This attribute is OPTIONAL.
pub scte35_cmd: Option<String>, class: Option<String>,
pub scte35_out: Option<String>, /// The date at which the Date Range begins. This attribute is REQUIRED.
pub scte35_in: Option<String>, start_date: DateTime<FixedOffset>,
pub end_on_next: bool, /// The date at which the Date Range ends. It MUST be equal to or later than the value of the
pub client_attributes: BTreeMap<String, String>, /// START-DATE attribute. This attribute is OPTIONAL.
end_date: Option<DateTime<FixedOffset>>,
/// The duration of the Date Range. It MUST NOT be negative. A single
/// instant in time (e.g., crossing a finish line) SHOULD be
/// represented with a duration of 0. This attribute is OPTIONAL.
duration: Option<Duration>,
/// The expected duration of the Date Range. It MUST NOT be negative. This
/// attribute SHOULD be used to indicate the expected duration of a
/// Date Range whose actual duration is not yet known.
/// It is OPTIONAL.
planned_duration: Option<Duration>,
///
scte35_cmd: Option<String>,
///
scte35_out: Option<String>,
///
scte35_in: Option<String>,
/// This attribute indicates that the end of the range containing it is equal to the
/// START-DATE of its Following Range. The Following Range is the
/// Date Range of the same CLASS, that has the earliest START-DATE
/// after the START-DATE of the range in question. This attribute is
/// OPTIONAL.
end_on_next: bool,
/// The "X-" prefix defines a namespace reserved for client-defined
/// attributes. The client-attribute MUST be a legal AttributeName.
/// Clients SHOULD use a reverse-DNS syntax when defining their own
/// attribute names to avoid collisions. The attribute value MUST be
/// a quoted-string, a hexadecimal-sequence, or a decimal-floating-
/// point. An example of a client-defined attribute is X-COM-EXAMPLE-
/// AD-ID="XYZ123". These attributes are OPTIONAL.
client_attributes: BTreeMap<String, String>,
} }
impl ExtXDateRange { impl ExtXDateRange {
@ -102,7 +138,7 @@ impl FromStr for ExtXDateRange {
"ID" => id = Some(unquote(value)), "ID" => id = Some(unquote(value)),
"CLASS" => class = Some(unquote(value)), "CLASS" => class = Some(unquote(value)),
"START-DATE" => start_date = Some(unquote(value)), "START-DATE" => start_date = Some(unquote(value)),
"END-DATE" => end_date = Some(unquote(value)), "END-DATE" => end_date = Some(unquote(value).parse()?),
"DURATION" => { "DURATION" => {
let seconds: DecimalFloatingPoint = (value.parse())?; let seconds: DecimalFloatingPoint = (value.parse())?;
duration = Some(seconds.to_duration()); duration = Some(seconds.to_duration());
@ -132,7 +168,10 @@ impl FromStr for ExtXDateRange {
} }
let id = id.ok_or(Error::missing_value("EXT-X-ID"))?; 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"))?; let start_date = start_date
.ok_or(Error::missing_value("EXT-X-START-DATE"))?
.parse()?;
if end_on_next { if end_on_next {
if class.is_none() { if class.is_none() {
return Err(Error::invalid_input()); return Err(Error::invalid_input());

View file

@ -1,6 +1,8 @@
use std::fmt; use std::fmt;
use std::str::FromStr; use std::str::FromStr;
use chrono::{DateTime, FixedOffset};
use crate::types::ProtocolVersion; use crate::types::ProtocolVersion;
use crate::utils::tag; use crate::utils::tag;
use crate::Error; use crate::Error;
@ -8,19 +10,19 @@ use crate::Error;
/// [4.3.2.6. EXT-X-PROGRAM-DATE-TIME] /// [4.3.2.6. EXT-X-PROGRAM-DATE-TIME]
/// ///
/// [4.3.2.6. EXT-X-PROGRAM-DATE-TIME]: https://tools.ietf.org/html/rfc8216#section-4.3.2.6 /// [4.3.2.6. EXT-X-PROGRAM-DATE-TIME]: https://tools.ietf.org/html/rfc8216#section-4.3.2.6
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct ExtXProgramDateTime(String); pub struct ExtXProgramDateTime(DateTime<FixedOffset>);
impl ExtXProgramDateTime { impl ExtXProgramDateTime {
pub(crate) const PREFIX: &'static str = "#EXT-X-PROGRAM-DATE-TIME:"; pub(crate) const PREFIX: &'static str = "#EXT-X-PROGRAM-DATE-TIME:";
/// Makes a new `ExtXProgramDateTime` tag. /// Makes a new `ExtXProgramDateTime` tag.
pub fn new<T: ToString>(date_time: T) -> Self { pub fn new<T: Into<DateTime<FixedOffset>>>(date_time: T) -> Self {
Self(date_time.to_string()) Self(date_time.into())
} }
/// Returns the date-time of the first sample of the associated media segment. /// Returns the date-time of the first sample of the associated media segment.
pub const fn date_time(&self) -> &String { pub const fn date_time(&self) -> &DateTime<FixedOffset> {
&self.0 &self.0
} }
@ -32,7 +34,8 @@ impl ExtXProgramDateTime {
impl fmt::Display for ExtXProgramDateTime { impl fmt::Display for ExtXProgramDateTime {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}{}", Self::PREFIX, self.0) let date_time = self.0.to_rfc3339();
write!(f, "{}{}", Self::PREFIX, date_time)
} }
} }
@ -43,8 +46,8 @@ impl FromStr for ExtXProgramDateTime {
let input = tag(input, Self::PREFIX)?; let input = tag(input, Self::PREFIX)?;
// TODO: parse with chrono // TODO: parse with chrono
let date_time = DateTime::parse_from_rfc3339(input)?;
Ok(Self::new(input)) Ok(Self::new(date_time))
} }
} }
@ -53,12 +56,34 @@ mod test {
use super::*; use super::*;
#[test] #[test]
fn ext_x_program_date_time() { fn test_display() {
let text = "#EXT-X-PROGRAM-DATE-TIME:2010-02-19T14:54:23.031+08:00"; let date_time = "2010-02-19T14:54:23.031+08:00"
assert!(text.parse::<ExtXProgramDateTime>().is_ok()); .parse::<DateTime<FixedOffset>>()
.unwrap();
let tag = text.parse::<ExtXProgramDateTime>().unwrap(); let program_date_time = ExtXProgramDateTime::new(date_time);
assert_eq!(tag.to_string(), text);
assert_eq!(tag.requires_version(), ProtocolVersion::V1); assert_eq!(
program_date_time.to_string(),
"#EXT-X-PROGRAM-DATE-TIME:2010-02-19T14:54:23.031+08:00".to_string()
);
}
#[test]
fn test_parser() {
"#EXT-X-PROGRAM-DATE-TIME:2010-02-19T14:54:23.031+08:00"
.parse::<ExtXProgramDateTime>()
.unwrap();
}
#[test]
fn test_requires_version() {
let date_time = "2010-02-19T14:54:23.031+08:00"
.parse::<DateTime<FixedOffset>>()
.unwrap();
let program_date_time = ExtXProgramDateTime::new(date_time);
assert_eq!(program_date_time.requires_version(), ProtocolVersion::V1);
} }
} }