1
0
Fork 0
mirror of https://github.com/sile/hls_m3u8.git synced 2024-11-25 08:31:00 +00:00

move types into their own files

This commit is contained in:
Luro02 2019-09-06 13:20:40 +02:00
parent 3ecbbd9acb
commit 5da2fa8104
19 changed files with 977 additions and 840 deletions

View file

@ -1,840 +0,0 @@
//! Miscellaneous types.
use crate::attribute::AttributePairs;
use crate::{Error, ErrorKind, Result};
use std::fmt;
use std::ops::Deref;
use std::str::{self, FromStr};
use std::time::Duration;
use trackable::error::ErrorKindExt;
/// String that represents a single line in a playlist file.
///
/// See: [4.1. Definition of a Playlist]
///
/// [4.1. Definition of a Playlist]: https://tools.ietf.org/html/rfc8216#section-4.1
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SingleLineString(String);
impl SingleLineString {
/// Makes a new `SingleLineString` instance.
///
/// # Errors
///
/// If the given string contains any control characters,
/// this function will return an error which has the kind `ErrorKind::InvalidInput`.
pub fn new<T: Into<String>>(s: T) -> Result<Self> {
let s = s.into();
track_assert!(!s.chars().any(|c| c.is_control()), ErrorKind::InvalidInput);
Ok(SingleLineString(s))
}
}
impl Deref for SingleLineString {
type Target = str;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl AsRef<str> for SingleLineString {
fn as_ref(&self) -> &str {
&self.0
}
}
impl fmt::Display for SingleLineString {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
/// Quoted string.
///
/// See: [4.2. Attribute Lists]
///
/// [4.2. Attribute Lists]: https://tools.ietf.org/html/rfc8216#section-4.2
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct QuotedString(String);
impl QuotedString {
/// Makes a new `QuotedString` instance.
///
/// # Errors
///
/// If the given string contains any control characters or double-quote character,
/// this function will return an error which has the kind `ErrorKind::InvalidInput`.
pub fn new<T: Into<String>>(s: T) -> Result<Self> {
let s = s.into();
track_assert!(
!s.chars().any(|c| c.is_control() || c == '"'),
ErrorKind::InvalidInput
);
Ok(QuotedString(s))
}
}
impl Deref for QuotedString {
type Target = str;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl AsRef<str> for QuotedString {
fn as_ref(&self) -> &str {
&self.0
}
}
impl fmt::Display for QuotedString {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self.0)
}
}
impl FromStr for QuotedString {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
let len = s.len();
let bytes = s.as_bytes();
track_assert!(len >= 2, ErrorKind::InvalidInput);
track_assert_eq!(bytes[0], b'"', ErrorKind::InvalidInput);
track_assert_eq!(bytes[len - 1], b'"', ErrorKind::InvalidInput);
let s = unsafe { str::from_utf8_unchecked(&bytes[1..len - 1]) };
track!(QuotedString::new(s))
}
}
/// Decimal resolution.
///
/// See: [4.2. Attribute Lists]
///
/// [4.2. Attribute Lists]: https://tools.ietf.org/html/rfc8216#section-4.2
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct DecimalResolution {
/// Horizontal pixel dimension.
pub width: usize,
/// Vertical pixel dimension.
pub height: usize,
}
impl fmt::Display for DecimalResolution {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}x{}", self.width, self.height)
}
}
impl FromStr for DecimalResolution {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
let mut tokens = s.splitn(2, 'x');
let width = tokens.next().expect("Never fails");
let height = track_assert_some!(tokens.next(), ErrorKind::InvalidInput);
Ok(DecimalResolution {
width: track!(width.parse().map_err(|e| ErrorKind::InvalidInput.cause(e)))?,
height: track!(height.parse().map_err(|e| ErrorKind::InvalidInput.cause(e)))?,
})
}
}
/// Non-negative decimal floating-point number.
///
/// See: [4.2. Attribute Lists]
///
/// [4.2. Attribute Lists]: https://tools.ietf.org/html/rfc8216#section-4.2
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub struct DecimalFloatingPoint(f64);
impl DecimalFloatingPoint {
/// Makes a new `DecimalFloatingPoint` instance.
///
/// # Errors
///
/// The given value must have a positive sign and be finite,
/// otherwise this function will return an error that has the kind `ErrorKind::InvalidInput`.
pub fn new(n: f64) -> Result<Self> {
track_assert!(n.is_sign_positive(), ErrorKind::InvalidInput);
track_assert!(n.is_finite(), ErrorKind::InvalidInput);
Ok(DecimalFloatingPoint(n))
}
/// Converts `DecimalFloatingPoint` to `f64`.
pub fn as_f64(self) -> f64 {
self.0
}
pub(crate) fn to_duration(self) -> Duration {
let secs = self.0 as u64;
let nanos = (self.0.fract() * 1_000_000_000.0) as u32;
Duration::new(secs, nanos)
}
pub(crate) fn from_duration(duration: Duration) -> Self {
let n =
(duration.as_secs() as f64) + (f64::from(duration.subsec_nanos()) / 1_000_000_000.0);
DecimalFloatingPoint(n)
}
}
impl From<u32> for DecimalFloatingPoint {
fn from(f: u32) -> Self {
DecimalFloatingPoint(f64::from(f))
}
}
impl Eq for DecimalFloatingPoint {}
impl fmt::Display for DecimalFloatingPoint {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
impl FromStr for DecimalFloatingPoint {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
track_assert!(
s.chars().all(|c| c.is_digit(10) || c == '.'),
ErrorKind::InvalidInput
);
let n = track!(s.parse().map_err(|e| ErrorKind::InvalidInput.cause(e)))?;
Ok(DecimalFloatingPoint(n))
}
}
/// Signed decimal floating-point number.
///
/// See: [4.2. Attribute Lists]
///
/// [4.2. Attribute Lists]: https://tools.ietf.org/html/rfc8216#section-4.2
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub struct SignedDecimalFloatingPoint(f64);
impl SignedDecimalFloatingPoint {
/// Makes a new `SignedDecimalFloatingPoint` instance.
///
/// # Errors
///
/// The given value must be finite,
/// otherwise this function will return an error that has the kind `ErrorKind::InvalidInput`.
pub fn new(n: f64) -> Result<Self> {
track_assert!(n.is_finite(), ErrorKind::InvalidInput);
Ok(SignedDecimalFloatingPoint(n))
}
/// Converts `DecimalFloatingPoint` to `f64`.
pub fn as_f64(self) -> f64 {
self.0
}
}
impl From<i32> for SignedDecimalFloatingPoint {
fn from(f: i32) -> Self {
SignedDecimalFloatingPoint(f64::from(f))
}
}
impl Eq for SignedDecimalFloatingPoint {}
impl fmt::Display for SignedDecimalFloatingPoint {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
impl FromStr for SignedDecimalFloatingPoint {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
track_assert!(
s.chars().all(|c| c.is_digit(10) || c == '.' || c == '-'),
ErrorKind::InvalidInput
);
let n = track!(s.parse().map_err(|e| ErrorKind::InvalidInput.cause(e)))?;
Ok(SignedDecimalFloatingPoint(n))
}
}
/// Hexadecimal sequence.
///
/// See: [4.2. Attribute Lists]
///
/// [4.2. Attribute Lists]: https://tools.ietf.org/html/rfc8216#section-4.2
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct HexadecimalSequence(Vec<u8>);
impl HexadecimalSequence {
/// Makes a new `HexadecimalSequence` instance.
pub fn new<T: Into<Vec<u8>>>(v: T) -> Self {
HexadecimalSequence(v.into())
}
/// Converts into the underlying byte sequence.
pub fn into_bytes(self) -> Vec<u8> {
self.0
}
}
impl Deref for HexadecimalSequence {
type Target = [u8];
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl AsRef<[u8]> for HexadecimalSequence {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl fmt::Display for HexadecimalSequence {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "0x")?;
for b in &self.0 {
write!(f, "{:02x}", b)?;
}
Ok(())
}
}
impl FromStr for HexadecimalSequence {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
track_assert!(
s.starts_with("0x") || s.starts_with("0X"),
ErrorKind::InvalidInput
);
track_assert!(s.len() % 2 == 0, ErrorKind::InvalidInput);
let mut v = Vec::with_capacity(s.len() / 2 - 1);
for c in s.as_bytes().chunks(2).skip(1) {
let d = track!(str::from_utf8(c).map_err(|e| ErrorKind::InvalidInput.cause(e)))?;
let b =
track!(u8::from_str_radix(d, 16).map_err(|e| ErrorKind::InvalidInput.cause(e)))?;
v.push(b);
}
Ok(HexadecimalSequence(v))
}
}
/// Initialization vector.
///
/// See: [4.3.2.4. EXT-X-KEY]
///
/// [4.3.2.4. EXT-X-KEY]: https://tools.ietf.org/html/rfc8216#section-4.3.2.4
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct InitializationVector(pub [u8; 16]);
impl Deref for InitializationVector {
type Target = [u8];
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl AsRef<[u8]> for InitializationVector {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl fmt::Display for InitializationVector {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "0x")?;
for b in &self.0 {
write!(f, "{:02x}", b)?;
}
Ok(())
}
}
impl FromStr for InitializationVector {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
track_assert!(
s.starts_with("0x") || s.starts_with("0X"),
ErrorKind::InvalidInput
);
track_assert_eq!(s.len() - 2, 32, ErrorKind::InvalidInput);
let mut v = [0; 16];
for (i, c) in s.as_bytes().chunks(2).skip(1).enumerate() {
let d = track!(str::from_utf8(c).map_err(|e| ErrorKind::InvalidInput.cause(e)))?;
let b =
track!(u8::from_str_radix(d, 16).map_err(|e| ErrorKind::InvalidInput.cause(e)))?;
v[i] = b;
}
Ok(InitializationVector(v))
}
}
/// [7. Protocol Version Compatibility]
///
/// [7. Protocol Version Compatibility]: https://tools.ietf.org/html/rfc8216#section-7
#[allow(missing_docs)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ProtocolVersion {
V1,
V2,
V3,
V4,
V5,
V6,
V7,
}
impl fmt::Display for ProtocolVersion {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let n = match *self {
ProtocolVersion::V1 => 1,
ProtocolVersion::V2 => 2,
ProtocolVersion::V3 => 3,
ProtocolVersion::V4 => 4,
ProtocolVersion::V5 => 5,
ProtocolVersion::V6 => 6,
ProtocolVersion::V7 => 7,
};
write!(f, "{}", n)
}
}
impl FromStr for ProtocolVersion {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
Ok(match s {
"1" => ProtocolVersion::V1,
"2" => ProtocolVersion::V2,
"3" => ProtocolVersion::V3,
"4" => ProtocolVersion::V4,
"5" => ProtocolVersion::V5,
"6" => ProtocolVersion::V6,
"7" => ProtocolVersion::V7,
_ => track_panic!(ErrorKind::InvalidInput, "Unknown protocol version: {:?}", s),
})
}
}
/// Byte range.
///
/// See: [4.3.2.2. EXT-X-BYTERANGE]
///
/// [4.3.2.2. EXT-X-BYTERANGE]: https://tools.ietf.org/html/rfc8216#section-4.3.2.2
#[allow(missing_docs)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ByteRange {
pub length: usize,
pub start: Option<usize>,
}
impl fmt::Display for ByteRange {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.length)?;
if let Some(x) = self.start {
write!(f, "@{}", x)?;
}
Ok(())
}
}
impl FromStr for ByteRange {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
let mut tokens = s.splitn(2, '@');
let length = tokens.next().expect("Never fails");
let start = if let Some(start) = tokens.next() {
Some(track!(start
.parse()
.map_err(|e| ErrorKind::InvalidInput.cause(e)))?)
} else {
None
};
Ok(ByteRange {
length: track!(length.parse().map_err(|e| ErrorKind::InvalidInput.cause(e)))?,
start,
})
}
}
/// Decryption key.
///
/// See: [4.3.2.4. EXT-X-KEY]
///
/// [4.3.2.4. EXT-X-KEY]: https://tools.ietf.org/html/rfc8216#section-4.3.2.4
#[allow(missing_docs)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct DecryptionKey {
pub method: EncryptionMethod,
pub uri: QuotedString,
pub iv: Option<InitializationVector>,
pub key_format: Option<QuotedString>,
pub key_format_versions: Option<QuotedString>,
}
impl DecryptionKey {
pub(crate) fn requires_version(&self) -> ProtocolVersion {
if self.key_format.is_some() | self.key_format_versions.is_some() {
ProtocolVersion::V5
} else if self.iv.is_some() {
ProtocolVersion::V2
} else {
ProtocolVersion::V1
}
}
}
impl fmt::Display for DecryptionKey {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "METHOD={}", self.method)?;
write!(f, ",URI={}", self.uri)?;
if let Some(ref x) = self.iv {
write!(f, ",IV={}", x)?;
}
if let Some(ref x) = self.key_format {
write!(f, ",KEYFORMAT={}", x)?;
}
if let Some(ref x) = self.key_format_versions {
write!(f, ",KEYFORMATVERSIONS={}", x)?;
}
Ok(())
}
}
impl FromStr for DecryptionKey {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
let mut method = None;
let mut uri = None;
let mut iv = None;
let mut key_format = None;
let mut key_format_versions = None;
let attrs = AttributePairs::parse(s);
for attr in attrs {
let (key, value) = track!(attr)?;
match key {
"METHOD" => method = Some(track!(value.parse())?),
"URI" => uri = Some(track!(value.parse())?),
"IV" => iv = Some(track!(value.parse())?),
"KEYFORMAT" => key_format = Some(track!(value.parse())?),
"KEYFORMATVERSIONS" => key_format_versions = Some(track!(value.parse())?),
_ => {
// [6.3.1. General Client Responsibilities]
// > ignore any attribute/value pair with an unrecognized AttributeName.
}
}
}
let method = track_assert_some!(method, ErrorKind::InvalidInput);
let uri = track_assert_some!(uri, ErrorKind::InvalidInput);
Ok(DecryptionKey {
method,
uri,
iv,
key_format,
key_format_versions,
})
}
}
/// Encryption method.
///
/// See: [4.3.2.4. EXT-X-KEY]
///
/// [4.3.2.4. EXT-X-KEY]: https://tools.ietf.org/html/rfc8216#section-4.3.2.4
#[allow(missing_docs)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum EncryptionMethod {
Aes128,
SampleAes,
}
impl fmt::Display for EncryptionMethod {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
EncryptionMethod::Aes128 => "AES-128".fmt(f),
EncryptionMethod::SampleAes => "SAMPLE-AES".fmt(f),
}
}
}
impl FromStr for EncryptionMethod {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
match s {
"AES-128" => Ok(EncryptionMethod::Aes128),
"SAMPLE-AES" => Ok(EncryptionMethod::SampleAes),
_ => track_panic!(
ErrorKind::InvalidInput,
"Unknown encryption method: {:?}",
s
),
}
}
}
/// Playlist type.
///
/// See: [4.3.3.5. EXT-X-PLAYLIST-TYPE]
///
/// [4.3.3.5. EXT-X-PLAYLIST-TYPE]: https://tools.ietf.org/html/rfc8216#section-4.3.3.5
#[allow(missing_docs)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PlaylistType {
Event,
Vod,
}
impl fmt::Display for PlaylistType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
PlaylistType::Event => write!(f, "EVENT"),
PlaylistType::Vod => write!(f, "VOD"),
}
}
}
impl FromStr for PlaylistType {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
match s {
"EVENT" => Ok(PlaylistType::Event),
"VOD" => Ok(PlaylistType::Vod),
_ => track_panic!(ErrorKind::InvalidInput, "Unknown playlist type: {:?}", s),
}
}
}
/// Media type.
///
/// See: [4.3.4.1. EXT-X-MEDIA]
///
/// [4.3.4.1. EXT-X-MEDIA]: https://tools.ietf.org/html/rfc8216#section-4.3.4.1
#[allow(missing_docs)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum MediaType {
Audio,
Video,
Subtitles,
ClosedCaptions,
}
impl fmt::Display for MediaType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
MediaType::Audio => "AUDIO".fmt(f),
MediaType::Video => "VIDEO".fmt(f),
MediaType::Subtitles => "SUBTITLES".fmt(f),
MediaType::ClosedCaptions => "CLOSED-CAPTIONS".fmt(f),
}
}
}
impl FromStr for MediaType {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
Ok(match s {
"AUDIO" => MediaType::Audio,
"VIDEO" => MediaType::Video,
"SUBTITLES" => MediaType::Subtitles,
"CLOSED-CAPTIONS" => MediaType::ClosedCaptions,
_ => track_panic!(ErrorKind::InvalidInput, "Unknown media type: {:?}", s),
})
}
}
/// Identifier of a rendition within the segments in a media playlist.
///
/// See: [4.3.4.1. EXT-X-MEDIA]
///
/// [4.3.4.1. EXT-X-MEDIA]: https://tools.ietf.org/html/rfc8216#section-4.3.4.1
#[allow(missing_docs)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum InStreamId {
Cc1,
Cc2,
Cc3,
Cc4,
Service1,
Service2,
Service3,
Service4,
Service5,
Service6,
Service7,
Service8,
Service9,
Service10,
Service11,
Service12,
Service13,
Service14,
Service15,
Service16,
Service17,
Service18,
Service19,
Service20,
Service21,
Service22,
Service23,
Service24,
Service25,
Service26,
Service27,
Service28,
Service29,
Service30,
Service31,
Service32,
Service33,
Service34,
Service35,
Service36,
Service37,
Service38,
Service39,
Service40,
Service41,
Service42,
Service43,
Service44,
Service45,
Service46,
Service47,
Service48,
Service49,
Service50,
Service51,
Service52,
Service53,
Service54,
Service55,
Service56,
Service57,
Service58,
Service59,
Service60,
Service61,
Service62,
Service63,
}
impl fmt::Display for InStreamId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
format!("{:?}", self).to_uppercase().fmt(f)
}
}
impl FromStr for InStreamId {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
Ok(match s {
"CC1" => InStreamId::Cc1,
"CC2" => InStreamId::Cc2,
"CC3" => InStreamId::Cc3,
"CC4" => InStreamId::Cc4,
"SERVICE1" => InStreamId::Service1,
"SERVICE2" => InStreamId::Service2,
"SERVICE3" => InStreamId::Service3,
"SERVICE4" => InStreamId::Service4,
"SERVICE5" => InStreamId::Service5,
"SERVICE6" => InStreamId::Service6,
"SERVICE7" => InStreamId::Service7,
"SERVICE8" => InStreamId::Service8,
"SERVICE9" => InStreamId::Service9,
"SERVICE10" => InStreamId::Service10,
"SERVICE11" => InStreamId::Service11,
"SERVICE12" => InStreamId::Service12,
"SERVICE13" => InStreamId::Service13,
"SERVICE14" => InStreamId::Service14,
"SERVICE15" => InStreamId::Service15,
"SERVICE16" => InStreamId::Service16,
"SERVICE17" => InStreamId::Service17,
"SERVICE18" => InStreamId::Service18,
"SERVICE19" => InStreamId::Service19,
"SERVICE20" => InStreamId::Service20,
"SERVICE21" => InStreamId::Service21,
"SERVICE22" => InStreamId::Service22,
"SERVICE23" => InStreamId::Service23,
"SERVICE24" => InStreamId::Service24,
"SERVICE25" => InStreamId::Service25,
"SERVICE26" => InStreamId::Service26,
"SERVICE27" => InStreamId::Service27,
"SERVICE28" => InStreamId::Service28,
"SERVICE29" => InStreamId::Service29,
"SERVICE30" => InStreamId::Service30,
"SERVICE31" => InStreamId::Service31,
"SERVICE32" => InStreamId::Service32,
"SERVICE33" => InStreamId::Service33,
"SERVICE34" => InStreamId::Service34,
"SERVICE35" => InStreamId::Service35,
"SERVICE36" => InStreamId::Service36,
"SERVICE37" => InStreamId::Service37,
"SERVICE38" => InStreamId::Service38,
"SERVICE39" => InStreamId::Service39,
"SERVICE40" => InStreamId::Service40,
"SERVICE41" => InStreamId::Service41,
"SERVICE42" => InStreamId::Service42,
"SERVICE43" => InStreamId::Service43,
"SERVICE44" => InStreamId::Service44,
"SERVICE45" => InStreamId::Service45,
"SERVICE46" => InStreamId::Service46,
"SERVICE47" => InStreamId::Service47,
"SERVICE48" => InStreamId::Service48,
"SERVICE49" => InStreamId::Service49,
"SERVICE50" => InStreamId::Service50,
"SERVICE51" => InStreamId::Service51,
"SERVICE52" => InStreamId::Service52,
"SERVICE53" => InStreamId::Service53,
"SERVICE54" => InStreamId::Service54,
"SERVICE55" => InStreamId::Service55,
"SERVICE56" => InStreamId::Service56,
"SERVICE57" => InStreamId::Service57,
"SERVICE58" => InStreamId::Service58,
"SERVICE59" => InStreamId::Service59,
"SERVICE60" => InStreamId::Service60,
"SERVICE61" => InStreamId::Service61,
"SERVICE62" => InStreamId::Service62,
"SERVICE63" => InStreamId::Service63,
_ => track_panic!(ErrorKind::InvalidInput, "Unknown instream id: {:?}", s),
})
}
}
/// HDCP level.
///
/// See: [4.3.4.2. EXT-X-STREAM-INF]
///
/// [4.3.4.2. EXT-X-STREAM-INF]: https://tools.ietf.org/html/rfc8216#section-4.3.4.2
#[allow(missing_docs)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum HdcpLevel {
Type0,
None,
}
impl fmt::Display for HdcpLevel {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
HdcpLevel::Type0 => "TYPE-0".fmt(f),
HdcpLevel::None => "NONE".fmt(f),
}
}
}
impl FromStr for HdcpLevel {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
match s {
"TYPE-0" => Ok(HdcpLevel::Type0),
"NONE" => Ok(HdcpLevel::None),
_ => track_panic!(ErrorKind::InvalidInput, "Unknown HDCP level: {:?}", s),
}
}
}
/// The identifier of a closed captions group or its absence.
///
/// See: [4.3.4.2. EXT-X-STREAM-INF]
///
/// [4.3.4.2. EXT-X-STREAM-INF]: https://tools.ietf.org/html/rfc8216#section-4.3.4.2
#[allow(missing_docs)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ClosedCaptions {
GroupId(QuotedString),
None,
}
impl fmt::Display for ClosedCaptions {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
ClosedCaptions::GroupId(ref x) => x.fmt(f),
ClosedCaptions::None => "NONE".fmt(f),
}
}
}
impl FromStr for ClosedCaptions {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
if s == "NONE" {
Ok(ClosedCaptions::None)
} else {
Ok(ClosedCaptions::GroupId(track!(s.parse())?))
}
}
}
/// Session data.
///
/// See: [4.3.4.4. EXT-X-SESSION-DATA]
///
/// [4.3.4.4. EXT-X-SESSION-DATA]: https://tools.ietf.org/html/rfc8216#section-4.3.4.4
#[allow(missing_docs)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum SessionData {
Value(QuotedString),
Uri(QuotedString),
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn single_line_string() {
assert!(SingleLineString::new("foo").is_ok());
assert!(SingleLineString::new("b\rar").is_err());
}
}

45
src/types/byte_range.rs Normal file
View file

@ -0,0 +1,45 @@
use crate::{Error, ErrorKind, Result};
use std::fmt;
use std::str::{self, FromStr};
use trackable::error::ErrorKindExt;
/// Byte range.
///
/// See: [4.3.2.2. EXT-X-BYTERANGE]
///
/// [4.3.2.2. EXT-X-BYTERANGE]: https://tools.ietf.org/html/rfc8216#section-4.3.2.2
#[allow(missing_docs)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ByteRange {
pub length: usize,
pub start: Option<usize>,
}
impl fmt::Display for ByteRange {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.length)?;
if let Some(x) = self.start {
write!(f, "@{}", x)?;
}
Ok(())
}
}
impl FromStr for ByteRange {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
let mut tokens = s.splitn(2, '@');
let length = tokens.next().expect("Never fails");
let start = if let Some(start) = tokens.next() {
Some(track!(start
.parse()
.map_err(|e| ErrorKind::InvalidInput.cause(e)))?)
} else {
None
};
Ok(ByteRange {
length: track!(length.parse().map_err(|e| ErrorKind::InvalidInput.cause(e)))?,
start,
})
}
}

View file

@ -0,0 +1,36 @@
use crate::{Error, Result};
use std::fmt;
use std::str::{self, FromStr};
use crate::types::QuotedString;
/// The identifier of a closed captions group or its absence.
///
/// See: [4.3.4.2. EXT-X-STREAM-INF]
///
/// [4.3.4.2. EXT-X-STREAM-INF]: https://tools.ietf.org/html/rfc8216#section-4.3.4.2
#[allow(missing_docs)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ClosedCaptions {
GroupId(QuotedString),
None,
}
impl fmt::Display for ClosedCaptions {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
ClosedCaptions::GroupId(ref x) => x.fmt(f),
ClosedCaptions::None => "NONE".fmt(f),
}
}
}
impl FromStr for ClosedCaptions {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
if s == "NONE" {
Ok(ClosedCaptions::None)
} else {
Ok(ClosedCaptions::GroupId(track!(s.parse())?))
}
}
}

View file

@ -0,0 +1,70 @@
use crate::{Error, ErrorKind, Result};
use std::fmt;
use std::str::{self, FromStr};
use std::time::Duration;
use trackable::error::ErrorKindExt;
/// Non-negative decimal floating-point number.
///
/// See: [4.2. Attribute Lists]
///
/// [4.2. Attribute Lists]: https://tools.ietf.org/html/rfc8216#section-4.2
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub struct DecimalFloatingPoint(f64);
impl DecimalFloatingPoint {
/// Makes a new `DecimalFloatingPoint` instance.
///
/// # Errors
///
/// The given value must have a positive sign and be finite,
/// otherwise this function will return an error that has the kind `ErrorKind::InvalidInput`.
pub fn new(n: f64) -> Result<Self> {
track_assert!(n.is_sign_positive(), ErrorKind::InvalidInput);
track_assert!(n.is_finite(), ErrorKind::InvalidInput);
Ok(DecimalFloatingPoint(n))
}
/// Converts `DecimalFloatingPoint` to `f64`.
pub fn as_f64(self) -> f64 {
self.0
}
pub(crate) fn to_duration(self) -> Duration {
let secs = self.0 as u64;
let nanos = (self.0.fract() * 1_000_000_000.0) as u32;
Duration::new(secs, nanos)
}
pub(crate) fn from_duration(duration: Duration) -> Self {
let n =
(duration.as_secs() as f64) + (f64::from(duration.subsec_nanos()) / 1_000_000_000.0);
DecimalFloatingPoint(n)
}
}
impl From<u32> for DecimalFloatingPoint {
fn from(f: u32) -> Self {
DecimalFloatingPoint(f64::from(f))
}
}
impl Eq for DecimalFloatingPoint {}
impl fmt::Display for DecimalFloatingPoint {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
impl FromStr for DecimalFloatingPoint {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
track_assert!(
s.chars().all(|c| c.is_digit(10) || c == '.'),
ErrorKind::InvalidInput
);
let n = track!(s.parse().map_err(|e| ErrorKind::InvalidInput.cause(e)))?;
Ok(DecimalFloatingPoint(n))
}
}

View file

@ -0,0 +1,37 @@
use crate::{Error, ErrorKind, Result};
use std::fmt;
use std::str::{self, FromStr};
use trackable::error::ErrorKindExt;
/// Decimal resolution.
///
/// See: [4.2. Attribute Lists]
///
/// [4.2. Attribute Lists]: https://tools.ietf.org/html/rfc8216#section-4.2
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct DecimalResolution {
/// Horizontal pixel dimension.
pub width: usize,
/// Vertical pixel dimension.
pub height: usize,
}
impl fmt::Display for DecimalResolution {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}x{}", self.width, self.height)
}
}
impl FromStr for DecimalResolution {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
let mut tokens = s.splitn(2, 'x');
let width = tokens.next().expect("Never fails");
let height = track_assert_some!(tokens.next(), ErrorKind::InvalidInput);
Ok(DecimalResolution {
width: track!(width.parse().map_err(|e| ErrorKind::InvalidInput.cause(e)))?,
height: track!(height.parse().map_err(|e| ErrorKind::InvalidInput.cause(e)))?,
})
}
}

View file

@ -0,0 +1,84 @@
use crate::attribute::AttributePairs;
use crate::{Error, ErrorKind, Result};
use crate::types::{QuotedString, InitializationVector, ProtocolVersion, EncryptionMethod};
use std::fmt;
use std::str::{self, FromStr};
/// Decryption key.
///
/// See: [4.3.2.4. EXT-X-KEY]
///
/// [4.3.2.4. EXT-X-KEY]: https://tools.ietf.org/html/rfc8216#section-4.3.2.4
#[allow(missing_docs)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct DecryptionKey {
pub method: EncryptionMethod,
pub uri: QuotedString,
pub iv: Option<InitializationVector>,
pub key_format: Option<QuotedString>,
pub key_format_versions: Option<QuotedString>,
}
impl DecryptionKey {
pub(crate) fn requires_version(&self) -> ProtocolVersion {
if self.key_format.is_some() | self.key_format_versions.is_some() {
ProtocolVersion::V5
} else if self.iv.is_some() {
ProtocolVersion::V2
} else {
ProtocolVersion::V1
}
}
}
impl fmt::Display for DecryptionKey {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "METHOD={}", self.method)?;
write!(f, ",URI={}", self.uri)?;
if let Some(ref x) = self.iv {
write!(f, ",IV={}", x)?;
}
if let Some(ref x) = self.key_format {
write!(f, ",KEYFORMAT={}", x)?;
}
if let Some(ref x) = self.key_format_versions {
write!(f, ",KEYFORMATVERSIONS={}", x)?;
}
Ok(())
}
}
impl FromStr for DecryptionKey {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
let mut method = None;
let mut uri = None;
let mut iv = None;
let mut key_format = None;
let mut key_format_versions = None;
let attrs = AttributePairs::parse(s);
for attr in attrs {
let (key, value) = track!(attr)?;
match key {
"METHOD" => method = Some(track!(value.parse())?),
"URI" => uri = Some(track!(value.parse())?),
"IV" => iv = Some(track!(value.parse())?),
"KEYFORMAT" => key_format = Some(track!(value.parse())?),
"KEYFORMATVERSIONS" => key_format_versions = Some(track!(value.parse())?),
_ => {
// [6.3.1. General Client Responsibilities]
// > ignore any attribute/value pair with an unrecognized AttributeName.
}
}
}
let method = track_assert_some!(method, ErrorKind::InvalidInput);
let uri = track_assert_some!(uri, ErrorKind::InvalidInput);
Ok(DecryptionKey {
method,
uri,
iv,
key_format,
key_format_versions,
})
}
}

View file

@ -0,0 +1,39 @@
use crate::{Error, ErrorKind, Result};
use std::fmt;
use std::str::{self, FromStr};
/// Encryption method.
///
/// See: [4.3.2.4. EXT-X-KEY]
///
/// [4.3.2.4. EXT-X-KEY]: https://tools.ietf.org/html/rfc8216#section-4.3.2.4
#[allow(missing_docs)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum EncryptionMethod {
Aes128,
SampleAes,
}
impl fmt::Display for EncryptionMethod {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
EncryptionMethod::Aes128 => "AES-128".fmt(f),
EncryptionMethod::SampleAes => "SAMPLE-AES".fmt(f),
}
}
}
impl FromStr for EncryptionMethod {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
match s {
"AES-128" => Ok(EncryptionMethod::Aes128),
"SAMPLE-AES" => Ok(EncryptionMethod::SampleAes),
_ => track_panic!(
ErrorKind::InvalidInput,
"Unknown encryption method: {:?}",
s
),
}
}
}

35
src/types/hdcp_level.rs Normal file
View file

@ -0,0 +1,35 @@
use crate::{Error, ErrorKind, Result};
use std::fmt;
use std::str::{self, FromStr};
/// HDCP level.
///
/// See: [4.3.4.2. EXT-X-STREAM-INF]
///
/// [4.3.4.2. EXT-X-STREAM-INF]: https://tools.ietf.org/html/rfc8216#section-4.3.4.2
#[allow(missing_docs)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum HdcpLevel {
Type0,
None,
}
impl fmt::Display for HdcpLevel {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
HdcpLevel::Type0 => "TYPE-0".fmt(f),
HdcpLevel::None => "NONE".fmt(f),
}
}
}
impl FromStr for HdcpLevel {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
match s {
"TYPE-0" => Ok(HdcpLevel::Type0),
"NONE" => Ok(HdcpLevel::None),
_ => track_panic!(ErrorKind::InvalidInput, "Unknown HDCP level: {:?}", s),
}
}
}

View file

@ -0,0 +1,68 @@
use crate::{Error, ErrorKind, Result};
use std::fmt;
use std::ops::Deref;
use std::str::{self, FromStr};
use trackable::error::ErrorKindExt;
/// Hexadecimal sequence.
///
/// See: [4.2. Attribute Lists]
///
/// [4.2. Attribute Lists]: https://tools.ietf.org/html/rfc8216#section-4.2
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct HexadecimalSequence(Vec<u8>);
impl HexadecimalSequence {
/// Makes a new `HexadecimalSequence` instance.
pub fn new<T: Into<Vec<u8>>>(v: T) -> Self {
HexadecimalSequence(v.into())
}
/// Converts into the underlying byte sequence.
pub fn into_bytes(self) -> Vec<u8> {
self.0
}
}
impl Deref for HexadecimalSequence {
type Target = [u8];
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl AsRef<[u8]> for HexadecimalSequence {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl fmt::Display for HexadecimalSequence {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "0x")?;
for b in &self.0 {
write!(f, "{:02x}", b)?;
}
Ok(())
}
}
impl FromStr for HexadecimalSequence {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
track_assert!(
s.starts_with("0x") || s.starts_with("0X"),
ErrorKind::InvalidInput
);
track_assert!(s.len() % 2 == 0, ErrorKind::InvalidInput);
let mut v = Vec::with_capacity(s.len() / 2 - 1);
for c in s.as_bytes().chunks(2).skip(1) {
let d = track!(str::from_utf8(c).map_err(|e| ErrorKind::InvalidInput.cause(e)))?;
let b =
track!(u8::from_str_radix(d, 16).map_err(|e| ErrorKind::InvalidInput.cause(e)))?;
v.push(b);
}
Ok(HexadecimalSequence(v))
}
}

162
src/types/in_stream_id.rs Normal file
View file

@ -0,0 +1,162 @@
use crate::{Error, ErrorKind, Result};
use std::fmt;
use std::str::{self, FromStr};
/// Identifier of a rendition within the segments in a media playlist.
///
/// See: [4.3.4.1. EXT-X-MEDIA]
///
/// [4.3.4.1. EXT-X-MEDIA]: https://tools.ietf.org/html/rfc8216#section-4.3.4.1
#[allow(missing_docs)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum InStreamId {
Cc1,
Cc2,
Cc3,
Cc4,
Service1,
Service2,
Service3,
Service4,
Service5,
Service6,
Service7,
Service8,
Service9,
Service10,
Service11,
Service12,
Service13,
Service14,
Service15,
Service16,
Service17,
Service18,
Service19,
Service20,
Service21,
Service22,
Service23,
Service24,
Service25,
Service26,
Service27,
Service28,
Service29,
Service30,
Service31,
Service32,
Service33,
Service34,
Service35,
Service36,
Service37,
Service38,
Service39,
Service40,
Service41,
Service42,
Service43,
Service44,
Service45,
Service46,
Service47,
Service48,
Service49,
Service50,
Service51,
Service52,
Service53,
Service54,
Service55,
Service56,
Service57,
Service58,
Service59,
Service60,
Service61,
Service62,
Service63,
}
impl fmt::Display for InStreamId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
format!("{:?}", self).to_uppercase().fmt(f)
}
}
impl FromStr for InStreamId {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
Ok(match s {
"CC1" => InStreamId::Cc1,
"CC2" => InStreamId::Cc2,
"CC3" => InStreamId::Cc3,
"CC4" => InStreamId::Cc4,
"SERVICE1" => InStreamId::Service1,
"SERVICE2" => InStreamId::Service2,
"SERVICE3" => InStreamId::Service3,
"SERVICE4" => InStreamId::Service4,
"SERVICE5" => InStreamId::Service5,
"SERVICE6" => InStreamId::Service6,
"SERVICE7" => InStreamId::Service7,
"SERVICE8" => InStreamId::Service8,
"SERVICE9" => InStreamId::Service9,
"SERVICE10" => InStreamId::Service10,
"SERVICE11" => InStreamId::Service11,
"SERVICE12" => InStreamId::Service12,
"SERVICE13" => InStreamId::Service13,
"SERVICE14" => InStreamId::Service14,
"SERVICE15" => InStreamId::Service15,
"SERVICE16" => InStreamId::Service16,
"SERVICE17" => InStreamId::Service17,
"SERVICE18" => InStreamId::Service18,
"SERVICE19" => InStreamId::Service19,
"SERVICE20" => InStreamId::Service20,
"SERVICE21" => InStreamId::Service21,
"SERVICE22" => InStreamId::Service22,
"SERVICE23" => InStreamId::Service23,
"SERVICE24" => InStreamId::Service24,
"SERVICE25" => InStreamId::Service25,
"SERVICE26" => InStreamId::Service26,
"SERVICE27" => InStreamId::Service27,
"SERVICE28" => InStreamId::Service28,
"SERVICE29" => InStreamId::Service29,
"SERVICE30" => InStreamId::Service30,
"SERVICE31" => InStreamId::Service31,
"SERVICE32" => InStreamId::Service32,
"SERVICE33" => InStreamId::Service33,
"SERVICE34" => InStreamId::Service34,
"SERVICE35" => InStreamId::Service35,
"SERVICE36" => InStreamId::Service36,
"SERVICE37" => InStreamId::Service37,
"SERVICE38" => InStreamId::Service38,
"SERVICE39" => InStreamId::Service39,
"SERVICE40" => InStreamId::Service40,
"SERVICE41" => InStreamId::Service41,
"SERVICE42" => InStreamId::Service42,
"SERVICE43" => InStreamId::Service43,
"SERVICE44" => InStreamId::Service44,
"SERVICE45" => InStreamId::Service45,
"SERVICE46" => InStreamId::Service46,
"SERVICE47" => InStreamId::Service47,
"SERVICE48" => InStreamId::Service48,
"SERVICE49" => InStreamId::Service49,
"SERVICE50" => InStreamId::Service50,
"SERVICE51" => InStreamId::Service51,
"SERVICE52" => InStreamId::Service52,
"SERVICE53" => InStreamId::Service53,
"SERVICE54" => InStreamId::Service54,
"SERVICE55" => InStreamId::Service55,
"SERVICE56" => InStreamId::Service56,
"SERVICE57" => InStreamId::Service57,
"SERVICE58" => InStreamId::Service58,
"SERVICE59" => InStreamId::Service59,
"SERVICE60" => InStreamId::Service60,
"SERVICE61" => InStreamId::Service61,
"SERVICE62" => InStreamId::Service62,
"SERVICE63" => InStreamId::Service63,
_ => track_panic!(ErrorKind::InvalidInput, "Unknown instream id: {:?}", s),
})
}
}

View file

@ -0,0 +1,56 @@
use crate::{Error, ErrorKind, Result};
use std::fmt;
use std::ops::Deref;
use std::str::{self, FromStr};
use trackable::error::ErrorKindExt;
/// Initialization vector.
///
/// See: [4.3.2.4. EXT-X-KEY]
///
/// [4.3.2.4. EXT-X-KEY]: https://tools.ietf.org/html/rfc8216#section-4.3.2.4
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct InitializationVector(pub [u8; 16]);
impl Deref for InitializationVector {
type Target = [u8];
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl AsRef<[u8]> for InitializationVector {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl fmt::Display for InitializationVector {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "0x")?;
for b in &self.0 {
write!(f, "{:02x}", b)?;
}
Ok(())
}
}
impl FromStr for InitializationVector {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
track_assert!(
s.starts_with("0x") || s.starts_with("0X"),
ErrorKind::InvalidInput
);
track_assert_eq!(s.len() - 2, 32, ErrorKind::InvalidInput);
let mut v = [0; 16];
for (i, c) in s.as_bytes().chunks(2).skip(1).enumerate() {
let d = track!(str::from_utf8(c).map_err(|e| ErrorKind::InvalidInput.cause(e)))?;
let b =
track!(u8::from_str_radix(d, 16).map_err(|e| ErrorKind::InvalidInput.cause(e)))?;
v[i] = b;
}
Ok(InitializationVector(v))
}
}

41
src/types/media_type.rs Normal file
View file

@ -0,0 +1,41 @@
use crate::{Error, ErrorKind, Result};
use std::fmt;
use std::str::{self, FromStr};
/// Media type.
///
/// See: [4.3.4.1. EXT-X-MEDIA]
///
/// [4.3.4.1. EXT-X-MEDIA]: https://tools.ietf.org/html/rfc8216#section-4.3.4.1
#[allow(missing_docs)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum MediaType {
Audio,
Video,
Subtitles,
ClosedCaptions,
}
impl fmt::Display for MediaType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
MediaType::Audio => "AUDIO".fmt(f),
MediaType::Video => "VIDEO".fmt(f),
MediaType::Subtitles => "SUBTITLES".fmt(f),
MediaType::ClosedCaptions => "CLOSED-CAPTIONS".fmt(f),
}
}
}
impl FromStr for MediaType {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
Ok(match s {
"AUDIO" => MediaType::Audio,
"VIDEO" => MediaType::Video,
"SUBTITLES" => MediaType::Subtitles,
"CLOSED-CAPTIONS" => MediaType::ClosedCaptions,
_ => track_panic!(ErrorKind::InvalidInput, "Unknown media type: {:?}", s),
})
}
}

36
src/types/mod.rs Normal file
View file

@ -0,0 +1,36 @@
//! Miscellaneous types.
mod byte_range;
mod closed_captions;
mod decimal_floating_point;
mod decimal_resolution;
mod decryption_key;
mod encryption_method;
mod hdcp_level;
mod hexadecimal_sequence;
mod in_stream_id;
mod initialization_vector;
mod media_type;
mod playlist_type;
mod protocol_version;
mod quoted_string;
mod session_data;
mod signed_decimal_floating_point;
mod single_line_string;
pub use byte_range::*;
pub use closed_captions::*;
pub use decimal_floating_point::*;
pub use decimal_resolution::*;
pub use decryption_key::*;
pub use encryption_method::*;
pub use hdcp_level::*;
pub use hexadecimal_sequence::*;
pub use in_stream_id::*;
pub use initialization_vector::*;
pub use media_type::*;
pub use playlist_type::*;
pub use protocol_version::*;
pub use quoted_string::*;
pub use session_data::*;
pub use signed_decimal_floating_point::*;
pub use single_line_string::*;

View file

@ -0,0 +1,35 @@
use crate::{Error, ErrorKind, Result};
use std::fmt;
use std::str::{self, FromStr};
/// Playlist type.
///
/// See: [4.3.3.5. EXT-X-PLAYLIST-TYPE]
///
/// [4.3.3.5. EXT-X-PLAYLIST-TYPE]: https://tools.ietf.org/html/rfc8216#section-4.3.3.5
#[allow(missing_docs)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PlaylistType {
Event,
Vod,
}
impl fmt::Display for PlaylistType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
PlaylistType::Event => write!(f, "EVENT"),
PlaylistType::Vod => write!(f, "VOD"),
}
}
}
impl FromStr for PlaylistType {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
match s {
"EVENT" => Ok(PlaylistType::Event),
"VOD" => Ok(PlaylistType::Vod),
_ => track_panic!(ErrorKind::InvalidInput, "Unknown playlist type: {:?}", s),
}
}
}

View file

@ -0,0 +1,47 @@
use crate::{Error, ErrorKind, Result};
use std::fmt;
use std::str::{self, FromStr};
/// [7. Protocol Version Compatibility]
///
/// [7. Protocol Version Compatibility]: https://tools.ietf.org/html/rfc8216#section-7
#[allow(missing_docs)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ProtocolVersion {
V1,
V2,
V3,
V4,
V5,
V6,
V7,
}
impl fmt::Display for ProtocolVersion {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let n = match *self {
ProtocolVersion::V1 => 1,
ProtocolVersion::V2 => 2,
ProtocolVersion::V3 => 3,
ProtocolVersion::V4 => 4,
ProtocolVersion::V5 => 5,
ProtocolVersion::V6 => 6,
ProtocolVersion::V7 => 7,
};
write!(f, "{}", n)
}
}
impl FromStr for ProtocolVersion {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
Ok(match s {
"1" => ProtocolVersion::V1,
"2" => ProtocolVersion::V2,
"3" => ProtocolVersion::V3,
"4" => ProtocolVersion::V4,
"5" => ProtocolVersion::V5,
"6" => ProtocolVersion::V6,
"7" => ProtocolVersion::V7,
_ => track_panic!(ErrorKind::InvalidInput, "Unknown protocol version: {:?}", s),
})
}
}

View file

@ -0,0 +1,62 @@
use crate::{Error, ErrorKind, Result};
use std::fmt;
use std::ops::Deref;
use std::str::{self, FromStr};
/// Quoted string.
///
/// See: [4.2. Attribute Lists]
///
/// [4.2. Attribute Lists]: https://tools.ietf.org/html/rfc8216#section-4.2
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct QuotedString(String);
impl QuotedString {
/// Makes a new `QuotedString` instance.
///
/// # Errors
///
/// If the given string contains any control characters or double-quote character,
/// this function will return an error which has the kind `ErrorKind::InvalidInput`.
pub fn new<T: Into<String>>(s: T) -> Result<Self> {
let s = s.into();
track_assert!(
!s.chars().any(|c| c.is_control() || c == '"'),
ErrorKind::InvalidInput
);
Ok(QuotedString(s))
}
}
impl Deref for QuotedString {
type Target = str;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl AsRef<str> for QuotedString {
fn as_ref(&self) -> &str {
&self.0
}
}
impl fmt::Display for QuotedString {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self.0)
}
}
impl FromStr for QuotedString {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
let len = s.len();
let bytes = s.as_bytes();
track_assert!(len >= 2, ErrorKind::InvalidInput);
track_assert_eq!(bytes[0], b'"', ErrorKind::InvalidInput);
track_assert_eq!(bytes[len - 1], b'"', ErrorKind::InvalidInput);
let s = unsafe { str::from_utf8_unchecked(&bytes[1..len - 1]) };
track!(QuotedString::new(s))
}
}

13
src/types/session_data.rs Normal file
View file

@ -0,0 +1,13 @@
use crate::types::QuotedString;
/// Session data.
///
/// See: [4.3.4.4. EXT-X-SESSION-DATA]
///
/// [4.3.4.4. EXT-X-SESSION-DATA]: https://tools.ietf.org/html/rfc8216#section-4.3.4.4
#[allow(missing_docs)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum SessionData {
Value(QuotedString),
Uri(QuotedString),
}

View file

@ -0,0 +1,56 @@
use crate::{Error, ErrorKind, Result};
use std::fmt;
use std::str::{self, FromStr};
use trackable::error::ErrorKindExt;
/// Signed decimal floating-point number.
///
/// See: [4.2. Attribute Lists]
///
/// [4.2. Attribute Lists]: https://tools.ietf.org/html/rfc8216#section-4.2
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub struct SignedDecimalFloatingPoint(f64);
impl SignedDecimalFloatingPoint {
/// Makes a new `SignedDecimalFloatingPoint` instance.
///
/// # Errors
///
/// The given value must be finite,
/// otherwise this function will return an error that has the kind `ErrorKind::InvalidInput`.
pub fn new(n: f64) -> Result<Self> {
track_assert!(n.is_finite(), ErrorKind::InvalidInput);
Ok(SignedDecimalFloatingPoint(n))
}
/// Converts `DecimalFloatingPoint` to `f64`.
pub fn as_f64(self) -> f64 {
self.0
}
}
impl From<i32> for SignedDecimalFloatingPoint {
fn from(f: i32) -> Self {
SignedDecimalFloatingPoint(f64::from(f))
}
}
impl Eq for SignedDecimalFloatingPoint {}
impl fmt::Display for SignedDecimalFloatingPoint {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
impl FromStr for SignedDecimalFloatingPoint {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
track_assert!(
s.chars().all(|c| c.is_digit(10) || c == '.' || c == '-'),
ErrorKind::InvalidInput
);
let n = track!(s.parse().map_err(|e| ErrorKind::InvalidInput.cause(e)))?;
Ok(SignedDecimalFloatingPoint(n))
}
}

View file

@ -0,0 +1,55 @@
use crate::{ErrorKind, Result};
use std::fmt;
use std::ops::Deref;
/// String that represents a single line in a playlist file.
///
/// See: [4.1. Definition of a Playlist]
///
/// [4.1. Definition of a Playlist]: https://tools.ietf.org/html/rfc8216#section-4.1
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SingleLineString(String);
impl SingleLineString {
/// Makes a new `SingleLineString` instance.
///
/// # Errors
///
/// If the given string contains any control characters,
/// this function will return an error which has the kind `ErrorKind::InvalidInput`.
pub fn new<T: Into<String>>(s: T) -> Result<Self> {
let s = s.into();
track_assert!(!s.chars().any(|c| c.is_control()), ErrorKind::InvalidInput);
Ok(SingleLineString(s))
}
}
impl Deref for SingleLineString {
type Target = str;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl AsRef<str> for SingleLineString {
fn as_ref(&self) -> &str {
&self.0
}
}
impl fmt::Display for SingleLineString {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn single_line_string() {
assert!(SingleLineString::new("foo").is_ok());
assert!(SingleLineString::new("b\rar").is_err());
}
}