1
0
Fork 0
mirror of https://github.com/sile/hls_m3u8.git synced 2024-06-02 08:09:41 +00:00
hls_m3u8/src/utils.rs
2020-02-10 13:21:48 +01:00

153 lines
4.4 KiB
Rust

use crate::Error;
use core::iter;
macro_rules! required_version {
( $( $tag:expr ),* ) => {
::core::iter::empty()
$(
.chain(::core::iter::once($tag.required_version()))
)*
.max()
.unwrap_or_default()
}
}
pub(crate) fn parse_iv_from_str(input: &str) -> crate::Result<[u8; 16]> {
if !(input.starts_with("0x") || input.starts_with("0X")) {
return Err(Error::invalid_input());
}
if input.len() - 2 != 32 {
return Err(Error::invalid_input());
}
let mut result = [0; 16];
hex::decode_to_slice(&input.as_bytes()[2..], &mut result).map_err(Error::hex)?;
Ok(result)
}
pub(crate) fn parse_yes_or_no<T: AsRef<str>>(s: T) -> crate::Result<bool> {
match s.as_ref() {
"YES" => Ok(true),
"NO" => Ok(false),
_ => Err(Error::invalid_input()),
}
}
/// According to the documentation the following characters are forbidden
/// inside a quoted string:
/// - carriage return (`\r`)
/// - new line (`\n`)
/// - double quotes (`"`)
///
/// Therefore it is safe to simply remove any occurence of those characters.
/// [rfc8216#section-4.2](https://tools.ietf.org/html/rfc8216#section-4.2)
pub(crate) fn unquote<T: AsRef<str>>(value: T) -> String {
value
.as_ref()
.chars()
.filter(|c| *c != '"' && *c != '\n' && *c != '\r')
.collect()
}
/// Puts a string inside quotes.
#[allow(clippy::needless_pass_by_value)]
pub(crate) fn quote<T: ToString>(value: T) -> String {
// the replace is for the case, that quote is called on an already quoted
// string, which could cause problems!
iter::once('"')
.chain(value.to_string().chars().filter(|c| *c != '"'))
.chain(iter::once('"'))
.collect()
}
/// Checks, if the given tag is at the start of the input. If this is the case,
/// it will remove it and return the rest of the input.
///
/// # Error
///
/// This function will return `Error::MissingTag`, if the input doesn't start
/// with the tag, that has been passed to this function.
pub(crate) fn tag<T>(input: &str, tag: T) -> crate::Result<&str>
where
T: AsRef<str>,
{
if !input.trim().starts_with(tag.as_ref()) {
return Err(Error::missing_tag(tag.as_ref(), input));
}
Ok(input.trim().split_at(tag.as_ref().len()).1)
}
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
#[test]
fn test_parse_yes_or_no() {
assert!(parse_yes_or_no("YES").unwrap());
assert!(!parse_yes_or_no("NO").unwrap());
assert!(parse_yes_or_no("garbage").is_err());
}
#[test]
fn test_unquote() {
assert_eq!(unquote("\"TestValue\""), "TestValue".to_string());
assert_eq!(unquote("\"TestValue\n\""), "TestValue".to_string());
assert_eq!(unquote("\"TestValue\n\r\""), "TestValue".to_string());
}
#[test]
fn test_quote() {
assert_eq!(quote("value"), "\"value\"".to_string());
assert_eq!(quote("\"value\""), "\"value\"".to_string());
}
#[test]
fn test_tag() {
let input = "HelloMyFriendThisIsASampleString";
let input = tag(input, "Hello").unwrap();
assert_eq!(input, "MyFriendThisIsASampleString");
let input = tag(input, "My").unwrap();
assert_eq!(input, "FriendThisIsASampleString");
let input = tag(input, "FriendThisIs").unwrap();
assert_eq!(input, "ASampleString");
let input = tag(input, "A").unwrap();
assert_eq!(input, "SampleString");
assert!(tag(input, "B").is_err());
assert_eq!(
tag(
concat!(
"\n #EXTM3U\n",
" #EXT-X-TARGETDURATION:5220\n",
" #EXTINF:0,\n",
" http://media.example.com/entire1.ts\n",
" #EXTINF:5220,\n",
" http://media.example.com/entire2.ts\n",
" #EXT-X-ENDLIST"
),
"#EXTM3U"
)
.unwrap(),
concat!(
"\n",
" #EXT-X-TARGETDURATION:5220\n",
" #EXTINF:0,\n",
" http://media.example.com/entire1.ts\n",
" #EXTINF:5220,\n",
" http://media.example.com/entire2.ts\n",
" #EXT-X-ENDLIST"
)
);
}
}