1
0
Fork 0
mirror of https://github.com/sile/hls_m3u8.git synced 2024-06-01 19:43:39 +00:00
hls_m3u8/src/tags/media_segment/inf.rs

281 lines
7.5 KiB
Rust
Raw Normal View History

use std::borrow::Cow;
use std::convert::TryFrom;
2019-09-06 10:55:00 +00:00
use std::fmt;
use std::time::Duration;
2019-09-13 14:06:52 +00:00
2020-03-25 10:41:24 +00:00
use derive_more::AsRef;
2019-10-04 09:02:21 +00:00
use crate::types::ProtocolVersion;
2019-09-13 14:06:52 +00:00
use crate::utils::tag;
2019-10-04 09:02:21 +00:00
use crate::{Error, RequiredVersion};
2019-09-06 10:55:00 +00:00
2020-03-25 10:41:24 +00:00
/// Specifies the duration of a [`Media Segment`].
2019-09-15 08:40:45 +00:00
///
2019-10-03 14:23:27 +00:00
/// [`Media Segment`]: crate::media_segment::MediaSegment
2020-03-25 10:41:24 +00:00
#[derive(AsRef, Default, Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct ExtInf<'a> {
2020-03-25 10:41:24 +00:00
#[as_ref]
2019-09-06 10:55:00 +00:00
duration: Duration,
title: Option<Cow<'a, str>>,
2019-09-06 10:55:00 +00:00
}
impl<'a> ExtInf<'a> {
2019-09-06 10:55:00 +00:00
pub(crate) const PREFIX: &'static str = "#EXTINF:";
2019-10-03 14:23:27 +00:00
/// Makes a new [`ExtInf`] tag.
2019-09-22 16:00:38 +00:00
///
/// # Example
2020-02-02 12:38:11 +00:00
///
2019-09-22 16:00:38 +00:00
/// ```
/// # use hls_m3u8::tags::ExtInf;
/// use std::time::Duration;
///
/// let ext_inf = ExtInf::new(Duration::from_secs(5));
/// ```
2020-02-24 15:30:43 +00:00
#[must_use]
2019-09-08 10:23:33 +00:00
pub const fn new(duration: Duration) -> Self {
2019-09-22 18:33:40 +00:00
Self {
2019-09-06 10:55:00 +00:00
duration,
title: None,
}
}
2019-10-03 14:23:27 +00:00
/// Makes a new [`ExtInf`] tag with the given title.
2019-09-22 16:00:38 +00:00
///
/// # Example
2020-02-02 12:38:11 +00:00
///
2019-09-22 16:00:38 +00:00
/// ```
/// # use hls_m3u8::tags::ExtInf;
/// use std::time::Duration;
///
/// let ext_inf = ExtInf::with_title(Duration::from_secs(5), "title");
/// ```
2020-02-24 15:30:43 +00:00
#[must_use]
pub fn with_title<T: Into<Cow<'a, str>>>(duration: Duration, title: T) -> Self {
2019-09-22 18:33:40 +00:00
Self {
2019-09-06 10:55:00 +00:00
duration,
2020-02-10 12:21:48 +00:00
title: Some(title.into()),
2019-09-06 10:55:00 +00:00
}
}
/// Returns the duration of the associated media segment.
2019-09-22 16:00:38 +00:00
///
/// # Example
2020-02-02 12:38:11 +00:00
///
2019-09-22 16:00:38 +00:00
/// ```
/// # use hls_m3u8::tags::ExtInf;
/// use std::time::Duration;
///
/// let ext_inf = ExtInf::new(Duration::from_secs(5));
///
2019-10-03 15:01:15 +00:00
/// assert_eq!(ext_inf.duration(), Duration::from_secs(5));
2019-09-22 16:00:38 +00:00
/// ```
2020-02-24 15:30:43 +00:00
#[must_use]
2019-10-03 15:01:15 +00:00
pub const fn duration(&self) -> Duration { self.duration }
2019-09-06 10:55:00 +00:00
2019-09-22 16:00:38 +00:00
/// Sets the duration of the associated media segment.
///
/// # Example
2020-02-02 12:38:11 +00:00
///
2019-09-22 16:00:38 +00:00
/// ```
/// # use hls_m3u8::tags::ExtInf;
/// use std::time::Duration;
///
/// let mut ext_inf = ExtInf::new(Duration::from_secs(5));
///
/// ext_inf.set_duration(Duration::from_secs(10));
///
2019-10-03 15:01:15 +00:00
/// assert_eq!(ext_inf.duration(), Duration::from_secs(10));
2019-09-22 16:00:38 +00:00
/// ```
pub fn set_duration(&mut self, value: Duration) -> &mut Self {
self.duration = value;
self
}
2019-09-06 10:55:00 +00:00
/// Returns the title of the associated media segment.
2019-09-22 16:00:38 +00:00
///
/// # Example
2020-02-02 12:38:11 +00:00
///
2019-09-22 16:00:38 +00:00
/// ```
/// # use hls_m3u8::tags::ExtInf;
/// use std::time::Duration;
///
/// let ext_inf = ExtInf::with_title(Duration::from_secs(5), "title");
///
/// assert_eq!(ext_inf.title(), &Some("title".into()));
2019-09-22 16:00:38 +00:00
/// ```
2020-02-24 15:30:43 +00:00
#[must_use]
pub const fn title(&self) -> &Option<Cow<'a, str>> { &self.title }
2019-09-22 16:00:38 +00:00
/// Sets the title of the associated media segment.
///
/// # Example
2020-02-02 12:38:11 +00:00
///
2019-09-22 16:00:38 +00:00
/// ```
/// # use hls_m3u8::tags::ExtInf;
/// use std::time::Duration;
///
/// let mut ext_inf = ExtInf::with_title(Duration::from_secs(5), "title");
///
/// ext_inf.set_title(Some("better title"));
///
/// assert_eq!(ext_inf.title(), &Some("better title".into()));
2019-09-22 16:00:38 +00:00
/// ```
pub fn set_title<T: Into<Cow<'a, str>>>(&mut self, value: Option<T>) -> &mut Self {
2020-08-11 09:13:14 +00:00
self.title = value.map(Into::into);
2019-09-22 16:00:38 +00:00
self
2019-09-06 10:55:00 +00:00
}
/// Makes the struct independent of its lifetime, by taking ownership of all
/// internal [`Cow`]s.
///
/// # Note
///
/// This is a relatively expensive operation.
#[must_use]
pub fn into_owned(self) -> ExtInf<'static> {
ExtInf {
duration: self.duration,
title: self.title.map(|v| Cow::Owned(v.into_owned())),
}
}
2019-09-22 08:57:28 +00:00
}
2019-09-06 10:55:00 +00:00
2020-02-02 12:38:11 +00:00
/// This tag requires [`ProtocolVersion::V1`], if the duration does not have
/// nanoseconds, otherwise it requires [`ProtocolVersion::V3`].
impl<'a> RequiredVersion for ExtInf<'a> {
2019-09-22 08:57:28 +00:00
fn required_version(&self) -> ProtocolVersion {
2019-09-06 10:55:00 +00:00
if self.duration.subsec_nanos() == 0 {
ProtocolVersion::V1
} else {
ProtocolVersion::V3
}
}
}
impl<'a> fmt::Display for ExtInf<'a> {
2020-04-09 06:43:13 +00:00
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2019-09-06 10:55:00 +00:00
write!(f, "{}", Self::PREFIX)?;
2019-10-05 14:08:03 +00:00
write!(f, "{},", self.duration.as_secs_f64())?;
2019-09-06 10:55:00 +00:00
2019-09-14 19:42:06 +00:00
if let Some(value) = &self.title {
2019-09-15 08:40:45 +00:00
write!(f, "{}", value)?;
2019-09-06 10:55:00 +00:00
}
Ok(())
}
}
impl<'a> TryFrom<&'a str> for ExtInf<'a> {
type Error = Error;
2019-09-06 10:55:00 +00:00
fn try_from(input: &'a str) -> Result<Self, Self::Error> {
2020-02-06 11:27:48 +00:00
let mut input = tag(input, Self::PREFIX)?.splitn(2, ',');
2020-02-14 12:01:42 +00:00
let duration = input.next().unwrap();
let duration = Duration::from_secs_f64(
duration
.parse()
.map_err(|e| Error::parse_float(duration, e))?,
);
2020-02-06 11:27:48 +00:00
let title = input
.next()
2020-02-10 12:21:48 +00:00
.map(str::trim)
2020-02-06 11:27:48 +00:00
.filter(|value| !value.is_empty())
.map(|v| Cow::Borrowed(v));
2019-09-15 08:40:45 +00:00
2019-09-22 18:33:40 +00:00
Ok(Self { duration, title })
2019-09-06 10:55:00 +00:00
}
}
impl<'a> From<Duration> for ExtInf<'a> {
2019-10-03 15:01:15 +00:00
fn from(value: Duration) -> Self { Self::new(value) }
2019-09-15 08:40:45 +00:00
}
2019-09-06 10:55:00 +00:00
#[cfg(test)]
mod test {
use super::*;
use pretty_assertions::assert_eq;
2019-09-06 10:55:00 +00:00
#[test]
2019-09-15 08:40:45 +00:00
fn test_display() {
assert_eq!(
"#EXTINF:5,".to_string(),
ExtInf::new(Duration::from_secs(5)).to_string()
);
assert_eq!(
"#EXTINF:5.5,".to_string(),
ExtInf::new(Duration::from_millis(5500)).to_string()
);
assert_eq!(
"#EXTINF:5.5,title".to_string(),
2019-09-15 09:05:22 +00:00
ExtInf::with_title(Duration::from_millis(5500), "title").to_string()
2019-09-15 08:40:45 +00:00
);
assert_eq!(
"#EXTINF:5,title".to_string(),
2019-09-15 09:05:22 +00:00
ExtInf::with_title(Duration::from_secs(5), "title").to_string()
2019-09-15 08:40:45 +00:00
);
}
#[test]
fn test_parser() {
// #EXTINF:<duration>,[<title>]
assert_eq!(
ExtInf::try_from("#EXTINF:5").unwrap(),
2019-09-15 08:40:45 +00:00
ExtInf::new(Duration::from_secs(5))
);
assert_eq!(
ExtInf::try_from("#EXTINF:5,").unwrap(),
2019-09-15 08:40:45 +00:00
ExtInf::new(Duration::from_secs(5))
);
assert_eq!(
ExtInf::try_from("#EXTINF:5.5").unwrap(),
2019-09-15 08:40:45 +00:00
ExtInf::new(Duration::from_millis(5500))
);
assert_eq!(
ExtInf::try_from("#EXTINF:5.5,").unwrap(),
2019-09-15 08:40:45 +00:00
ExtInf::new(Duration::from_millis(5500))
);
assert_eq!(
ExtInf::try_from("#EXTINF:5.5,title").unwrap(),
2019-09-15 09:05:22 +00:00
ExtInf::with_title(Duration::from_millis(5500), "title")
2019-09-15 08:40:45 +00:00
);
assert_eq!(
ExtInf::try_from("#EXTINF:5,title").unwrap(),
2019-09-15 09:05:22 +00:00
ExtInf::with_title(Duration::from_secs(5), "title")
2019-09-15 08:40:45 +00:00
);
2019-10-06 14:39:18 +00:00
assert!(ExtInf::try_from("#EXTINF:").is_err());
assert!(ExtInf::try_from("#EXTINF:garbage").is_err());
2019-09-15 08:40:45 +00:00
}
#[test]
fn test_title() {
2019-09-22 16:00:38 +00:00
assert_eq!(ExtInf::new(Duration::from_secs(5)).title(), &None);
2019-09-15 08:40:45 +00:00
assert_eq!(
2019-09-15 09:05:22 +00:00
ExtInf::with_title(Duration::from_secs(5), "title").title(),
&Some("title".into())
2019-09-15 08:40:45 +00:00
);
}
#[test]
2019-09-22 08:57:28 +00:00
fn test_required_version() {
2019-09-15 08:40:45 +00:00
assert_eq!(
2019-09-22 08:57:28 +00:00
ExtInf::new(Duration::from_secs(4)).required_version(),
2019-09-15 08:40:45 +00:00
ProtocolVersion::V1
);
assert_eq!(
2019-09-22 08:57:28 +00:00
ExtInf::new(Duration::from_millis(4400)).required_version(),
2019-09-15 08:40:45 +00:00
ProtocolVersion::V3
2019-09-06 10:55:00 +00:00
);
}
2019-10-06 14:39:18 +00:00
#[test]
fn test_from() {
assert_eq!(
ExtInf::from(Duration::from_secs(1)),
ExtInf::new(Duration::from_secs(1))
);
}
2019-09-06 10:55:00 +00:00
}