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

make chrono optional #49

This commit is contained in:
Luro02 2020-03-20 12:05:16 +01:00
parent 1b01675250
commit b2fb58559c
No known key found for this signature in database
GPG key ID: B66FD4F74501A9CF
5 changed files with 235 additions and 124 deletions

View file

@ -25,6 +25,7 @@ script:
- cargo clean - cargo clean
- cargo build - cargo build
- cargo test - cargo test
- cargo test --features chrono
# it's enough to run this once: # it's enough to run this once:
- | - |

View file

@ -16,14 +16,16 @@ codecov = { repository = "sile/hls_m3u8" }
travis-ci = { repository = "sile/hls_m3u8" } travis-ci = { repository = "sile/hls_m3u8" }
[dependencies] [dependencies]
chrono = "0.4" chrono = { version = "0.4", optional = true }
backtrace = { version = "0.3", features = ["std"], optional = true }
derive_builder = "0.9" derive_builder = "0.9"
derive_more = "0.99"
hex = "0.4" hex = "0.4"
thiserror = "1.0"
derive_more = "0.99"
shorthand = "0.1" shorthand = "0.1"
strum = { version = "0.17", features = ["derive"] } strum = { version = "0.17", features = ["derive"] }
thiserror = "1.0"
backtrace = { version = "0.3", features = ["std"], optional = true }
[dev-dependencies] [dev-dependencies]
pretty_assertions = "0.6" pretty_assertions = "0.6"

View file

@ -59,6 +59,7 @@ enum ErrorKind {
UnexpectedTag { tag: String }, UnexpectedTag { tag: String },
#[error("{source}")] #[error("{source}")]
#[cfg(feature = "chrono")]
Chrono { source: chrono::ParseError }, Chrono { source: chrono::ParseError },
#[error("builder error: {message}")] #[error("builder error: {message}")]
@ -169,6 +170,7 @@ impl Error {
} }
// third party crates: // third party crates:
#[cfg(feature = "chrono")]
pub(crate) fn chrono(source: chrono::format::ParseError) -> Self { pub(crate) fn chrono(source: chrono::format::ParseError) -> Self {
Self::new(ErrorKind::Chrono { source }) Self::new(ErrorKind::Chrono { source })
} }

View file

@ -3,6 +3,7 @@ use std::fmt;
use std::str::FromStr; use std::str::FromStr;
use std::time::Duration; use std::time::Duration;
#[cfg(feature = "chrono")]
use chrono::{DateTime, FixedOffset, SecondsFormat}; use chrono::{DateTime, FixedOffset, SecondsFormat};
use derive_builder::Builder; use derive_builder::Builder;
use shorthand::ShortHand; use shorthand::ShortHand;
@ -12,13 +13,8 @@ use crate::types::{ProtocolVersion, Value};
use crate::utils::{quote, tag, unquote}; use crate::utils::{quote, tag, unquote};
use crate::{Error, RequiredVersion}; use crate::{Error, RequiredVersion};
/// # [4.3.2.7. EXT-X-DATERANGE] /// The [`ExtXDateRange`] tag associates a date range (i.e., a range of time
/// /// defined by a starting and ending date) with a set of attribute/value pairs.
/// The [`ExtXDateRange`] tag associates a date range (i.e., a range of
/// time defined by a starting and ending date) with a set of attribute/
/// value pairs.
///
/// [4.3.2.7. EXT-X-DATERANGE]: https://tools.ietf.org/html/rfc8216#section-4.3.2.7
#[derive(ShortHand, Builder, Debug, Clone, PartialEq, PartialOrd)] #[derive(ShortHand, Builder, Debug, Clone, PartialEq, PartialOrd)]
#[builder(setter(into))] #[builder(setter(into))]
#[shorthand(enable(must_use, into))] #[shorthand(enable(must_use, into))]
@ -43,6 +39,7 @@ pub struct ExtXDateRange {
/// # Note /// # Note
/// ///
/// This attribute is required. /// This attribute is required.
#[cfg(feature = "chrono")]
start_date: DateTime<FixedOffset>, start_date: DateTime<FixedOffset>,
/// The date at which the [`ExtXDateRange`] ends. It must be equal to or /// The date at which the [`ExtXDateRange`] ends. It must be equal to or
/// later than the value of the [`start-date`] attribute. /// later than the value of the [`start-date`] attribute.
@ -52,8 +49,12 @@ pub struct ExtXDateRange {
/// This attribute is optional. /// This attribute is optional.
/// ///
/// [`start-date`]: #method.start_date /// [`start-date`]: #method.start_date
#[cfg(feature = "chrono")]
#[builder(setter(strip_option), default)] #[builder(setter(strip_option), default)]
end_date: Option<DateTime<FixedOffset>>, end_date: Option<DateTime<FixedOffset>>,
#[cfg(not(feature = "chrono"))]
#[builder(setter(strip_option), default)]
end_date: Option<String>,
/// The duration of the [`ExtXDateRange`]. A single instant in time (e.g., /// The duration of the [`ExtXDateRange`]. A single instant in time (e.g.,
/// crossing a finish line) should be represented with a duration of 0. /// crossing a finish line) should be represented with a duration of 0.
/// ///
@ -145,27 +146,48 @@ impl ExtXDateRange {
/// Makes a new [`ExtXDateRange`] tag. /// Makes a new [`ExtXDateRange`] tag.
/// ///
/// # Example /// # Example
/// #[cfg_attr(
/// ``` feature = "chrono",
/// # use hls_m3u8::tags::ExtXDateRange; doc = r#"
/// use chrono::offset::TimeZone; ```
/// use chrono::{DateTime, FixedOffset}; # use hls_m3u8::tags::ExtXDateRange;
/// use chrono::offset::TimeZone;
/// const HOURS_IN_SECS: i32 = 3600; // 1 hour = 3600 seconds use chrono::{DateTime, FixedOffset};
///
/// let date_range = ExtXDateRange::new( const HOURS_IN_SECS: i32 = 3600; // 1 hour = 3600 seconds
/// "id",
/// FixedOffset::east(8 * HOURS_IN_SECS) let date_range = ExtXDateRange::new(
/// .ymd(2010, 2, 19) "id",
/// .and_hms_milli(14, 54, 23, 31), FixedOffset::east(8 * HOURS_IN_SECS)
/// ); .ymd(2010, 2, 19)
/// ``` .and_hms_milli(14, 54, 23, 31),
);
```
"#
)]
#[cfg_attr(
not(feature = "chrono"),
doc = r#"
```
# use hls_m3u8::tags::ExtXDateRange;
let date_range = ExtXDateRange::new("id", "2010-02-19T14:54:23.031+08:00");
```
"#
)]
#[must_use] #[must_use]
pub fn new<T: Into<String>>(id: T, start_date: DateTime<FixedOffset>) -> Self { pub fn new<T: Into<String>, #[cfg(not(feature = "chrono"))] I: Into<String>>(
id: T,
#[cfg(feature = "chrono")] start_date: DateTime<FixedOffset>,
#[cfg(not(feature = "chrono"))] start_date: I,
) -> Self {
Self { Self {
id: id.into(), id: id.into(),
class: None, class: None,
#[cfg(feature = "chrono")]
start_date, start_date,
#[cfg(not(feature = "chrono"))]
start_date: start_date.into(),
end_date: None, end_date: None,
duration: None, duration: None,
planned_duration: None, planned_duration: None,
@ -210,8 +232,26 @@ impl FromStr for ExtXDateRange {
match key { match key {
"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" => {
"END-DATE" => end_date = Some(unquote(value).parse().map_err(Error::chrono)?), #[cfg(feature = "chrono")]
{
start_date = Some(unquote(value).parse().map_err(Error::chrono)?)
}
#[cfg(not(feature = "chrono"))]
{
start_date = Some(unquote(value))
}
}
"END-DATE" => {
#[cfg(feature = "chrono")]
{
end_date = Some(unquote(value).parse().map_err(Error::chrono)?)
}
#[cfg(not(feature = "chrono"))]
{
end_date = Some(unquote(value))
}
}
"DURATION" => { "DURATION" => {
duration = Some(Duration::from_secs_f64( duration = Some(Duration::from_secs_f64(
value.parse().map_err(|e| Error::parse_float(value, e))?, value.parse().map_err(|e| Error::parse_float(value, e))?,
@ -244,10 +284,7 @@ impl FromStr for ExtXDateRange {
} }
let id = id.ok_or_else(|| Error::missing_value("ID"))?; let id = id.ok_or_else(|| Error::missing_value("ID"))?;
let start_date = start_date let start_date = start_date.ok_or_else(|| Error::missing_value("START-DATE"))?;
.ok_or_else(|| Error::missing_value("START-DATE"))?
.parse()
.map_err(Error::chrono)?;
if end_on_next && class.is_none() { if end_on_next && class.is_none() {
return Err(Error::invalid_input()); return Err(Error::invalid_input());
@ -277,13 +314,23 @@ impl fmt::Display for ExtXDateRange {
write!(f, ",CLASS={}", quote(value))?; write!(f, ",CLASS={}", quote(value))?;
} }
#[cfg(feature = "chrono")]
{
write!( write!(
f, f,
",START-DATE={}", ",START-DATE={}",
quote(&self.start_date.to_rfc3339_opts(SecondsFormat::AutoSi, true)) quote(&self.start_date.to_rfc3339_opts(SecondsFormat::AutoSi, true))
)?; )?;
}
#[cfg(not(feature = "chrono"))]
{
write!(f, ",START-DATE={}", quote(&self.start_date))?;
}
if let Some(value) = &self.end_date { if let Some(value) = &self.end_date {
#[cfg(feature = "chrono")]
{
write!( write!(
f, f,
",END-DATE={}", ",END-DATE={}",
@ -291,6 +338,12 @@ impl fmt::Display for ExtXDateRange {
)?; )?;
} }
#[cfg(not(feature = "chrono"))]
{
write!(f, ",END-DATE={}", quote(&value))?;
}
}
if let Some(value) = &self.duration { if let Some(value) = &self.duration {
write!(f, ",DURATION={}", value.as_secs_f64())?; write!(f, ",DURATION={}", value.as_secs_f64())?;
} }
@ -327,9 +380,11 @@ impl fmt::Display for ExtXDateRange {
mod test { mod test {
use super::*; use super::*;
use crate::types::Float; use crate::types::Float;
#[cfg(feature = "chrono")]
use chrono::offset::TimeZone; use chrono::offset::TimeZone;
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
#[cfg(feature = "chrono")]
const HOURS_IN_SECS: i32 = 3600; // 1 hour = 3600 seconds const HOURS_IN_SECS: i32 = 3600; // 1 hour = 3600 seconds
macro_rules! generate_tests { macro_rules! generate_tests {
@ -369,7 +424,16 @@ mod test {
{ {
ExtXDateRange::builder() ExtXDateRange::builder()
.id("splice-6FFFFFF0") .id("splice-6FFFFFF0")
.start_date(FixedOffset::east(0).ymd(2014, 3, 5).and_hms(11, 15, 0)) .start_date({
#[cfg(feature = "chrono")]
{
FixedOffset::east(0).ymd(2014, 3, 5).and_hms(11, 15, 0)
}
#[cfg(not(feature = "chrono"))]
{
"2014-03-05T11:15:00Z"
}
})
.planned_duration(Duration::from_secs_f64(59.993)) .planned_duration(Duration::from_secs_f64(59.993))
.scte35_out(concat!( .scte35_out(concat!(
"0xFC002F0000000000FF00001", "0xFC002F0000000000FF00001",
@ -393,8 +457,26 @@ mod test {
ExtXDateRange::builder() ExtXDateRange::builder()
.id("test_id") .id("test_id")
.class("test_class") .class("test_class")
.start_date(FixedOffset::east(0).ymd(2014, 3, 5).and_hms(11, 15, 0)) .start_date({
.end_date(FixedOffset::east(0).ymd(2014, 3, 5).and_hms(11, 16, 0)) #[cfg(feature = "chrono")]
{
FixedOffset::east(0).ymd(2014, 3, 5).and_hms(11, 15, 0)
}
#[cfg(not(feature = "chrono"))]
{
"2014-03-05T11:15:00Z"
}
})
.end_date({
#[cfg(feature = "chrono")]
{
FixedOffset::east(0).ymd(2014, 3, 5).and_hms(11, 16, 0)
}
#[cfg(not(feature = "chrono"))]
{
"2014-03-05T11:16:00Z"
}
})
.duration(Duration::from_secs_f64(60.1)) .duration(Duration::from_secs_f64(60.1))
.planned_duration(Duration::from_secs_f64(59.993)) .planned_duration(Duration::from_secs_f64(59.993))
.insert_client_attribute("X-CUSTOM", Float::new(45.3)) .insert_client_attribute("X-CUSTOM", Float::new(45.3))
@ -424,12 +506,18 @@ mod test {
#[test] #[test]
fn test_required_version() { fn test_required_version() {
assert_eq!( assert_eq!(
ExtXDateRange::new( ExtXDateRange::new("id", {
"id", #[cfg(feature = "chrono")]
{
FixedOffset::east(8 * HOURS_IN_SECS) FixedOffset::east(8 * HOURS_IN_SECS)
.ymd(2010, 2, 19) .ymd(2010, 2, 19)
.and_hms_milli(14, 54, 23, 31) .and_hms_milli(14, 54, 23, 31)
) }
#[cfg(not(feature = "chrono"))]
{
"2010-02-19T14:54:23.031+08:00"
}
})
.required_version(), .required_version(),
ProtocolVersion::V1 ProtocolVersion::V1
); );

View file

@ -1,7 +1,9 @@
use std::fmt; use std::fmt;
use std::str::FromStr; use std::str::FromStr;
#[cfg(feature = "chrono")]
use chrono::{DateTime, FixedOffset, SecondsFormat}; use chrono::{DateTime, FixedOffset, SecondsFormat};
#[cfg(feature = "chrono")]
use derive_more::{Deref, DerefMut}; use derive_more::{Deref, DerefMut};
use crate::types::ProtocolVersion; use crate::types::ProtocolVersion;
@ -16,8 +18,18 @@ use crate::{Error, RequiredVersion};
/// [`MediaSegment`]: crate::MediaSegment /// [`MediaSegment`]: crate::MediaSegment
/// [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 /// https://tools.ietf.org/html/rfc8216#section-4.3.2.6
#[derive(Deref, DerefMut, Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct ExtXProgramDateTime(DateTime<FixedOffset>); #[cfg_attr(feature = "chrono", derive(Deref, DerefMut, Copy))]
pub struct ExtXProgramDateTime {
/// The date-time of the first sample of the associated media segment.
#[cfg(feature = "chrono")]
#[cfg_attr(feature = "chrono", deref_mut, deref)]
pub date_time: DateTime<FixedOffset>,
/// The date-time of the first sample of the associated media segment.
#[cfg(not(feature = "chrono"))]
pub date_time: String,
__non_exhaustive: (),
}
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:";
@ -39,67 +51,28 @@ impl ExtXProgramDateTime {
/// ); /// );
/// ``` /// ```
#[must_use] #[must_use]
pub const fn new(date_time: DateTime<FixedOffset>) -> Self { Self(date_time) } #[cfg(feature = "chrono")]
pub const fn new(date_time: DateTime<FixedOffset>) -> Self {
Self {
date_time,
__non_exhaustive: (),
}
}
/// Returns the date-time of the first sample of the associated media /// Makes a new [`ExtXProgramDateTime`] tag.
/// segment.
/// ///
/// # Example /// # Example
/// ///
/// ``` /// ```
/// # use hls_m3u8::tags::ExtXProgramDateTime; /// # use hls_m3u8::tags::ExtXProgramDateTime;
/// use chrono::{FixedOffset, TimeZone}; /// let program_date_time = ExtXProgramDateTime::new("2010-02-19T14:54:23.031+08:00");
///
/// const HOURS_IN_SECS: i32 = 3600; // 1 hour = 3600 seconds
///
/// let program_date_time = ExtXProgramDateTime::new(
/// FixedOffset::east(8 * HOURS_IN_SECS)
/// .ymd(2010, 2, 19)
/// .and_hms_milli(14, 54, 23, 31),
/// );
///
/// assert_eq!(
/// program_date_time.date_time(),
/// FixedOffset::east(8 * HOURS_IN_SECS)
/// .ymd(2010, 2, 19)
/// .and_hms_milli(14, 54, 23, 31)
/// );
/// ``` /// ```
#[must_use] #[cfg(not(feature = "chrono"))]
pub const fn date_time(&self) -> DateTime<FixedOffset> { self.0 } pub fn new<T: Into<String>>(date_time: T) -> Self {
Self {
/// Sets the date-time of the first sample of the associated media segment. date_time: date_time.into(),
/// __non_exhaustive: (),
/// # Example }
///
/// ```
/// # use hls_m3u8::tags::ExtXProgramDateTime;
/// use chrono::{FixedOffset, TimeZone};
///
/// const HOURS_IN_SECS: i32 = 3600; // 1 hour = 3600 seconds
///
/// let mut program_date_time = ExtXProgramDateTime::new(
/// FixedOffset::east(8 * HOURS_IN_SECS)
/// .ymd(2010, 2, 19)
/// .and_hms_milli(14, 54, 23, 31),
/// );
///
/// program_date_time.set_date_time(
/// FixedOffset::east(8 * HOURS_IN_SECS)
/// .ymd(2010, 10, 10)
/// .and_hms_milli(10, 10, 10, 10),
/// );
///
/// assert_eq!(
/// program_date_time.date_time(),
/// FixedOffset::east(8 * HOURS_IN_SECS)
/// .ymd(2010, 10, 10)
/// .and_hms_milli(10, 10, 10, 10)
/// );
/// ```
pub fn set_date_time(&mut self, value: DateTime<FixedOffset>) -> &mut Self {
self.0 = value;
self
} }
} }
@ -110,7 +83,16 @@ impl RequiredVersion for 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 {
let date_time = self.0.to_rfc3339_opts(SecondsFormat::Millis, true); let date_time = {
#[cfg(feature = "chrono")]
{
self.date_time.to_rfc3339_opts(SecondsFormat::Millis, true)
}
#[cfg(not(feature = "chrono"))]
{
&self.date_time
}
};
write!(f, "{}{}", Self::PREFIX, date_time) write!(f, "{}{}", Self::PREFIX, date_time)
} }
} }
@ -121,7 +103,17 @@ impl FromStr for ExtXProgramDateTime {
fn from_str(input: &str) -> Result<Self, Self::Err> { fn from_str(input: &str) -> Result<Self, Self::Err> {
let input = tag(input, Self::PREFIX)?; let input = tag(input, Self::PREFIX)?;
let date_time = DateTime::parse_from_rfc3339(input).map_err(Error::chrono)?; let date_time = {
#[cfg(feature = "chrono")]
{
DateTime::parse_from_rfc3339(input).map_err(Error::chrono)?
}
#[cfg(not(feature = "chrono"))]
{
input
}
};
Ok(Self::new(date_time)) Ok(Self::new(date_time))
} }
} }
@ -129,20 +121,30 @@ impl FromStr for ExtXProgramDateTime {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
#[cfg(feature = "chrono")]
use chrono::{Datelike, TimeZone}; use chrono::{Datelike, TimeZone};
#[cfg(feature = "chrono")]
use core::ops::DerefMut; use core::ops::DerefMut;
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
#[cfg(feature = "chrono")]
const HOURS_IN_SECS: i32 = 3600; // 1 hour = 3600 seconds const HOURS_IN_SECS: i32 = 3600; // 1 hour = 3600 seconds
#[test] #[test]
fn test_display() { fn test_display() {
assert_eq!( assert_eq!(
ExtXProgramDateTime::new( ExtXProgramDateTime::new({
#[cfg(feature = "chrono")]
{
FixedOffset::east(8 * HOURS_IN_SECS) FixedOffset::east(8 * HOURS_IN_SECS)
.ymd(2010, 2, 19) .ymd(2010, 2, 19)
.and_hms_milli(14, 54, 23, 31) .and_hms_milli(14, 54, 23, 31)
) }
#[cfg(not(feature = "chrono"))]
{
"2010-02-19T14:54:23.031+08:00"
}
})
.to_string(), .to_string(),
"#EXT-X-PROGRAM-DATE-TIME:2010-02-19T14:54:23.031+08:00".to_string() "#EXT-X-PROGRAM-DATE-TIME:2010-02-19T14:54:23.031+08:00".to_string()
); );
@ -151,11 +153,18 @@ mod test {
#[test] #[test]
fn test_parser() { fn test_parser() {
assert_eq!( assert_eq!(
ExtXProgramDateTime::new( ExtXProgramDateTime::new({
#[cfg(feature = "chrono")]
{
FixedOffset::east(8 * HOURS_IN_SECS) FixedOffset::east(8 * HOURS_IN_SECS)
.ymd(2010, 2, 19) .ymd(2010, 2, 19)
.and_hms_milli(14, 54, 23, 31) .and_hms_milli(14, 54, 23, 31)
), }
#[cfg(not(feature = "chrono"))]
{
"2010-02-19T14:54:23.031+08:00"
}
}),
"#EXT-X-PROGRAM-DATE-TIME:2010-02-19T14:54:23.031+08:00" "#EXT-X-PROGRAM-DATE-TIME:2010-02-19T14:54:23.031+08:00"
.parse::<ExtXProgramDateTime>() .parse::<ExtXProgramDateTime>()
.unwrap() .unwrap()
@ -165,17 +174,25 @@ mod test {
#[test] #[test]
fn test_required_version() { fn test_required_version() {
assert_eq!( assert_eq!(
ExtXProgramDateTime::new( ExtXProgramDateTime::new({
#[cfg(feature = "chrono")]
{
FixedOffset::east(8 * HOURS_IN_SECS) FixedOffset::east(8 * HOURS_IN_SECS)
.ymd(2010, 2, 19) .ymd(2010, 2, 19)
.and_hms_milli(14, 54, 23, 31), .and_hms_milli(14, 54, 23, 31)
) }
#[cfg(not(feature = "chrono"))]
{
"2010-02-19T14:54:23.031+08:00"
}
})
.required_version(), .required_version(),
ProtocolVersion::V1 ProtocolVersion::V1
); );
} }
#[test] #[test]
#[cfg(feature = "chrono")]
fn test_deref() { fn test_deref() {
assert_eq!( assert_eq!(
ExtXProgramDateTime::new( ExtXProgramDateTime::new(
@ -189,6 +206,7 @@ mod test {
} }
#[test] #[test]
#[cfg(feature = "chrono")]
fn test_deref_mut() { fn test_deref_mut() {
assert_eq!( assert_eq!(
ExtXProgramDateTime::new( ExtXProgramDateTime::new(