1
0
Fork 0
mirror of https://github.com/sile/hls_m3u8.git synced 2024-05-12 13:33:02 +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 build
- cargo test
- cargo test --features chrono
# it's enough to run this once:
- |

View file

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

View file

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

View file

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

View file

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