1
0
Fork 0
mirror of https://github.com/sile/hls_m3u8.git synced 2024-06-02 21:49:35 +00:00
hls_m3u8/src/attribute.rs

145 lines
4.8 KiB
Rust
Raw Normal View History

use core::iter::FusedIterator;
2019-09-14 09:31:16 +00:00
2020-02-02 12:38:11 +00:00
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, PartialOrd, Ord, Hash)]
pub(crate) struct AttributePairs<'a> {
string: &'a str,
index: usize,
2019-09-14 09:31:16 +00:00
}
2018-02-11 06:10:52 +00:00
impl<'a> AttributePairs<'a> {
pub const fn new(string: &'a str) -> Self { Self { string, index: 0 } }
2019-09-14 09:31:16 +00:00
}
impl<'a> Iterator for AttributePairs<'a> {
type Item = (&'a str, &'a str);
fn next(&mut self) -> Option<Self::Item> {
// return `None`, if there are no more chars
self.string.as_bytes().get(self.index + 1)?;
let key = {
// the position in the string:
let start = self.index;
// the key ends at an `=`:
let end = self
.string
.bytes()
.skip(self.index)
.position(|i| i == b'=')?
+ start;
// advance the index to the 2nd char after the end of the key
// (this will skip the `=`)
self.index = end + 1;
core::str::from_utf8(&self.string.as_bytes()[start..end]).unwrap()
};
let value = {
let start = self.index;
let mut end = 0;
// find the end of the value by searching for `,`.
// it should ignore `,` that are inside double quotes.
let mut inside_quotes = false;
while let Some(item) = self.string.as_bytes().get(start + end) {
end += 1;
if *item == b'"' {
inside_quotes = !inside_quotes;
} else if *item == b',' && !inside_quotes {
self.index += 1;
end -= 1;
break;
}
2018-02-18 14:36:52 +00:00
}
2019-09-14 09:31:16 +00:00
self.index += end;
end += start;
2019-09-14 09:31:16 +00:00
core::str::from_utf8(&self.string.as_bytes()[start..end]).unwrap()
};
2019-09-15 14:45:43 +00:00
Some((key.trim(), value.trim()))
2018-02-11 06:10:52 +00:00
}
2019-09-13 14:06:52 +00:00
fn size_hint(&self) -> (usize, Option<usize>) {
let mut remaining = 0;
2018-02-11 06:10:52 +00:00
// each `=` in the remaining str is an iteration
// this also ignores `=` inside quotes!
let mut inside_quotes = false;
for c in self.string.as_bytes().iter().skip(self.index) {
if *c == b'=' && !inside_quotes {
remaining += 1;
} else if *c == b'"' {
2019-10-05 14:08:03 +00:00
inside_quotes = !inside_quotes;
2019-09-14 09:31:16 +00:00
}
}
(remaining, Some(remaining))
}
2018-02-11 06:10:52 +00:00
}
2018-02-18 14:36:52 +00:00
impl<'a> ExactSizeIterator for AttributePairs<'a> {}
impl<'a> FusedIterator for AttributePairs<'a> {}
2018-02-18 14:36:52 +00:00
#[cfg(test)]
mod test {
use super::*;
use pretty_assertions::assert_eq;
2018-02-18 14:36:52 +00:00
#[test]
fn test_attributes() {
let mut attributes = AttributePairs::new("KEY=VALUE,PAIR=YES");
assert_eq!((2, Some(2)), attributes.size_hint());
assert_eq!(Some(("KEY", "VALUE")), attributes.next());
assert_eq!((1, Some(1)), attributes.size_hint());
assert_eq!(Some(("PAIR", "YES")), attributes.next());
assert_eq!((0, Some(0)), attributes.size_hint());
assert_eq!(None, attributes.next());
let mut attributes = AttributePairs::new("garbage");
assert_eq!((0, Some(0)), attributes.size_hint());
assert_eq!(None, attributes.next());
let mut attributes = AttributePairs::new("KEY=,=VALUE,=,");
assert_eq!((3, Some(3)), attributes.size_hint());
assert_eq!(Some(("KEY", "")), attributes.next());
assert_eq!((2, Some(2)), attributes.size_hint());
assert_eq!(Some(("", "VALUE")), attributes.next());
assert_eq!((1, Some(1)), attributes.size_hint());
assert_eq!(Some(("", "")), attributes.next());
assert_eq!((0, Some(0)), attributes.size_hint());
assert_eq!(None, attributes.next());
// test quotes:
let mut attributes = AttributePairs::new("KEY=\"VALUE,\",");
assert_eq!((1, Some(1)), attributes.size_hint());
assert_eq!(Some(("KEY", "\"VALUE,\"")), attributes.next());
assert_eq!((0, Some(0)), attributes.size_hint());
assert_eq!(None, attributes.next());
// test with chars, that are larger, than 1 byte
let mut attributes = AttributePairs::new(
"LANGUAGE=\"fre\",\
NAME=\"Français\",\
AUTOSELECT=YES",
);
2019-09-14 09:31:16 +00:00
assert_eq!(Some(("LANGUAGE", "\"fre\"")), attributes.next());
assert_eq!(Some(("NAME", "\"Français\"")), attributes.next());
assert_eq!(Some(("AUTOSELECT", "YES")), attributes.next());
2018-02-18 14:36:52 +00:00
}
2019-09-22 18:33:40 +00:00
#[test]
fn test_parser() {
let mut pairs = AttributePairs::new("FOO=BAR,BAR=\"baz,qux\",ABC=12.3");
2019-09-22 18:33:40 +00:00
assert_eq!(pairs.next(), Some(("FOO", "BAR")));
assert_eq!(pairs.next(), Some(("BAR", "\"baz,qux\"")));
assert_eq!(pairs.next(), Some(("ABC", "12.3")));
assert_eq!(pairs.next(), None);
2019-09-22 18:33:40 +00:00
}
2018-02-18 14:36:52 +00:00
}