mirror of
https://github.com/sile/hls_m3u8.git
synced 2024-09-25 21:19:59 +00:00
241 lines
7.2 KiB
Rust
241 lines
7.2 KiB
Rust
use std::fmt;
|
|
use std::str::{self, FromStr};
|
|
use std::time::Duration;
|
|
use std::u8;
|
|
use trackable::error::ErrorKindExt;
|
|
|
|
use {Error, ErrorKind, Result};
|
|
|
|
#[derive(Debug)]
|
|
pub struct AttributePairs<'a> {
|
|
input: &'a str,
|
|
}
|
|
impl<'a> AttributePairs<'a> {
|
|
pub fn parse(input: &'a str) -> Self {
|
|
AttributePairs { input }
|
|
}
|
|
|
|
fn parse_name(&mut self) -> Result<&'a str> {
|
|
for i in 0..self.input.len() {
|
|
match self.input.as_bytes()[i] {
|
|
b'=' => {
|
|
let (key, _) = self.input.split_at(i);
|
|
let (_, rest) = self.input.split_at(i + 1);
|
|
self.input = rest;
|
|
return Ok(key);
|
|
}
|
|
b'A'...b'Z' | b'0'...b'9' | b'-' => {}
|
|
_ => track_panic!(
|
|
ErrorKind::InvalidInput,
|
|
"Malformed attribute name: {:?}",
|
|
self.input
|
|
),
|
|
}
|
|
}
|
|
track_panic!(
|
|
ErrorKind::InvalidInput,
|
|
"No attribute value: {:?}",
|
|
self.input
|
|
);
|
|
}
|
|
|
|
fn parse_raw_value(&mut self) -> &'a str {
|
|
let (value_end, next) = if let Some(i) = self.input.bytes().position(|c| c == b',') {
|
|
(i, i + 1)
|
|
} else {
|
|
(self.input.len(), self.input.len())
|
|
};
|
|
let (value, _) = self.input.split_at(value_end);
|
|
let (_, rest) = self.input.split_at(next);
|
|
self.input = rest;
|
|
value
|
|
}
|
|
}
|
|
impl<'a> Iterator for AttributePairs<'a> {
|
|
type Item = Result<(&'a str, &'a str)>;
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
if self.input.is_empty() {
|
|
return None;
|
|
}
|
|
|
|
let result = || -> Result<(&'a str, &'a str)> {
|
|
// TODO: check key duplications
|
|
let key = track!(self.parse_name())?;
|
|
let value = self.parse_raw_value();
|
|
Ok((key, value))
|
|
}();
|
|
Some(result)
|
|
}
|
|
}
|
|
|
|
// TODO: export and rename
|
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
pub struct QuotedString(String);
|
|
impl QuotedString {
|
|
pub fn new<T: Into<String>>(s: T) -> Result<Self> {
|
|
// TODO: validate
|
|
Ok(QuotedString(s.into()))
|
|
}
|
|
pub fn as_str(&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_assert!(
|
|
s.chars().all(|c| c != '\r' && c != '\n' && c != '"'),
|
|
ErrorKind::InvalidInput,
|
|
"{:?}",
|
|
s
|
|
);
|
|
Ok(QuotedString(s.to_owned()))
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
pub struct HexadecimalSequence(Vec<u8>);
|
|
impl HexadecimalSequence {
|
|
pub fn new<T: Into<Vec<u8>>>(v: T) -> Self {
|
|
HexadecimalSequence(v.into())
|
|
}
|
|
}
|
|
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))
|
|
}
|
|
}
|
|
|
|
// TODO: delete
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
pub struct DecimalInteger(pub u64);
|
|
impl fmt::Display for DecimalInteger {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
write!(f, "{}", self.0)
|
|
}
|
|
}
|
|
impl FromStr for DecimalInteger {
|
|
type Err = Error;
|
|
fn from_str(s: &str) -> Result<Self> {
|
|
let n = track!(s.parse().map_err(|e| ErrorKind::InvalidInput.cause(e)))?;
|
|
Ok(DecimalInteger(n))
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
|
|
pub struct DecimalFloatingPoint(f64);
|
|
impl DecimalFloatingPoint {
|
|
pub 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 fn from_duration(duration: Duration) -> Self {
|
|
let n = (duration.as_secs() as f64) + (duration.subsec_nanos() as f64 / 1_000_000_000.0);
|
|
DecimalFloatingPoint(n)
|
|
}
|
|
pub fn as_f64(&self) -> f64 {
|
|
self.0
|
|
}
|
|
}
|
|
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| match c {
|
|
'0'...'9' | '.' => true,
|
|
_ => false,
|
|
}),
|
|
ErrorKind::InvalidInput
|
|
);
|
|
let n = track!(s.parse().map_err(|e| ErrorKind::InvalidInput.cause(e)))?;
|
|
Ok(DecimalFloatingPoint(n))
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
|
|
pub struct SignedDecimalFloatingPoint(pub f64);
|
|
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| match c {
|
|
'0'...'9' | '.' | '-' => true,
|
|
_ => false,
|
|
}),
|
|
ErrorKind::InvalidInput
|
|
);
|
|
let n = track!(s.parse().map_err(|e| ErrorKind::InvalidInput.cause(e)))?;
|
|
Ok(SignedDecimalFloatingPoint(n))
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
pub struct DecimalResolution {
|
|
pub width: usize,
|
|
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)))?,
|
|
})
|
|
}
|
|
}
|