1
0
Fork 0
mirror of https://github.com/sile/hls_m3u8.git synced 2024-11-22 15:21:01 +00:00

Rewrote AttributePairs

This commit is contained in:
Luro02 2019-09-14 11:31:16 +02:00
parent c8f3df1228
commit 3721106795
11 changed files with 168 additions and 129 deletions

View file

@ -1,94 +1,136 @@
use crate::{Error, Result}; use std::collections::HashMap;
use std::collections::HashSet; use std::ops::{Deref, DerefMut};
use std::str; use std::str::FromStr;
#[derive(Debug)] use crate::Error;
pub struct AttributePairs<'a> {
input: &'a str,
visited_keys: HashSet<&'a str>,
}
impl<'a> AttributePairs<'a> {
pub fn parse(input: &'a str) -> Self {
AttributePairs {
input,
visited_keys: HashSet::new(),
}
}
fn parse_name(&mut self) -> Result<&'a str> { #[derive(Clone, Debug, Default, Eq, PartialEq)]
for i in 0..self.input.len() { pub struct AttributePairs(HashMap<String, String>);
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'-' => {}
_ => {
return Err(Error::invalid_attribute(self.input.to_string()));
}
}
}
Err(Error::missing_value(self.input.to_string())) impl AttributePairs {
} pub fn new() -> Self {
Self::default()
fn parse_raw_value(&mut self) -> &'a str {
let mut in_quote = false;
let mut value_end = self.input.len();
let mut next = self.input.len();
for (i, c) in self.input.bytes().enumerate() {
match c {
b'"' => {
in_quote = !in_quote;
}
b',' if !in_quote => {
value_end = i;
next = i + 1;
break;
}
_ => {}
}
}
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> { impl Deref for AttributePairs {
if self.input.is_empty() { type Target = HashMap<String, String>;
return None;
}
let result = || -> Result<(&'a str, &'a str)> { fn deref(&self) -> &Self::Target {
let key = self.parse_name()?; &self.0
self.visited_keys.insert(key);
let value = self.parse_raw_value();
Ok((key, value))
}();
Some(result)
} }
} }
impl DerefMut for AttributePairs {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl IntoIterator for AttributePairs {
type Item = (String, String);
type IntoIter = ::std::collections::hash_map::IntoIter<String, String>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl<'a> IntoIterator for &'a AttributePairs {
type Item = (&'a String, &'a String);
type IntoIter = ::std::collections::hash_map::Iter<'a, String, String>;
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
impl FromStr for AttributePairs {
type Err = Error;
fn from_str(input: &str) -> Result<Self, Self::Err> {
let mut result = AttributePairs::new();
for line in split(input) {
let pair = line.trim().split("=").collect::<Vec<_>>();
if pair.len() < 2 {
return Err(Error::invalid_input());
}
let key = pair[0].to_uppercase();
let value = pair[1].to_string();
result.insert(key.to_string(), value.to_string());
}
Ok(result)
}
}
fn split(value: &str) -> Vec<String> {
let mut result = vec![];
let mut inside_quotes = false;
let mut temp_string = String::new();
for c in value.chars() {
match c {
'"' => {
if inside_quotes {
inside_quotes = false;
} else {
inside_quotes = true;
}
temp_string.push(c);
}
',' => {
if !inside_quotes {
result.push(temp_string);
temp_string = String::new();
} else {
temp_string.push(c);
}
}
_ => {
temp_string.push(c);
}
}
}
result.push(temp_string);
result
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
#[test] #[test]
fn it_works() { fn test_parser() {
let mut pairs = AttributePairs::parse("FOO=BAR,BAR=\"baz,qux\",ABC=12.3"); let pairs = ("FOO=BAR,BAR=\"baz,qux\",ABC=12.3")
assert_eq!(pairs.next().map(|x| x.ok()), Some(Some(("FOO", "BAR")))); .parse::<AttributePairs>()
assert_eq!( .unwrap();
pairs.next().map(|x| x.ok()),
Some(Some(("BAR", "\"baz,qux\""))) let mut iterator = pairs.iter();
); assert!(iterator.any(|(k, v)| k == "FOO" && "BAR" == v));
assert_eq!(pairs.next().map(|x| x.ok()), Some(Some(("ABC", "12.3"))));
assert_eq!(pairs.next().map(|x| x.ok()), None) let mut iterator = pairs.iter();
assert!(iterator.any(|(k, v)| k == "BAR" && v == "\"baz,qux\""));
let mut iterator = pairs.iter();
assert!(iterator.any(|(k, v)| k == "ABC" && v == "12.3"));
}
#[test]
fn test_iterator() {
let mut attrs = AttributePairs::new();
attrs.insert("key_01".to_string(), "value_01".to_string());
attrs.insert("key_02".to_string(), "value_02".to_string());
let mut iterator = attrs.iter();
assert!(iterator.any(|(k, v)| k == "key_01" && v == "value_01"));
let mut iterator = attrs.iter();
assert!(iterator.any(|(k, v)| k == "key_02" && v == "value_02"));
} }
} }

View file

@ -46,6 +46,9 @@ pub enum ErrorKind {
#[fail(display = "Unknown Protocol version: {:?}", _0)] #[fail(display = "Unknown Protocol version: {:?}", _0)]
UnknownProtocolVersion(String), UnknownProtocolVersion(String),
#[fail(display = "IoError: {}", _0)]
Io(String),
/// Hints that destructuring should not be exhaustive. /// Hints that destructuring should not be exhaustive.
/// ///
/// This enum may grow additional variants, so this makes sure clients /// This enum may grow additional variants, so this makes sure clients
@ -164,6 +167,10 @@ impl Error {
pub(crate) fn unknown_protocol_version<T: ToString>(value: T) -> Self { pub(crate) fn unknown_protocol_version<T: ToString>(value: T) -> Self {
Self::from(ErrorKind::UnknownProtocolVersion(value.to_string())) Self::from(ErrorKind::UnknownProtocolVersion(value.to_string()))
} }
pub(crate) fn io<T: ToString>(value: T) -> Self {
Self::from(ErrorKind::Io(value.to_string()))
}
} }
impl From<std::num::ParseIntError> for Error { impl From<std::num::ParseIntError> for Error {
@ -177,3 +184,9 @@ impl From<std::num::ParseFloatError> for Error {
Error::parse_float_error(value) Error::parse_float_error(value)
} }
} }
impl From<std::io::Error> for Error {
fn from(value: ::std::io::Error) -> Self {
Error::io(value)
}
}

View file

@ -94,10 +94,8 @@ impl FromStr for ExtXIFrameStreamInf {
let mut hdcp_level = None; let mut hdcp_level = None;
let mut video = None; let mut video = None;
let attrs = AttributePairs::parse(input); for (key, value) in input.parse::<AttributePairs>()? {
for attr in attrs { match key.as_str() {
let (key, value) = attr?;
match key {
"URI" => uri = Some(unquote(value)), "URI" => uri = Some(unquote(value)),
"BANDWIDTH" => bandwidth = Some(parse_u64(value)?), "BANDWIDTH" => bandwidth = Some(parse_u64(value)?),
"AVERAGE-BANDWIDTH" => average_bandwidth = Some(parse_u64(value)?), "AVERAGE-BANDWIDTH" => average_bandwidth = Some(parse_u64(value)?),

View file

@ -331,10 +331,9 @@ impl FromStr for ExtXMedia {
let input = tag(input, Self::PREFIX)?; let input = tag(input, Self::PREFIX)?;
let mut builder = ExtXMediaBuilder::new(); let mut builder = ExtXMediaBuilder::new();
let attrs = AttributePairs::parse(input);
for attr in attrs { for (key, value) in input.parse::<AttributePairs>()? {
let (key, value) = attr?; match key.as_str() {
match key {
"TYPE" => { "TYPE" => {
builder.media_type(value.parse()?); builder.media_type(value.parse()?);
} }

View file

@ -77,10 +77,8 @@ impl FromStr for ExtXSessionData {
let mut uri = None; let mut uri = None;
let mut language = None; let mut language = None;
let attrs = AttributePairs::parse(input); for (key, value) in input.parse::<AttributePairs>()? {
for attr in attrs { match key.as_str() {
let (key, value) = attr?;
match key {
"DATA-ID" => data_id = Some(unquote(value)), "DATA-ID" => data_id = Some(unquote(value)),
"VALUE" => session_value = Some(unquote(value)), "VALUE" => session_value = Some(unquote(value)),
"URI" => uri = Some(unquote(value)), "URI" => uri = Some(unquote(value)),
@ -92,7 +90,7 @@ impl FromStr for ExtXSessionData {
} }
} }
let data_id = data_id.ok_or(Error::missing_value("DATA-ID"))?; let data_id = data_id.ok_or(Error::missing_value("EXT-X-DATA-ID"))?;
let data = { let data = {
if let Some(value) = session_value { if let Some(value) = session_value {
if uri.is_some() { if uri.is_some() {

View file

@ -149,10 +149,10 @@ impl FromStr for ExtXStreamInf {
fn from_str(input: &str) -> Result<Self, Self::Err> { fn from_str(input: &str) -> Result<Self, Self::Err> {
let mut lines = input.lines(); let mut lines = input.lines();
let first_line = lines.next().ok_or(Error::invalid_input())?; // TODO! let first_line = lines.next().ok_or(Error::missing_value("first_line"))?;
let second_line = lines.next().ok_or(Error::invalid_input())?; // TODO! let second_line = lines.next().ok_or(Error::missing_value("second_line"))?;
tag(first_line, Self::PREFIX)?; let first_line = tag(first_line, Self::PREFIX)?;
let uri = SingleLineString::new(second_line)?; let uri = SingleLineString::new(second_line)?;
@ -167,10 +167,8 @@ impl FromStr for ExtXStreamInf {
let mut subtitles = None; let mut subtitles = None;
let mut closed_captions = None; let mut closed_captions = None;
let attrs = AttributePairs::parse(first_line.split_at(Self::PREFIX.len()).1); for (key, value) in first_line.parse::<AttributePairs>()? {
for attr in attrs { match key.as_str() {
let (key, value) = (attr)?;
match key {
"BANDWIDTH" => bandwidth = Some((parse_u64(value))?), "BANDWIDTH" => bandwidth = Some((parse_u64(value))?),
"AVERAGE-BANDWIDTH" => average_bandwidth = Some((parse_u64(value))?), "AVERAGE-BANDWIDTH" => average_bandwidth = Some((parse_u64(value))?),
"CODECS" => codecs = Some(unquote(value)), "CODECS" => codecs = Some(unquote(value)),
@ -188,7 +186,7 @@ impl FromStr for ExtXStreamInf {
} }
} }
let bandwidth = bandwidth.ok_or(Error::missing_value("BANDWIDTH"))?; let bandwidth = bandwidth.ok_or(Error::missing_value("EXT-X-BANDWIDTH"))?;
Ok(ExtXStreamInf { Ok(ExtXStreamInf {
uri, uri,

View file

@ -96,11 +96,9 @@ impl FromStr for ExtXDateRange {
let mut end_on_next = false; let mut end_on_next = false;
let mut client_attributes = BTreeMap::new(); let mut client_attributes = BTreeMap::new();
let attrs = AttributePairs::parse(input);
for attr in attrs { for (key, value) in input.parse::<AttributePairs>()? {
let (key, value) = attr?; match key.as_str() {
match key {
"ID" => id = Some(unquote(value)), "ID" => id = Some(unquote(value)),
"CLASS" => class = Some(unquote(value)), "CLASS" => class = Some(unquote(value)),
"START-DATE" => start_date = Some(unquote(value)), "START-DATE" => start_date = Some(unquote(value)),

View file

@ -10,33 +10,31 @@ use crate::Error;
/// ///
/// [4.3.2.4. EXT-X-KEY]: https://tools.ietf.org/html/rfc8216#section-4.3.2.4 /// [4.3.2.4. EXT-X-KEY]: https://tools.ietf.org/html/rfc8216#section-4.3.2.4
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ExtXKey { pub struct ExtXKey(Option<DecryptionKey>);
key: Option<DecryptionKey>,
}
impl ExtXKey { impl ExtXKey {
pub(crate) const PREFIX: &'static str = "#EXT-X-KEY:"; pub(crate) const PREFIX: &'static str = "#EXT-X-KEY:";
/// Makes a new `ExtXKey` tag. /// Makes a new `ExtXKey` tag.
pub const fn new(key: DecryptionKey) -> Self { pub const fn new(key: DecryptionKey) -> Self {
ExtXKey { key: Some(key) } Self(Some(key))
} }
/// Makes a new `ExtXKey` tag without a decryption key. /// Makes a new `ExtXKey` tag without a decryption key.
/// ///
/// This tag has the `METHDO=NONE` attribute. /// This tag has the `METHDO=NONE` attribute.
pub const fn new_without_key() -> Self { pub const fn new_without_key() -> Self {
ExtXKey { key: None } Self(None)
} }
/// Returns the decryption key for the following media segments and media initialization sections. /// Returns the decryption key for the following media segments and media initialization sections.
pub fn key(&self) -> Option<&DecryptionKey> { pub fn key(&self) -> Option<&DecryptionKey> {
self.key.as_ref() self.0.as_ref()
} }
/// Returns the protocol compatibility version that this tag requires. /// Returns the protocol compatibility version that this tag requires.
pub fn requires_version(&self) -> ProtocolVersion { pub fn requires_version(&self) -> ProtocolVersion {
self.key self.0
.as_ref() .as_ref()
.map_or(ProtocolVersion::V1, |k| k.requires_version()) .map_or(ProtocolVersion::V1, |k| k.requires_version())
} }
@ -45,7 +43,7 @@ impl ExtXKey {
impl fmt::Display for ExtXKey { impl fmt::Display for ExtXKey {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", Self::PREFIX)?; write!(f, "{}", Self::PREFIX)?;
if let Some(ref key) = self.key { if let Some(ref key) = self.0 {
write!(f, "{}", key)?; write!(f, "{}", key)?;
} else { } else {
write!(f, "METHOD=NONE")?; write!(f, "METHOD=NONE")?;
@ -60,17 +58,18 @@ impl FromStr for ExtXKey {
fn from_str(input: &str) -> Result<Self, Self::Err> { fn from_str(input: &str) -> Result<Self, Self::Err> {
let input = tag(input, Self::PREFIX)?; let input = tag(input, Self::PREFIX)?;
if AttributePairs::parse(input).any(|a| a.as_ref().ok() == Some(&("METHOD", "NONE"))) { let pairs = input.parse::<AttributePairs>()?;
for attr in AttributePairs::parse(input) {
let (key, _) = attr?; if pairs.iter().any(|(k, v)| k == "METHOD" && v == "NONE") {
for (key, _) in pairs {
if key == "URI" || key == "IV" || key == "KEYFORMAT" || key == "KEYFORMATVERSIONS" { if key == "URI" || key == "IV" || key == "KEYFORMAT" || key == "KEYFORMATVERSIONS" {
return Err(Error::invalid_input()); return Err(Error::invalid_input());
} }
} }
Ok(ExtXKey { key: None })
Ok(Self(None))
} else { } else {
let key = input.parse()?; Ok(Self(Some(input.parse()?)))
Ok(ExtXKey { key: Some(key) })
} }
} }
} }

View file

@ -69,10 +69,9 @@ impl FromStr for ExtXMap {
let mut uri = None; let mut uri = None;
let mut range = None; let mut range = None;
let attrs = AttributePairs::parse(input);
for attr in attrs { for (key, value) in input.parse::<AttributePairs>()? {
let (key, value) = (attr)?; match key.as_str() {
match key {
"URI" => uri = Some(unquote(value)), "URI" => uri = Some(unquote(value)),
"BYTERANGE" => { "BYTERANGE" => {
range = Some((unquote(value).parse())?); range = Some((unquote(value).parse())?);

View file

@ -71,11 +71,8 @@ impl FromStr for ExtXStart {
let mut time_offset = None; let mut time_offset = None;
let mut precise = false; let mut precise = false;
let attrs = AttributePairs::parse(input); for (key, value) in input.parse::<AttributePairs>()? {
match key.as_str() {
for attr in attrs {
let (key, value) = (attr)?;
match key {
"TIME-OFFSET" => time_offset = Some((value.parse())?), "TIME-OFFSET" => time_offset = Some((value.parse())?),
"PRECISE" => precise = (parse_yes_or_no(value))?, "PRECISE" => precise = (parse_yes_or_no(value))?,
_ => { _ => {

View file

@ -60,10 +60,8 @@ impl FromStr for DecryptionKey {
let mut key_format = None; let mut key_format = None;
let mut key_format_versions = None; let mut key_format_versions = None;
let attrs = AttributePairs::parse(input); for (key, value) in input.parse::<AttributePairs>()? {
for attr in attrs { match key.as_str() {
let (key, value) = (attr)?;
match key {
"METHOD" => method = Some((value.parse())?), "METHOD" => method = Some((value.parse())?),
"URI" => uri = Some(unquote(value)), "URI" => uri = Some(unquote(value)),
"IV" => iv = Some((value.parse())?), "IV" => iv = Some((value.parse())?),