1
0
Fork 0
mirror of https://github.com/sile/hls_m3u8.git synced 2024-05-03 09:09:06 +00:00
hls_m3u8/src/tags/media_segment/byte_range.rs
2020-04-22 10:34:23 +02:00

257 lines
6.4 KiB
Rust

use std::convert::TryFrom;
use std::fmt;
use core::ops::{Add, AddAssign, Sub, SubAssign};
use derive_more::{AsMut, AsRef, Deref, DerefMut, From};
use crate::types::{ByteRange, ProtocolVersion};
use crate::utils::tag;
use crate::{Error, RequiredVersion};
/// Indicates that a [`MediaSegment`] is a sub-range of the resource identified
/// by its `URI`.
///
/// # Example
///
/// Constructing an [`ExtXByteRange`]:
///
/// ```
/// # use hls_m3u8::tags::ExtXByteRange;
/// assert_eq!(ExtXByteRange::from(22..55), ExtXByteRange::from(22..=54));
/// ```
///
/// It is also possible to omit the start, in which case it assumes that the
/// [`ExtXByteRange`] starts at the byte after the end of the previous
/// [`ExtXByteRange`] or 0 if there is no previous one.
///
/// ```
/// # use hls_m3u8::tags::ExtXByteRange;
/// assert_eq!(ExtXByteRange::from(..55), ExtXByteRange::from(..=54));
/// ```
///
/// [`MediaSegment`]: crate::MediaSegment
#[derive(
AsRef, AsMut, From, Deref, DerefMut, Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord,
)]
#[from(forward)]
pub struct ExtXByteRange(ByteRange);
impl ExtXByteRange {
pub(crate) const PREFIX: &'static str = "#EXT-X-BYTERANGE:";
/// Adds `num` to the `start` and `end` of the range.
///
/// # Example
///
/// ```
/// # use hls_m3u8::tags::ExtXByteRange;
/// let range = ExtXByteRange::from(10..22);
/// let nrange = range.saturating_add(5);
///
/// assert_eq!(nrange.len(), range.len());
/// assert_eq!(nrange.start(), range.start().map(|c| c + 5));
/// ```
///
/// # Overflow
///
/// If the range is saturated it will not overflow and instead
/// stay at it's current value.
///
/// ```
/// # use hls_m3u8::tags::ExtXByteRange;
/// let range = ExtXByteRange::from(5..usize::max_value());
///
/// // this would cause the end to overflow
/// let nrange = range.saturating_add(1);
///
/// // but the range remains unchanged
/// assert_eq!(range, nrange);
/// ```
///
/// # Note
///
/// The length of the range will remain unchanged,
/// if the `start` is `Some`.
#[inline]
#[must_use]
pub fn saturating_add(self, num: usize) -> Self { Self(self.0.saturating_add(num)) }
/// Subtracts `num` from the `start` and `end` of the range.
///
/// # Example
///
/// ```
/// # use hls_m3u8::tags::ExtXByteRange;
/// let range = ExtXByteRange::from(10..22);
/// let nrange = range.saturating_sub(5);
///
/// assert_eq!(nrange.len(), range.len());
/// assert_eq!(nrange.start(), range.start().map(|c| c - 5));
/// ```
///
/// # Underflow
///
/// If the range is saturated it will not underflow and instead stay
/// at it's current value.
///
/// ```
/// # use hls_m3u8::tags::ExtXByteRange;
/// let range = ExtXByteRange::from(0..10);
///
/// // this would cause the start to underflow
/// let nrange = range.saturating_sub(1);
///
/// // but the range remains unchanged
/// assert_eq!(range, nrange);
/// ```
///
/// # Note
///
/// The length of the range will remain unchanged,
/// if the `start` is `Some`.
#[inline]
#[must_use]
pub fn saturating_sub(self, num: usize) -> Self { Self(self.0.saturating_sub(num)) }
/// Returns a shared reference to the underlying [`ByteRange`].
///
/// # Example
///
/// ```
/// # use hls_m3u8::tags::ExtXByteRange;
/// use hls_m3u8::types::ByteRange;
///
/// assert_eq!(
/// ExtXByteRange::from(2..11).as_byte_range(),
/// &ByteRange::from(2..11)
/// );
/// ```
#[inline]
#[must_use]
pub const fn as_byte_range(&self) -> &ByteRange { &self.0 }
}
/// This tag requires [`ProtocolVersion::V4`].
impl RequiredVersion for ExtXByteRange {
fn required_version(&self) -> ProtocolVersion { ProtocolVersion::V4 }
}
impl Into<ByteRange> for ExtXByteRange {
fn into(self) -> ByteRange { self.0 }
}
impl<T> Sub<T> for ExtXByteRange
where
ByteRange: Sub<T, Output = ByteRange>,
{
type Output = Self;
#[must_use]
#[inline]
fn sub(self, rhs: T) -> Self::Output { Self(self.0.sub(rhs)) }
}
impl<T> SubAssign<T> for ExtXByteRange
where
ByteRange: SubAssign<T>,
{
#[inline]
fn sub_assign(&mut self, other: T) { self.0.sub_assign(other); }
}
impl<T> Add<T> for ExtXByteRange
where
ByteRange: Add<T, Output = ByteRange>,
{
type Output = Self;
#[must_use]
#[inline]
fn add(self, rhs: T) -> Self::Output { Self(self.0.add(rhs)) }
}
impl<T> AddAssign<T> for ExtXByteRange
where
ByteRange: AddAssign<T>,
{
#[inline]
fn add_assign(&mut self, other: T) { self.0.add_assign(other); }
}
impl fmt::Display for ExtXByteRange {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", Self::PREFIX)?;
write!(f, "{}", self.0)?;
Ok(())
}
}
impl TryFrom<&str> for ExtXByteRange {
type Error = Error;
fn try_from(input: &str) -> Result<Self, Self::Error> {
let input = tag(input, Self::PREFIX)?;
Ok(Self(ByteRange::try_from(input)?))
}
}
#[cfg(test)]
mod test {
use super::*;
use pretty_assertions::assert_eq;
#[test]
fn test_display() {
assert_eq!(
ExtXByteRange::from(2..15).to_string(),
"#EXT-X-BYTERANGE:13@2".to_string()
);
assert_eq!(
ExtXByteRange::from(..22).to_string(),
"#EXT-X-BYTERANGE:22".to_string()
);
}
#[test]
fn test_parser() {
assert_eq!(
ExtXByteRange::from(2..15),
ExtXByteRange::try_from("#EXT-X-BYTERANGE:13@2").unwrap()
);
assert_eq!(
ExtXByteRange::from(..22),
ExtXByteRange::try_from("#EXT-X-BYTERANGE:22").unwrap()
);
}
#[test]
fn test_deref() {
let byte_range = ExtXByteRange::from(0..22);
assert_eq!(byte_range.len(), 22);
assert_eq!(byte_range.start(), Some(0));
}
#[test]
fn test_deref_mut() {
let mut byte_range = ExtXByteRange::from(10..110);
byte_range.set_start(Some(50));
assert_eq!(byte_range.len(), 60);
assert_eq!(byte_range.start(), Some(50));
}
#[test]
fn test_required_version() {
assert_eq!(
ExtXByteRange::from(5..20).required_version(),
ProtocolVersion::V4
);
}
}