Port to nom 7

Fixes https://github.com/rutgersc/m3u8-rs/issues/35
This commit is contained in:
Sebastian Dröge 2021-11-17 14:10:18 +02:00
parent f104d431d9
commit 81398f86cd
3 changed files with 436 additions and 283 deletions

View file

@ -9,7 +9,7 @@ documentation = "https://rutgersc.github.io/doc/m3u8_rs/index.html"
license = "MIT" license = "MIT"
[dependencies] [dependencies]
nom = { version = "5.1.0", optional = true } nom = { version = "7", optional = true }
[features] [features]
default = ["parser"] default = ["parser"]

View file

@ -83,14 +83,15 @@ extern crate nom;
pub mod playlist; pub mod playlist;
use self::nom::character::complete::{digit1, multispace0, space0}; use self::nom::bytes::complete::{tag, take, is_not, is_a, take_while1, take_until};
use self::nom::character::complete::{line_ending, not_line_ending}; use self::nom::character::is_digit;
use self::nom::combinator::map; use self::nom::character::complete::{digit1, multispace0, space0, line_ending, not_line_ending, char, none_of};
use self::nom::sequence::{delimited, preceded, tuple, pair, terminated};
use self::nom::combinator::{map, map_res, opt, peek, eof, complete};
use self::nom::multi::{fold_many0, many0};
use self::nom::branch::alt;
use self::nom::IResult; use self::nom::IResult;
use self::nom::{
alt, char, complete, delimited, do_parse, eof, is_a, is_not, many0, map, map_res, named,
none_of, opt, peek, tag, take, take_until, terminated,
};
use playlist::*; use playlist::*;
use std::collections::HashMap; use std::collections::HashMap;
use std::f32; use std::f32;
@ -221,55 +222,61 @@ pub fn contains_master_tag(input: &[u8]) -> Option<(bool, String)> {
is_master_opt is_master_opt
} }
named!(pub is_master_playlist_tag_line(&[u8]) -> Option<(bool, String)>, pub fn is_master_playlist_tag_line(i: &[u8]) -> IResult<&[u8], Option<(bool, String)>> {
do_parse!( map(
opt!(is_a!("\r\n")) tuple((
>> tag: opt!(alt!( opt(is_a("\r\n")),
map!(tag!("#EXT-X-STREAM-INF"), |t| (true, t)) opt(alt((
| map!(tag!("#EXT-X-I-FRAME-STREAM-INF"), |t| (true, t)) map(tag("#EXT-X-STREAM-INF"), |t| (true, t)),
| map!(terminated!(tag!("#EXT-X-MEDIA"), is_not!("-")), |t| (true, t)) // terminated!() to prevent matching with #EXT-X-MEDIA-SEQUENCE for which we have a separate pattern below map(tag("#EXT-X-I-FRAME-STREAM-INF"), |t| (true, t)),
| map!(tag!("#EXT-X-SESSION-KEY"), |t| (true, t)) map(terminated(tag("#EXT-X-MEDIA"), is_not("-")), |t| (true, t)), // terminated() to prevent matching with #EXT-X-MEDIA-SEQUENCE for which we have a separate pattern below
| map!(tag!("#EXT-X-SESSION-DATA"), |t| (true, t)) map(tag("#EXT-X-SESSION-KEY"), |t| (true, t)),
map(tag("#EXT-X-SESSION-DATA"), |t| (true, t)),
| map!(tag!("#EXT-X-TARGETDURATION"), |t| (false, t)) map(tag("#EXT-X-TARGETDURATION"), |t| (false, t)),
| map!(tag!("#EXT-X-MEDIA-SEQUENCE"), |t| (false, t)) map(tag("#EXT-X-MEDIA-SEQUENCE"), |t| (false, t)),
| map!(tag!("#EXT-X-DISCONTINUITY-SEQUENCE"), |t| (false, t)) map(tag("#EXT-X-DISCONTINUITY-SEQUENCE"), |t| (false, t)),
| map!(tag!("#EXT-X-ENDLIST"), |t| (false, t)) map(tag("#EXT-X-ENDLIST"), |t| (false, t)),
| map!(tag!("#EXT-X-PLAYLIST-TYPE"), |t| (false, t)) map(tag("#EXT-X-PLAYLIST-TYPE"), |t| (false, t)),
| map!(tag!("#EXT-X-I-FRAMES-ONLY"), |t| (false, t)) map(tag("#EXT-X-I-FRAMES-ONLY"), |t| (false, t)),
| map!(tag!("#EXTINF"), |t| (false, t)) map(tag("#EXTINF"), |t| (false, t)),
| map!(tag!("#EXT-X-BYTERANGE"), |t| (false, t)) map(tag("#EXT-X-BYTERANGE"), |t| (false, t)),
| map!(tag!("#EXT-X-DISCONTINUITY"), |t| (false, t)) map(tag("#EXT-X-DISCONTINUITY"), |t| (false, t)),
| map!(tag!("#EXT-X-KEY"), |t| (false, t)) map(tag("#EXT-X-KEY"), |t| (false, t)),
| map!(tag!("#EXT-X-MAP"), |t| (false, t)) map(tag("#EXT-X-MAP"), |t| (false, t)),
| map!(tag!("#EXT-X-PROGRAM-DATE-TIME"), |t| (false, t)) map(tag("#EXT-X-PROGRAM-DATE-TIME"), |t| (false, t)),
| map!(tag!("#EXT-X-DATERANGE"), |t| (false, t)) map(tag("#EXT-X-DATERANGE"), |t| (false, t)),
)) ))),
>> consume_line consume_line,
>> )),
( { |(_, tag, _)| tag.map(|(a,b)| (a, from_utf8_slice(b).unwrap())),
tag.map(|(a,b)| (a, from_utf8_slice(b).unwrap())) )(i)
} ) }
)
);
// ----------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------
// Master Playlist Tags // Master Playlist Tags
// ----------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------
pub fn parse_master_playlist_tags(input: &[u8]) -> IResult<&[u8], Vec<MasterPlaylistTag>> { pub fn parse_master_playlist_tags(i: &[u8]) -> IResult<&[u8], Vec<MasterPlaylistTag>> {
do_parse!( map(
input, tuple((
tags: many0!(complete!(do_parse!( many0(
m: master_playlist_tag >> multispace0 >> (m) complete(
))) >> opt!(eof!()) map(
>> ({ pair(master_playlist_tag, multispace0),
let mut tags_rev: Vec<MasterPlaylistTag> = tags; |(tag, _)| tag,
tags_rev.reverse(); )
tags_rev )
}) ),
) opt(eof),
)),
|(tags, _)| {
let mut tags_rev: Vec<MasterPlaylistTag> = tags;
tags_rev.reverse();
tags_rev
},
)(i)
} }
/// Contains all the tags required to parse a master playlist. /// Contains all the tags required to parse a master playlist.
@ -288,24 +295,27 @@ pub enum MasterPlaylistTag {
Unknown(ExtTag), Unknown(ExtTag),
} }
pub fn master_playlist_tag(input: &[u8]) -> IResult<&[u8], MasterPlaylistTag> { pub fn master_playlist_tag(i: &[u8]) -> IResult<&[u8], MasterPlaylistTag> {
alt!( // Don't accept empty inputs here
input, peek(take(1usize))(i)?;
map!(m3u_tag, MasterPlaylistTag::M3U)
| map!(version_tag, MasterPlaylistTag::Version) alt((
| map!(variant_stream_tag, MasterPlaylistTag::VariantStream) map(m3u_tag, MasterPlaylistTag::M3U),
| map!(variant_i_frame_stream_tag, MasterPlaylistTag::VariantStream) map(version_tag, MasterPlaylistTag::Version),
| map!(alternative_media_tag, MasterPlaylistTag::AlternativeMedia) map(variant_stream_tag, MasterPlaylistTag::VariantStream),
| map!(session_data_tag, MasterPlaylistTag::SessionData) map(variant_i_frame_stream_tag, MasterPlaylistTag::VariantStream),
| map!(session_key_tag, MasterPlaylistTag::SessionKey) map(alternative_media_tag, MasterPlaylistTag::AlternativeMedia),
| map!(start_tag, MasterPlaylistTag::Start) map(session_data_tag, MasterPlaylistTag::SessionData),
| map!(tag!("#EXT-X-INDEPENDENT-SEGMENTS"), |_| { map(session_key_tag, MasterPlaylistTag::SessionKey),
MasterPlaylistTag::IndependentSegments map(start_tag, MasterPlaylistTag::Start),
}) map(
| map!(ext_tag, MasterPlaylistTag::Unknown) tag("#EXT-X-INDEPENDENT-SEGMENTS"),
| map!(comment_tag, MasterPlaylistTag::Comment) |_| MasterPlaylistTag::IndependentSegments,
| map!(consume_line, MasterPlaylistTag::Uri) ),
) map(ext_tag, MasterPlaylistTag::Unknown),
map(comment_tag, MasterPlaylistTag::Comment),
map(consume_line, MasterPlaylistTag::Uri),
))(i)
} }
pub fn master_playlist_from_tags(mut tags: Vec<MasterPlaylistTag>) -> MasterPlaylist { pub fn master_playlist_from_tags(mut tags: Vec<MasterPlaylistTag>) -> MasterPlaylist {
@ -349,48 +359,79 @@ pub fn master_playlist_from_tags(mut tags: Vec<MasterPlaylistTag>) -> MasterPlay
master_playlist master_playlist
} }
named!(pub variant_stream_tag<VariantStream>, pub fn variant_stream_tag(i: &[u8]) -> IResult<&[u8], VariantStream> {
do_parse!(tag!("#EXT-X-STREAM-INF:") >> attributes: key_value_pairs >> map(
( VariantStream::from_hashmap(attributes, false))) pair(
); tag("#EXT-X-STREAM-INF:"),
key_value_pairs,
),
|(_, attributes)| VariantStream::from_hashmap(attributes, false),
)(i)
}
named!(pub variant_i_frame_stream_tag<VariantStream>, pub fn variant_i_frame_stream_tag(i: &[u8]) -> IResult<&[u8], VariantStream> {
do_parse!( tag!("#EXT-X-I-FRAME-STREAM-INF:") >> attributes: key_value_pairs >> map(
( VariantStream::from_hashmap(attributes, true))) pair(
); tag("#EXT-X-I-FRAME-STREAM-INF:"),
key_value_pairs,
),
|(_, attributes)| VariantStream::from_hashmap(attributes, true),
)(i)
}
named!(pub alternative_media_tag<AlternativeMedia>, pub fn alternative_media_tag(i: &[u8]) -> IResult<&[u8], AlternativeMedia> {
do_parse!( tag!("#EXT-X-MEDIA:") >> attributes: key_value_pairs >> map(
( AlternativeMedia::from_hashmap(attributes))) pair(
); tag("#EXT-X-MEDIA:"),
key_value_pairs,
),
|(_, media)| AlternativeMedia::from_hashmap(media),
)(i)
}
named!(pub session_data_tag<SessionData>, pub fn session_data_tag(i: &[u8]) -> IResult<&[u8], SessionData> {
do_parse!( tag!("#EXT-X-SESSION-DATA:") >> map_res(
session_data: map_res!(key_value_pairs, |attrs| SessionData::from_hashmap(attrs)) >> pair(
( session_data)) tag("#EXT-X-SESSION-DATA:"),
); key_value_pairs,
),
|(_, session_data)| SessionData::from_hashmap(session_data),
)(i)
}
named!(pub session_key_tag<SessionKey>, pub fn session_key_tag(i: &[u8]) -> IResult<&[u8], SessionKey> {
do_parse!( tag!("#EXT-X-SESSION-KEY:") >> session_key: map!(key, SessionKey) >> map(
( session_key)) pair(
); tag("#EXT-X-SESSION-KEY:"),
key,
),
|(_, key)| SessionKey(key),
)(i)
}
// ----------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------
// Media Playlist // Media Playlist
// ----------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------
pub fn parse_media_playlist_tags(input: &[u8]) -> IResult<&[u8], Vec<MediaPlaylistTag>> { pub fn parse_media_playlist_tags(i: &[u8]) -> IResult<&[u8], Vec<MediaPlaylistTag>> {
do_parse!( map(
input, tuple((
tags: many0!(complete!(do_parse!( many0(
m: media_playlist_tag >> multispace0 >> (m) complete(
))) >> opt!(eof!()) map(
>> ({ pair(media_playlist_tag, multispace0),
let mut tags_rev: Vec<MediaPlaylistTag> = tags; |(tag, _)| tag,
tags_rev.reverse(); )
tags_rev )
}) ),
) opt(eof),
)),
|(tags, _)| {
let mut tags_rev: Vec<MediaPlaylistTag> = tags;
tags_rev.reverse();
tags_rev
},
)(i)
} }
/// Contains all the tags required to parse a media playlist. /// Contains all the tags required to parse a media playlist.
@ -410,37 +451,56 @@ pub enum MediaPlaylistTag {
IndependentSegments, IndependentSegments,
} }
pub fn media_playlist_tag(input: &[u8]) -> IResult<&[u8], MediaPlaylistTag> { pub fn media_playlist_tag(i: &[u8]) -> IResult<&[u8], MediaPlaylistTag> {
alt!( // Don't accept empty inputs here
input, peek(take(1usize))(i)?;
map!(m3u_tag, MediaPlaylistTag::M3U)
| map!(version_tag, MediaPlaylistTag::Version) alt((
| map!( map(m3u_tag, MediaPlaylistTag::M3U),
do_parse!(tag!("#EXT-X-TARGETDURATION:") >> n: float >> (n)), map(version_tag, MediaPlaylistTag::Version),
MediaPlaylistTag::TargetDuration map(
) pair(
| map!( tag("#EXT-X-TARGETDURATION:"),
do_parse!(tag!("#EXT-X-MEDIA-SEQUENCE:") >> n: number >> (n)), float,
MediaPlaylistTag::MediaSequence ),
) |(_, duration)| MediaPlaylistTag::TargetDuration(duration),
| map!( ),
do_parse!(tag!("#EXT-X-DISCONTINUITY-SEQUENCE:") >> n: number >> (n)), map(
MediaPlaylistTag::DiscontinuitySequence pair(
) tag("#EXT-X-MEDIA-SEQUENCE:"),
| map!( number,
do_parse!(tag!("#EXT-X-PLAYLIST-TYPE:") >> t: playlist_type >> (t)), ),
MediaPlaylistTag::PlaylistType |(_, sequence)| MediaPlaylistTag::MediaSequence(sequence),
) ),
| map!(tag!("#EXT-X-I-FRAMES-ONLY"), |_| { map(
MediaPlaylistTag::IFramesOnly pair(
}) tag("#EXT-X-DISCONTINUITY-SEQUENCE:"),
| map!(start_tag, MediaPlaylistTag::Start) number,
| map!(tag!("#EXT-X-INDEPENDENT-SEGMENTS"), |_| { ),
MediaPlaylistTag::IndependentSegments |(_, sequence)| MediaPlaylistTag::DiscontinuitySequence(sequence),
}) ),
| map!(tag!("#EXT-X-ENDLIST"), |_| MediaPlaylistTag::EndList) map(
| map!(media_segment_tag, MediaPlaylistTag::Segment) pair(
) tag("#EXT-X-PLAYLIST-TYPE:"),
playlist_type,
),
|(_, typ)| MediaPlaylistTag::PlaylistType(typ),
),
map(
tag("#EXT-X-I-FRAMES-ONLY"),
|_| MediaPlaylistTag::IFramesOnly,
),
map(start_tag, MediaPlaylistTag::Start),
map(
tag("#EXT-X-INDEPENDENT-SEGMENTS"),
|_| MediaPlaylistTag::IndependentSegments,
),
map(
tag("#EXT-X-ENDLIST"),
|_| MediaPlaylistTag::EndList,
),
map(media_segment_tag, MediaPlaylistTag::Segment),
))(i)
} }
pub fn media_playlist_from_tags(mut tags: Vec<MediaPlaylistTag>) -> MediaPlaylist { pub fn media_playlist_from_tags(mut tags: Vec<MediaPlaylistTag>) -> MediaPlaylist {
@ -521,16 +581,15 @@ pub fn media_playlist_from_tags(mut tags: Vec<MediaPlaylistTag>) -> MediaPlaylis
media_playlist media_playlist
} }
named!(pub playlist_type<MediaPlaylistType>, pub fn playlist_type(i: &[u8]) -> IResult<&[u8], MediaPlaylistType> {
map_res!( map_res(
do_parse!( tuple((
p: map_res!(is_not!("\r\n"), str::from_utf8) map_res(is_not("\r\n"), str::from_utf8),
>> take!(1) take(1usize),
>> (p) )),
), |(typ, _)| MediaPlaylistType::from_str(typ),
MediaPlaylistType::from_str )(i)
) }
);
// ----------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------
// Media Segment // Media Segment
@ -551,175 +610,269 @@ pub enum SegmentTag {
Uri(String), Uri(String),
} }
pub fn media_segment_tag(input: &[u8]) -> IResult<&[u8], SegmentTag> { pub fn media_segment_tag(i: &[u8]) -> IResult<&[u8], SegmentTag> {
alt!( alt((
input, map(
map!( pair(
do_parse!(tag!("#EXTINF:") >> e: duration_title_tag >> (e)), tag("#EXTINF:"),
|(a, b)| SegmentTag::Extinf(a, b) duration_title_tag,
) | map!( ),
do_parse!(tag!("#EXT-X-BYTERANGE:") >> r: byte_range_val >> (r)), |(_, (duration, title))| SegmentTag::Extinf(duration, title),
SegmentTag::ByteRange ),
) | map!(tag!("#EXT-X-DISCONTINUITY"), |_| SegmentTag::Discontinuity) map(
| map!( pair(
do_parse!(tag!("#EXT-X-KEY:") >> k: key >> (k)), tag("#EXT-X-BYTERANGE:"),
SegmentTag::Key byte_range_val,
) ),
| map!( |(_, range)| SegmentTag::ByteRange(range),
do_parse!(tag!("#EXT-X-MAP:") >> m: extmap >> (m)), ),
SegmentTag::Map map(
) tag("#EXT-X-DISCONTINUITY"),
| map!( |_| SegmentTag::Discontinuity
do_parse!(tag!("#EXT-X-PROGRAM-DATE-TIME:") >> t: consume_line >> (t)), ),
SegmentTag::ProgramDateTime map(
) pair(
| map!( tag("#EXT-X-KEY:"),
do_parse!(tag!("#EXT-X-DATE-RANGE:") >> t: consume_line >> (t)), key,
SegmentTag::DateRange ),
) |(_, key)| SegmentTag::Key(key),
| map!(ext_tag, SegmentTag::Unknown) ),
| map!(comment_tag, SegmentTag::Comment) map(
| map!(consume_line, SegmentTag::Uri) pair(
) tag("#EXT-X-MAP:"),
extmap,
),
|(_, map)| SegmentTag::Map(map),
),
map(
pair(
tag("#EXT-X-PROGRAM-DATE-TIME:"),
consume_line,
),
|(_, line)| SegmentTag::ProgramDateTime(line),
),
map(
pair(
tag("#EXT-X-DATE-RANGE:"),
consume_line,
),
|(_, line)| SegmentTag::DateRange(line),
),
map(ext_tag, SegmentTag::Unknown),
map(comment_tag, SegmentTag::Comment),
map(consume_line, SegmentTag::Uri),
))(i)
} }
named!(pub duration_title_tag<(f32, Option<String>)>, pub fn duration_title_tag(i: &[u8]) -> IResult<&[u8], (f32, Option<String>)> {
do_parse!( map(
duration: float tuple((
>> opt!(tag!(",")) float,
>> title: opt!(map_res!(is_not!("\r\n,"), from_utf8_slice)) opt(char(',')),
>> take!(1) opt(
>> opt!(tag!(",")) map_res(is_not("\r\n,"), from_utf8_slice),
>> ),
(duration, title) take(1usize),
) opt(char(',')),
); )),
|(duration, _, title, _, _)| (duration, title),
)(i)
}
named!(pub key<Key>, map!(key_value_pairs, Key::from_hashmap)); pub fn key(i: &[u8]) -> IResult<&[u8], Key> {
map(
key_value_pairs,
Key::from_hashmap,
)(i)
}
named!(pub extmap<Map>, map!(key_value_pairs, |attrs| Map { pub fn extmap(i: &[u8]) -> IResult<&[u8], Map> {
uri: attrs.get("URI").cloned().unwrap_or_default(), map_res(
byte_range: attrs.get("BYTERANGE").map(|range| { key_value_pairs,
match byte_range_val(range.as_bytes()) { |attrs| -> Result<Map, &str> {
IResult::Ok((_, br)) => br, let uri = attrs.get("URI").cloned().unwrap_or_default();
_ => panic!("Should not happen"), let byte_range = attrs.get("BYTERANGE").map(|range|
match byte_range_val(range.as_bytes()) {
IResult::Ok((_, range)) => Ok(range),
IResult::Err(_) => Err("invalid byte range"),
}
).transpose()?;
Ok(Map {
uri,
byte_range,
})
} }
}), )(i)
})); }
// ----------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------
// Basic tags // Basic tags
// ----------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------
named!(pub m3u_tag<String>, pub fn m3u_tag(i: &[u8]) -> IResult<&[u8], String> {
map_res!(tag!("#EXTM3U"), from_utf8_slice) map_res(
); tag("#EXTM3U"),
from_utf8_slice,
)(i)
}
named!(pub version_tag<usize>, pub fn version_tag(i: &[u8]) -> IResult<&[u8], usize> {
do_parse!( map(
tag!("#EXT-X-VERSION:") >> version: map_res!(digit1, str::from_utf8) >> pair(
(version.parse().unwrap_or_default()) tag("#EXT-X-VERSION:"),
) map_res(digit1, str::from_utf8),
); ),
|(_, version)| {
version.parse().unwrap_or_default()
}
)(i)
}
named!(pub start_tag<Start>, pub fn start_tag(i: &[u8]) -> IResult<&[u8], Start> {
do_parse!(tag!("#EXT-X-START:") >> attributes:key_value_pairs >> map(
(Start::from_hashmap(attributes)) pair(
) tag("#EXT-X-START:"),
); key_value_pairs,
),
|(_, attributes)| {
Start::from_hashmap(attributes)
}
)(i)
}
named!(pub ext_tag<ExtTag>, pub fn ext_tag(i: &[u8]) -> IResult<&[u8], ExtTag> {
do_parse!( map(
tag!("#EXT-") tuple((
>> tag: map_res!(is_not!("\r\n:"), from_utf8_slice) tag("#EXT-"),
>> opt!(tag!(":")) map_res(is_not("\r\n:"), from_utf8_slice),
>> rest: opt!(map_res!(is_not!("\r\n"), from_utf8_slice)) opt(char(':')),
>> take!(1) opt(map_res(is_not("\r\n"), from_utf8_slice)),
>> ( take(1usize),
)),
|(_, tag, _, rest, _)| {
ExtTag { tag, rest } ExtTag { tag, rest }
) }
) )(i)
); }
named!(pub comment_tag<String>, pub fn comment_tag(i: &[u8]) -> IResult<&[u8], String> {
do_parse!( map(
tag!("#") >> text: map_res!(is_not!("\r\n"), from_utf8_slice) pair(
>> take!(1) preceded(
>> (text) char('#'),
) map_res(is_not("\r\n"), from_utf8_slice),
); ),
take(1usize),
),
|(text, _)| text,
)(i)
}
// ----------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------
// Util // Util
// ----------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------
named!(pub key_value_pairs(&[u8]) -> HashMap<String, String>, pub fn key_value_pairs(i: &[u8]) -> IResult<&[u8], HashMap<String, String>> {
map!( fold_many0(
many0!(do_parse!(space0 >> k:key_value_pair >> (k) )) preceded(space0, key_value_pair),
, HashMap::new,
|pairs: Vec<(String, String)>| { |mut acc: HashMap<_, _>, (left, right)| {
pairs.into_iter().collect() acc.insert(left, right);
acc
} }
) )(i)
); }
named!(pub key_value_pair(&[u8]) -> (String, String), pub fn key_value_pair(i: &[u8]) -> IResult<&[u8], (String, String)> {
do_parse!( map(
peek!(none_of!("\r\n")) tuple((
>> left: map_res!(take_until!("="), from_utf8_slice) peek(none_of("\r\n")),
>> take!(1) map_res(take_until("="), from_utf8_slice),
>> right: alt!(quoted | unquoted) char('='),
>> opt!(char!(',')) alt((quoted, unquoted)),
>> opt(char(',')),
(left, right) )),
) |(_, left, _, right, _)| {
); (left, right)
}
)(i)
}
named!(pub quoted<String>, pub fn quoted(i: &[u8]) -> IResult<&[u8], String> {
delimited!(char!('\"'), map_res!(is_not!("\""), from_utf8_slice), char!('\"')) delimited(
); char('\"'),
map_res(
is_not("\""),
from_utf8_slice
),
char('\"')
)(i)
}
named!(pub unquoted<String>, pub fn unquoted(i: &[u8]) -> IResult<&[u8], String> {
map_res!(is_not!(",\r\n"), from_utf8_slice) map_res(
); is_not(",\r\n"),
from_utf8_slice
)(i)
}
named!(pub consume_line<String>, pub fn consume_line(i: &[u8]) -> IResult<&[u8], String> {
do_parse!( map(
line: map_res!(not_line_ending, from_utf8_slice) pair(
>> opt!(line_ending) map_res(not_line_ending, from_utf8_slice),
>> (line) opt(line_ending),
) ),
); |(line, _)| line,
)(i)
}
named!(pub number<i32>, pub fn number(i: &[u8]) -> IResult<&[u8], i32> {
map_res!(map_res!(digit1, str::from_utf8), str::FromStr::from_str) map_res(take_while1(is_digit),
); |s| {
// Can't fail because we validated it above already
let s = str::from_utf8(s).unwrap();
str::parse::<i32>(s)
})(i)
}
named!(pub byte_range_val<ByteRange>, pub fn byte_range_val(i: &[u8]) -> IResult<&[u8], ByteRange> {
do_parse!( map(
n: number pair(
>> o: opt!(do_parse!(char!('@') >> n:number >> (n) )) >> number,
(ByteRange { length: n, offset: o }) opt(
) preceded(char('@'), number)
); ),
),
|(n, o)| {
ByteRange { length: n, offset: o }
}
)(i)
}
named!(pub float<f32>, pub fn float(i: &[u8]) -> IResult<&[u8], f32> {
do_parse!( map_res(
left: map_res!(digit1, str::from_utf8) pair(
>> right_opt: opt!(do_parse!(char!('.') >> d:map_res!(digit1, str::from_utf8) >> (d) )) take_while1(is_digit),
>> opt(
( preceded(char('.'), take_while1(is_digit))
match right_opt { ),
),
|(left, right): (&[u8], Option<&[u8]>)| match right {
Some(right) => { Some(right) => {
let mut num = String::from(left); let n = &i[..(left.len() + right.len() + 1)];
num.push('.'); // Can't fail because we validated it above already
num.push_str(right); let n = str::from_utf8(n).unwrap();
num.parse().unwrap() n.parse()
}, }
None => left.parse().unwrap(), None => {
}) // Can't fail because we validated it above already
) let left = str::from_utf8(left).unwrap();
); left.parse()
}
}
)(i)
}
pub fn from_utf8_slice(s: &[u8]) -> Result<String, string::FromUtf8Error> { pub fn from_utf8_slice(s: &[u8]) -> Result<String, string::FromUtf8Error> {
String::from_utf8(s.to_vec()) String::from_utf8(s.to_vec())

View file

@ -5,7 +5,7 @@ extern crate nom;
use m3u8_rs::playlist::*; use m3u8_rs::playlist::*;
use m3u8_rs::*; use m3u8_rs::*;
use nom::*; use nom::AsBytes;
use std::collections::HashMap; use std::collections::HashMap;
use std::fs; use std::fs;
use std::fs::File; use std::fs::File;