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:
parent
fa96a76ca9
commit
db6961d19f
4 changed files with 106 additions and 28 deletions
|
@ -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"
|
||||
|
|
13
src/error.rs
13
src/error.rs
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue