1
0
Fork 0
mirror of https://github.com/sile/hls_m3u8.git synced 2024-11-15 20:01:01 +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"
derive_builder = "0.7.2"
url = "2.1.0"
chrono = "0.4.9"
[dev-dependencies]
clap = "2"

View file

@ -9,6 +9,9 @@ pub type Result<T> = std::result::Result<T, Error>;
/// The ErrorKind.
#[derive(Debug, Fail, Clone, PartialEq, Eq)]
pub enum ErrorKind {
#[fail(display = "ChronoParseError: {}", _0)]
/// An error from the [Chrono](chrono) crate.
ChronoParseError(String),
#[fail(display = "UrlParseError: {}", _0)]
/// An error from the [Url](url) crate.
UrlParseError(String),
@ -184,6 +187,10 @@ impl Error {
pub(crate) fn url<T: ToString>(value: T) -> Self {
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 {
@ -209,3 +216,9 @@ impl From<::url::ParseError> for Error {
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::time::Duration;
use chrono::{DateTime, FixedOffset};
use getset::{Getters, MutGetters, Setters};
use crate::attribute::AttributePairs;
use crate::types::{DecimalFloatingPoint, ProtocolVersion};
use crate::utils::{quote, tag, unquote};
@ -14,19 +17,52 @@ use crate::Error;
///
/// TODO: Implement properly
#[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 id: String,
pub class: Option<String>,
pub start_date: String,
pub end_date: Option<String>,
pub duration: Option<Duration>,
pub planned_duration: Option<Duration>,
pub scte35_cmd: Option<String>,
pub scte35_out: Option<String>,
pub scte35_in: Option<String>,
pub end_on_next: bool,
pub client_attributes: BTreeMap<String, String>,
/// A string that uniquely identifies a Date Range in the Playlist.
/// This attribute is REQUIRED.
id: String,
/// A client-defined string that specifies some set of attributes and their associated value
/// semantics. All Date Ranges with the same CLASS attribute value MUST adhere to these
/// semantics. This attribute is OPTIONAL.
class: Option<String>,
/// The date at which the Date Range begins. This attribute is REQUIRED.
start_date: DateTime<FixedOffset>,
/// The date at which the Date Range ends. It MUST be equal to or later than the value of the
/// 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 {
@ -102,7 +138,7 @@ impl FromStr for ExtXDateRange {
"ID" => id = Some(unquote(value)),
"CLASS" => class = 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" => {
let seconds: DecimalFloatingPoint = (value.parse())?;
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 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 class.is_none() {
return Err(Error::invalid_input());

View file

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