From 22571a34048d1e587a4de5199925d7ad32c3a76a Mon Sep 17 00:00:00 2001 From: Vadim Getmanshchuk Date: Wed, 12 Feb 2020 12:04:20 -0800 Subject: [PATCH 01/16] A stub on updating nom to 5.1.0 version --- src/lib.rs | 151 +++++++++++++++++++++++++++-------------------------- 1 file changed, 77 insertions(+), 74 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index f19faf1..1e68a0c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -46,7 +46,7 @@ //! let mut file = std::fs::File::open("masterplaylist.m3u8").unwrap(); //! let mut bytes: Vec = Vec::new(); //! file.read_to_end(&mut bytes).unwrap(); -//! +//! //! if let IResult::Done(_, pl) = m3u8_rs::parse_master_playlist(&bytes) { //! println!("{:?}", pl); //! } @@ -61,7 +61,7 @@ //! use m3u8_rs::playlist::{MediaPlaylist, MediaPlaylistType, MediaSegment}; //! //! fn main() { -//! let playlist = MediaPlaylist { +//! let playlist = MediaPlaylist { //! version: 6, //! target_duration: 3.0, //! media_sequence: 338559, @@ -78,10 +78,10 @@ //! ], //! ..Default::default() //! }; -//! +//! //! //let mut v: Vec = Vec::new(); //! //playlist.write_to(&mut v).unwrap(); -//! +//! //! //let mut file = std::fs::File::open("playlist.m3u8").unwrap(); //! //playlist.write_to(&mut file).unwrap(); //! } @@ -94,6 +94,7 @@ extern crate nom; pub mod playlist; use nom::*; + use std::str; use std::f32; use std::string; @@ -221,7 +222,7 @@ pub fn contains_master_tag(input: &[u8]) -> Option<(bool, String)> { } named!(pub is_master_playlist_tag_line(&[u8]) -> Option<(bool, String)>, - chain!( + do_parse!( tag: opt!(alt!( map!(tag!("#EXT-X-STREAM-INF"), |t| (true, t)) | map!(tag!("#EXT-X-I-FRAME-STREAM-INF"), |t| (true, t)) @@ -243,11 +244,11 @@ named!(pub is_master_playlist_tag_line(&[u8]) -> Option<(bool, String)>, | map!(tag!("#EXT-X-MAP"), |t| (false, t)) | map!(tag!("#EXT-X-PROGRAM-DATE-TIME"), |t| (false, t)) | map!(tag!("#EXT-X-DATERANGE"), |t| (false, t)) - )) - ~ consume_line - , || { + )) >> + consume_line >> + ( { tag.map(|(a,b)| (a, from_utf8_slice(b).unwrap())) - } + } ) ) ); @@ -256,9 +257,9 @@ named!(pub is_master_playlist_tag_line(&[u8]) -> Option<(bool, String)>, // ----------------------------------------------------------------------------------------------- pub fn parse_master_playlist_tags(input: &[u8]) -> IResult<&[u8], Vec> { - chain!(input, - mut tags: many0!(chain!(m:master_playlist_tag ~ multispace?, || m)) ~ eof?, - || { tags.reverse(); tags } + do_parse!(input, + tags: many0!(do_parse!(m:master_playlist_tag >> opt!(character::complete::multispace1) >> (m) )) >> opt!(eof!()) >> + ( { tags.reverse(); tags } ) ) } @@ -299,28 +300,28 @@ pub fn master_playlist_tag(input: &[u8]) -> IResult<&[u8], MasterPlaylistTag> { } named!(pub variant_stream_tag, - chain!(tag!("#EXT-X-STREAM-INF:") ~ attributes: key_value_pairs, - || VariantStream::from_hashmap(attributes, false)) + do_parse!(tag!("#EXT-X-STREAM-INF:") >> attributes: key_value_pairs >> + ( VariantStream::from_hashmap(attributes, false))) ); named!(pub variant_i_frame_stream_tag, - chain!( tag!("#EXT-X-I-FRAME-STREAM-INF:") ~ attributes: key_value_pairs, - || VariantStream::from_hashmap(attributes, true)) + do_parse!( tag!("#EXT-X-I-FRAME-STREAM-INF:") >> attributes: key_value_pairs >> + ( VariantStream::from_hashmap(attributes, true))) ); named!(pub alternative_media_tag, - chain!( tag!("#EXT-X-MEDIA:") ~ attributes: key_value_pairs, - || AlternativeMedia::from_hashmap(attributes)) + do_parse!( tag!("#EXT-X-MEDIA:") >> attributes: key_value_pairs >> + ( AlternativeMedia::from_hashmap(attributes))) ); named!(pub session_data_tag, - chain!( tag!("#EXT-X-SESSION-DATA:") ~ attributes: key_value_pairs, - || SessionData::from_hashmap(attributes)) + do_parse!( tag!("#EXT-X-SESSION-DATA:") >> attributes: key_value_pairs >> + ( SessionData::from_hashmap(attributes))) ); named!(pub session_key_tag, - chain!( tag!("#EXT-X-SESSION-KEY:") ~ session_key: map!(key, SessionKey), - || session_key) + do_parse!( tag!("#EXT-X-SESSION-KEY:") >> session_key: map!(key, SessionKey) >> + ( session_key)) ); // ----------------------------------------------------------------------------------------------- @@ -328,9 +329,9 @@ named!(pub session_key_tag, // ----------------------------------------------------------------------------------------------- pub fn parse_media_playlist_tags(input: &[u8]) -> IResult<&[u8], Vec> { - chain!(input, - mut tags: many0!(chain!(m:media_playlist_tag ~ multispace?, || m)) ~ eof?, - || { tags.reverse(); tags } + do_parse!(input, + tags: many0!(do_parse!(m:media_playlist_tag >> opt!(character::complete::multispace1) >> (m) )) >> opt!(eof!()) >> + ( { tags.reverse(); tags } ) ) } @@ -355,10 +356,10 @@ pub fn media_playlist_tag(input: &[u8]) -> IResult<&[u8], MediaPlaylistTag> { map!(m3u_tag, MediaPlaylistTag::M3U) | map!(version_tag, MediaPlaylistTag::Version) - | map!(chain!(tag!("#EXT-X-TARGETDURATION:") ~ n:float,||n), MediaPlaylistTag::TargetDuration) - | map!(chain!(tag!("#EXT-X-MEDIA-SEQUENCE:") ~ n:number,||n), MediaPlaylistTag::MediaSequence) - | map!(chain!(tag!("#EXT-X-DISCONTINUITY-SEQUENCE:") ~ n:number,||n), MediaPlaylistTag::DiscontinuitySequence) - | map!(chain!(tag!("#EXT-X-PLAYLIST-TYPE:") ~ t:playlist_type, ||t), MediaPlaylistTag::PlaylistType) + | map!(do_parse!(tag!("#EXT-X-TARGETDURATION:") >> n:float >> (n)), MediaPlaylistTag::TargetDuration) + | map!(do_parse!(tag!("#EXT-X-MEDIA-SEQUENCE:") >> n:number >> (n)), MediaPlaylistTag::MediaSequence) + | map!(do_parse!(tag!("#EXT-X-DISCONTINUITY-SEQUENCE:") >> n:number >> (n)), MediaPlaylistTag::DiscontinuitySequence) + | map!(do_parse!(tag!("#EXT-X-PLAYLIST-TYPE:") >> t:playlist_type >> (t)), MediaPlaylistTag::PlaylistType) | map!(tag!("#EXT-X-I-FRAMES-ONLY"), |_| MediaPlaylistTag::IFramesOnly) | map!(start_tag, MediaPlaylistTag::Start) | map!(tag!("#EXT-X-INDEPENDENT-SEGMENTS"), |_| MediaPlaylistTag::IndependentSegments) @@ -370,7 +371,7 @@ pub fn media_playlist_tag(input: &[u8]) -> IResult<&[u8], MediaPlaylistTag> { named!(pub playlist_type, map_res!( - map_res!(take_until_either_and_consume!("\r\n"), str::from_utf8), + map_res!(take_until!("\r\n"), str::from_utf8), MediaPlaylistType::from_str ) ); @@ -396,13 +397,13 @@ pub enum SegmentTag { pub fn media_segment_tag(input: &[u8]) -> IResult<&[u8], SegmentTag> { alt!(input, - map!(chain!(tag!("#EXTINF:") ~ e:duration_title_tag,||e), |(a,b)| SegmentTag::Extinf(a,b)) - | map!(chain!(tag!("#EXT-X-BYTERANGE:") ~ r:byte_range_val, || r), SegmentTag::ByteRange) + map!(do_parse!(tag!("#EXTINF:") >> e:duration_title_tag >> (e)), |(a,b)| SegmentTag::Extinf(a,b)) + | map!(do_parse!(tag!("#EXT-X-BYTERANGE:") >> r:byte_range_val >> (r)), SegmentTag::ByteRange) | map!(tag!("#EXT-X-DISCONTINUITY"), |_| SegmentTag::Discontinuity) - | map!(chain!(tag!("#EXT-X-KEY:") ~ k:key, || k), SegmentTag::Key) - | map!(chain!(tag!("#EXT-X-MAP:") ~ m:map, || m), SegmentTag::Map) - | map!(chain!(tag!("#EXT-X-PROGRAM-DATE-TIME:") ~ t:consume_line, || t), SegmentTag::ProgramDateTime) - | map!(chain!(tag!("#EXT-X-DATE-RANGE:") ~ t:consume_line, || t), SegmentTag::DateRange) + | map!(do_parse!(tag!("#EXT-X-KEY:") >> k:key >> (k)), SegmentTag::Key) + | map!(do_parse!(tag!("#EXT-X-MAP:") >> m:map >> (m)), SegmentTag::Map) + | map!(do_parse!(tag!("#EXT-X-PROGRAM-DATE-TIME:") >> t:consume_line >> (t)), SegmentTag::ProgramDateTime) + | map!(do_parse!(tag!("#EXT-X-DATE-RANGE:") >> t:consume_line >> (t)), SegmentTag::DateRange) | map!(ext_tag, SegmentTag::Unknown) | map!(comment_tag, SegmentTag::Comment) @@ -412,13 +413,13 @@ pub fn media_segment_tag(input: &[u8]) -> IResult<&[u8], SegmentTag> { } named!(pub duration_title_tag<(f32, Option)>, - chain!( - duration: float - ~ tag!(",")? - ~ title: opt!(map_res!(take_until_either_and_consume!("\r\n,"), from_utf8_slice)) - ~ tag!(",")? - , - || (duration, title) + do_parse!( + duration: float + >> opt!(tag!(",")) + >> title: opt!(map_res!(take_until!("\r\n,"), from_utf8_slice)) + >> opt!(tag!(",")) + >> + (duration, title) ) ); @@ -435,30 +436,32 @@ named!(pub m3u_tag, ); named!(pub version_tag, - chain!( - tag!("#EXT-X-VERSION:") ~ version: map_res!(digit, str::from_utf8), - || version.parse().unwrap_or_default() + do_parse!( + tag!("#EXT-X-VERSION:") >> version: map_res!(nom::character::streaming::digit1, str::from_utf8) >> + (version.parse().unwrap_or_default()) ) ); named!(pub start_tag, - chain!(tag!("#EXT-X-START:") ~ attributes:key_value_pairs, || Start::from_hashmap(attributes)) + do_parse!(tag!("#EXT-X-START:") >> attributes:key_value_pairs >> + (Start::from_hashmap(attributes)) + ) ); named!(pub ext_tag, - chain!( + do_parse!( tag!("#EXT-") - ~ tag: map_res!(take_until_and_consume!(":"), from_utf8_slice) - ~ rest: map_res!(take_until_either_and_consume!("\r\n"), from_utf8_slice) - , - || ExtTag { tag: tag, rest: rest } + >> tag: map_res!(take_until!(":"), from_utf8_slice) + >> rest: map_res!(take_until!("\r\n"), from_utf8_slice) + >> + (ExtTag { tag: tag, rest: rest }) ) ); named!(pub comment_tag, - chain!( - tag!("#") ~ text: map_res!(take_until_either_and_consume!("\r\n"), from_utf8_slice), - || text + do_parse!( + tag!("#") >> text: map_res!(take_until!("\r\n"), from_utf8_slice) + >> (text) ) ); @@ -468,7 +471,7 @@ named!(pub comment_tag, named!(pub key_value_pairs(&[u8]) -> HashMap, map!( - many0!(chain!(space? ~ k:key_value_pair,|| k)) + many0!(do_parse!(opt!(nom::character::streaming::space1) >> k:key_value_pair >> (k) )) , |pairs: Vec<(String, String)>| { pairs.into_iter().collect() @@ -477,13 +480,14 @@ named!(pub key_value_pairs(&[u8]) -> HashMap, ); named!(pub key_value_pair(&[u8]) -> (String, String), - chain!( + do_parse!( peek!(none_of!("\r\n")) - ~ left: map_res!(take_until_and_consume!("="), from_utf8_slice) - ~ right: alt!(quoted | unquoted) - ~ char!(',')? - , - || (left, right) + >> left: map_res!(take_until!("="), from_utf8_slice) + >> right: alt!(quoted | unquoted) + >> opt!(char!(',')) + >> + + (left, right) ) ); @@ -492,31 +496,30 @@ named!(pub quoted, ); named!(pub unquoted, - map_res!(take_until_either!(",\r\n"), from_utf8_slice) + map_res!(take_until!(",\r\n"), from_utf8_slice) ); named!(pub consume_line, - map_res!(take_until_either_and_consume!("\r\n"), from_utf8_slice) + map_res!(take_until!("\r\n"), from_utf8_slice) ); named!(pub number, - map_res!(map_res!(digit, str::from_utf8), str::FromStr::from_str) + map_res!(map_res!(nom::character::streaming::digit1, str::from_utf8), str::FromStr::from_str) ); named!(pub byte_range_val, - chain!( + do_parse!( n: number - ~ o: opt!(chain!(char!('@') ~ n:number,||n)) - , - || ByteRange { length: n, offset: o } + >> o: opt!(do_parse!(char!('@') >> n:number >> (n))) >> + (ByteRange { length: n, offset: o }) ) ); named!(pub float, - chain!( - left: map_res!(digit, str::from_utf8) - ~ right_opt: opt!(chain!(char!('.') ~ d:map_res!(digit, str::from_utf8),|| d)), - || + do_parse!( + left: map_res!(nom::character::streaming::digit1, str::from_utf8) + >> right_opt: opt!(do_parse!(char!('.') >> d:map_res!(nom::character::streaming::digit1, str::from_utf8) >> (d) )) >> + ( match right_opt { Some(right) => { let mut num = String::from(left); @@ -525,7 +528,7 @@ named!(pub float, num.parse().unwrap() }, None => left.parse().unwrap(), - } + }) ) ); From ca07767eb40db623b8d7ffaed0103113eb3e75ca Mon Sep 17 00:00:00 2001 From: Vadim Getmanshchuk Date: Wed, 12 Feb 2020 12:19:40 -0800 Subject: [PATCH 02/16] Update Cargo.toml upped nom dependency to 5.1.0 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 6fb28c3..4df9c4a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,4 +9,4 @@ documentation = "https://rutgersc.github.io/doc/m3u8_rs/index.html" license = "MIT" [dependencies] -nom = "^1.2.3" +nom = "5.1.0" From 67eebd17a0c4fc3ce322b6265d216197d9ad3ecd Mon Sep 17 00:00:00 2001 From: Rogier 'DocWilco' Mulhuijzen Date: Wed, 12 Feb 2020 14:38:15 -0800 Subject: [PATCH 03/16] Done is now Ok, map works a little different --- src/lib.rs | 18 +++++++++--------- src/playlist.rs | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 1e68a0c..f2149aa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -88,7 +88,6 @@ //! //! ``` -#[macro_use] extern crate nom; pub mod playlist; @@ -129,8 +128,9 @@ use playlist::*; /// } pub fn parse_playlist(input: &[u8]) -> IResult<&[u8], Playlist> { match is_master_playlist(input) { - true => parse_master_playlist(input).map(Playlist::MasterPlaylist), - false => parse_media_playlist(input).map(Playlist::MediaPlaylist), + // XXX: get rid of the local `map` to be able to `use` this + true => nom::combinator::map(parse_master_playlist, Playlist::MasterPlaylist)(input), + false => nom::combinator::map(parse_media_playlist, Playlist::MediaPlaylist)(input), } } @@ -157,35 +157,35 @@ pub fn parse_playlist(input: &[u8]) -> IResult<&[u8], Playlist> { pub fn parse_playlist_res(input: &[u8]) -> Result> { let parse_result = parse_playlist(input); match parse_result { - IResult::Done(_, playlist) => Ok(playlist), + IResult::Ok((_, playlist)) => Ok(playlist), _ => Err(parse_result), } } /// Parse input as a master playlist pub fn parse_master_playlist(input: &[u8]) -> IResult<&[u8], MasterPlaylist> { - parse_master_playlist_tags(input).map(MasterPlaylist::from_tags) + nom::combinator::map(parse_master_playlist_tags, MasterPlaylist::from_tags)(input) } /// Parse input as a master playlist pub fn parse_master_playlist_res(input: &[u8]) -> Result> { let parse_result = parse_master_playlist(input); match parse_result { - IResult::Done(_, playlist) => Ok(playlist), + IResult::Ok((_, playlist)) => Ok(playlist), _ => Err(parse_result), } } /// Parse input as a media playlist pub fn parse_media_playlist(input: &[u8]) -> IResult<&[u8], MediaPlaylist> { - parse_media_playlist_tags(input).map(MediaPlaylist::from_tags) + nom::combinator::map(parse_media_playlist_tags, MediaPlaylist::from_tags)(input) } /// Parse input as a media playlist pub fn parse_media_playlist_res(input: &[u8]) -> Result> { let parse_result = parse_media_playlist(input); match parse_result { - IResult::Done(_, playlist) => Ok(playlist), + IResult::Ok((_, playlist)) => Ok(playlist), _ => Err(parse_result), } } @@ -210,7 +210,7 @@ pub fn contains_master_tag(input: &[u8]) -> Option<(bool, String)> { while is_master_opt == None { match is_master_playlist_tag_line(current_input) { - IResult::Done(rest, result) => { + IResult::Ok((rest, result)) => { current_input = rest; is_master_opt = result; // result can be None (no media or master tag found) } diff --git a/src/playlist.rs b/src/playlist.rs index 13417ab..e87ad50 100644 --- a/src/playlist.rs +++ b/src/playlist.rs @@ -732,7 +732,7 @@ impl From for ByteRange { impl<'a> From<&'a str> for ByteRange { fn from(s: &'a str) -> Self { match byte_range_val(s.as_bytes()) { - IResult::Done(_, br) => br, + IResult::Ok((_, br)) => br, _ => panic!("Should not happen"), } } From af15863688cb10408dfe868ef33d4f93aacd32dd Mon Sep 17 00:00:00 2001 From: Vadim Getmanshchuk Date: Wed, 12 Feb 2020 16:35:31 -0800 Subject: [PATCH 04/16] Done -> Ok in examples --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index eeb22fc..6a6ddbf 100644 --- a/README.md +++ b/README.md @@ -60,8 +60,8 @@ file.read_to_end(&mut bytes).unwrap(); let parsed = m3u8_rs::parse_playlist(&bytes); match parsed { - IResult::Done(i, Playlist::MasterPlaylist(pl)) => println!("Master playlist:\n{}", pl), - IResult::Done(i, Playlist::MediaPlaylist(pl)) => println!("Media playlist:\n{}", pl), + IResult::Ok((i, Playlist::MasterPlaylist(pl))) => println!("Master playlist:\n{}", pl), + IResult::Ok((i, Playlist::MediaPlaylist(pl))) => println!("Media playlist:\n{}", pl), IResult::Error(e) => panic!("Parsing error: \n{}", e), IResult::Incomplete(e) => panic!("Parsing error: \n{:?}", e), } From ed0d35b3a3e450e2070a6c5ab17e02fa9ea7fa2f Mon Sep 17 00:00:00 2001 From: Vadim Getmanshchuk Date: Wed, 26 Feb 2020 16:15:27 -0800 Subject: [PATCH 05/16] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6a6ddbf..2c62075 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ To use this library, add the following dependency to `Cargo.toml`: ```toml [dependencies] -m3u8-rs = "1.0.2" +m3u8-rs = "1.0.6" ``` And add the crate to `lib.rs` From fc9f45dd18d47b3a45fabf4a24e48954b3e72cc4 Mon Sep 17 00:00:00 2001 From: Vadim Getmanshchuk Date: Wed, 26 Feb 2020 16:21:06 -0800 Subject: [PATCH 06/16] Fix tag duplication The fix is addressing duplication for #EXT-X-KEY and #EXT-X-MAP tags in a media manifest produced --- src/playlist.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/playlist.rs b/src/playlist.rs index e87ad50..a8799da 100644 --- a/src/playlist.rs +++ b/src/playlist.rs @@ -479,6 +479,8 @@ impl MediaPlaylist { next_segment.uri = u; media_playlist.segments.push(next_segment); next_segment = MediaSegment::empty(); + encryption_key = None; + map = None; } _ => (), } From b9d377bfa45da07ee93ad3760c6b5bb04a37e70e Mon Sep 17 00:00:00 2001 From: Vadim Getmanshchuk Date: Wed, 26 Feb 2020 17:05:14 -0800 Subject: [PATCH 07/16] Upgraded macros to Nom 5 Changes: IResult::Done - IResult::Ok chain! -> do_parse! slightly different syntax with `?` -> opt! digit -> digit1 space? -> space0 multispace? -> multispace0 many0!() -> many0!(complete!()) take_until_either! -> is_not! take_until_and_consume! -> take_until! + take!(1) take_until_either_and_consume! -> is_not! + take!(1) --- src/lib.rs | 74 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 48 insertions(+), 26 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index f2149aa..e038f68 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -46,7 +46,7 @@ //! let mut file = std::fs::File::open("masterplaylist.m3u8").unwrap(); //! let mut bytes: Vec = Vec::new(); //! file.read_to_end(&mut bytes).unwrap(); -//! +//! //! if let IResult::Done(_, pl) = m3u8_rs::parse_master_playlist(&bytes) { //! println!("{:?}", pl); //! } @@ -61,7 +61,7 @@ //! use m3u8_rs::playlist::{MediaPlaylist, MediaPlaylistType, MediaSegment}; //! //! fn main() { -//! let playlist = MediaPlaylist { +//! let playlist = MediaPlaylist { //! version: 6, //! target_duration: 3.0, //! media_sequence: 338559, @@ -78,10 +78,10 @@ //! ], //! ..Default::default() //! }; -//! +//! //! //let mut v: Vec = Vec::new(); //! //playlist.write_to(&mut v).unwrap(); -//! +//! //! //let mut file = std::fs::File::open("playlist.m3u8").unwrap(); //! //playlist.write_to(&mut file).unwrap(); //! } @@ -92,7 +92,10 @@ extern crate nom; pub mod playlist; -use nom::*; +use nom::character::complete::{digit1, multispace0, space0 }; +use nom::{IResult}; +use nom::{ delimited,none_of,peek,is_not,complete,terminated,tag, + alt,do_parse,opt,named,map,map_res,eof,many0,take,take_until,char}; use std::str; use std::f32; @@ -244,8 +247,9 @@ named!(pub is_master_playlist_tag_line(&[u8]) -> Option<(bool, String)>, | map!(tag!("#EXT-X-MAP"), |t| (false, t)) | map!(tag!("#EXT-X-PROGRAM-DATE-TIME"), |t| (false, t)) | map!(tag!("#EXT-X-DATERANGE"), |t| (false, t)) - )) >> - consume_line >> + )) + >> consume_line + >> ( { tag.map(|(a,b)| (a, from_utf8_slice(b).unwrap())) } ) @@ -256,13 +260,16 @@ named!(pub is_master_playlist_tag_line(&[u8]) -> Option<(bool, String)>, // Master Playlist Tags // ----------------------------------------------------------------------------------------------- -pub fn parse_master_playlist_tags(input: &[u8]) -> IResult<&[u8], Vec> { +pub fn parse_master_playlist_tags(input: &[u8]) -> IResult<&[u8], Vec> { do_parse!(input, - tags: many0!(do_parse!(m:master_playlist_tag >> opt!(character::complete::multispace1) >> (m) )) >> opt!(eof!()) >> - ( { tags.reverse(); tags } ) + tags: many0!(complete!(do_parse!( m : master_playlist_tag >> multispace0 >> (m) ))) + >> opt!(eof!()) + >> + ( {let mut tags_rev: Vec = tags; tags_rev.reverse(); tags_rev } ) ) } + /// Contains all the tags required to parse a master playlist. #[derive(Debug)] pub enum MasterPlaylistTag { @@ -330,8 +337,9 @@ named!(pub session_key_tag, pub fn parse_media_playlist_tags(input: &[u8]) -> IResult<&[u8], Vec> { do_parse!(input, - tags: many0!(do_parse!(m:media_playlist_tag >> opt!(character::complete::multispace1) >> (m) )) >> opt!(eof!()) >> - ( { tags.reverse(); tags } ) + tags: many0!(complete!(do_parse!(m:media_playlist_tag >> multispace0 >> (m) ))) >> opt!(eof!()) + >> + ( {let mut tags_rev: Vec = tags; tags_rev.reverse(); tags_rev } ) ) } @@ -371,7 +379,11 @@ pub fn media_playlist_tag(input: &[u8]) -> IResult<&[u8], MediaPlaylistTag> { named!(pub playlist_type, map_res!( - map_res!(take_until!("\r\n"), str::from_utf8), + do_parse!( + p: map_res!(is_not!("\r\n"), str::from_utf8) + >> take!(1) + >> (p) + ), MediaPlaylistType::from_str ) ); @@ -416,7 +428,8 @@ named!(pub duration_title_tag<(f32, Option)>, do_parse!( duration: float >> opt!(tag!(",")) - >> title: opt!(map_res!(take_until!("\r\n,"), from_utf8_slice)) + >> title: opt!(map_res!(is_not!("\r\n,"), from_utf8_slice)) + >> take!(1) >> opt!(tag!(",")) >> (duration, title) @@ -432,12 +445,12 @@ named!(pub map, map!(key_value_pairs, Map::from_hashmap)); // ----------------------------------------------------------------------------------------------- named!(pub m3u_tag, - map_res!(tag!("#EXTM3U"), from_utf8_slice) + map_res!(tag!("#EXTM3U"), from_utf8_slice) ); named!(pub version_tag, do_parse!( - tag!("#EXT-X-VERSION:") >> version: map_res!(nom::character::streaming::digit1, str::from_utf8) >> + tag!("#EXT-X-VERSION:") >> version: map_res!(digit1, str::from_utf8) >> (version.parse().unwrap_or_default()) ) ); @@ -452,7 +465,9 @@ named!(pub ext_tag, do_parse!( tag!("#EXT-") >> tag: map_res!(take_until!(":"), from_utf8_slice) - >> rest: map_res!(take_until!("\r\n"), from_utf8_slice) + >> take!(1) + >> rest: map_res!(is_not!("\r\n"), from_utf8_slice) + >> take!(1) >> (ExtTag { tag: tag, rest: rest }) ) @@ -460,7 +475,8 @@ named!(pub ext_tag, named!(pub comment_tag, do_parse!( - tag!("#") >> text: map_res!(take_until!("\r\n"), from_utf8_slice) + tag!("#") >> text: map_res!(is_not!("\r\n"), from_utf8_slice) + >> take!(1) >> (text) ) ); @@ -471,7 +487,7 @@ named!(pub comment_tag, named!(pub key_value_pairs(&[u8]) -> HashMap, map!( - many0!(do_parse!(opt!(nom::character::streaming::space1) >> k:key_value_pair >> (k) )) + many0!(do_parse!(space0 >> k:key_value_pair >> (k) )) , |pairs: Vec<(String, String)>| { pairs.into_iter().collect() @@ -483,10 +499,10 @@ named!(pub key_value_pair(&[u8]) -> (String, String), do_parse!( peek!(none_of!("\r\n")) >> left: map_res!(take_until!("="), from_utf8_slice) + >> take!(1) >> right: alt!(quoted | unquoted) >> opt!(char!(',')) >> - (left, right) ) ); @@ -496,29 +512,35 @@ named!(pub quoted, ); named!(pub unquoted, - map_res!(take_until!(",\r\n"), from_utf8_slice) + map_res!(is_not!(",\r\n"), from_utf8_slice) ); named!(pub consume_line, - map_res!(take_until!("\r\n"), from_utf8_slice) + do_parse!( + l: map_res!(is_not!("\r\n"), from_utf8_slice) + >> take!(1) + >> + (l) + ) ); named!(pub number, - map_res!(map_res!(nom::character::streaming::digit1, str::from_utf8), str::FromStr::from_str) + map_res!(map_res!(digit1, str::from_utf8), str::FromStr::from_str) ); named!(pub byte_range_val, do_parse!( n: number - >> o: opt!(do_parse!(char!('@') >> n:number >> (n))) >> + >> o: opt!(do_parse!(char!('@') >> n:number >> (n) )) >> (ByteRange { length: n, offset: o }) ) ); named!(pub float, do_parse!( - left: map_res!(nom::character::streaming::digit1, str::from_utf8) - >> right_opt: opt!(do_parse!(char!('.') >> d:map_res!(nom::character::streaming::digit1, str::from_utf8) >> (d) )) >> + left: map_res!(digit1, str::from_utf8) + >> right_opt: opt!(do_parse!(char!('.') >> d:map_res!(digit1, str::from_utf8) >> (d) )) + >> ( match right_opt { Some(right) => { From e4e1717b0a25f6dc7a26d61d2cad23cf34d69c86 Mon Sep 17 00:00:00 2001 From: Vadim Getmanshchuk Date: Wed, 26 Feb 2020 17:17:13 -0800 Subject: [PATCH 08/16] Back ported 1.0.6 release --- src/playlist.rs | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/src/playlist.rs b/src/playlist.rs index a8799da..509d7f9 100644 --- a/src/playlist.rs +++ b/src/playlist.rs @@ -14,13 +14,13 @@ macro_rules! write_some_attribute_quoted { ($w:expr, $tag:expr, $o:expr) => ( if let &Some(ref v) = $o { write!($w, "{}=\"{}\"", $tag, v) } else { Ok(()) } ); -} +} macro_rules! write_some_attribute { ($w:expr, $tag:expr, $o:expr) => ( if let &Some(ref v) = $o { write!($w, "{}={}", $tag, v) } else { Ok(()) } ); -} +} macro_rules! bool_default_false { ($optional:expr) => ( @@ -29,7 +29,7 @@ macro_rules! bool_default_false { Some(_) | None => false, } ); -} +} /// [Playlist](https://tools.ietf.org/html/draft-pantos-http-live-streaming-19#section-4.1), /// can either be a `MasterPlaylist` or a `MediaPlaylist`. @@ -78,7 +78,6 @@ impl MasterPlaylist { let mut alternatives = vec![]; while let Some(tag) = tags.pop() { - match tag { MasterPlaylistTag::Version(v) => { master_playlist.version = v; @@ -138,7 +137,7 @@ impl MasterPlaylist { if let Some(ref start) = self.start { start.write_to(w)?; } - if self.independent_segments { + if self.independent_segments { writeln!(w, "#EXT-X-INDEPENDENT-SEGMENTS")?; } @@ -171,7 +170,7 @@ pub struct VariantStream { // pub bandwidth: String, pub average_bandwidth: Option, - pub codecs: String, + pub codecs: Option, pub resolution: Option, pub frame_rate: Option, pub audio: Option, @@ -190,7 +189,7 @@ impl VariantStream { uri: attrs.remove("URI").unwrap_or_else(String::new), bandwidth: attrs.remove("BANDWIDTH").unwrap_or_else(String::new), average_bandwidth: attrs.remove("AVERAGE-BANDWIDTH"), - codecs: attrs.remove("CODECS").unwrap_or_else(String::new), + codecs: attrs.remove("CODECS"), resolution: attrs.remove("RESOLUTION"), frame_rate: attrs.remove("FRAME-RATE"), audio: attrs.remove("AUDIO"), @@ -204,7 +203,7 @@ impl VariantStream { pub fn write_to(&self, w: &mut T) -> std::io::Result<()> { for alternative in &self.alternatives { - alternative.write_to(w)?; + alternative.write_to(w)?; } if self.is_i_frame { @@ -226,7 +225,7 @@ impl VariantStream { fn write_stream_inf_common_attributes(&self, w: &mut T) -> std::io::Result<()> { write!(w, "BANDWIDTH={}", &self.bandwidth)?; write_some_attribute!(w, ",AVERAGE-BANDWIDTH", &self.average_bandwidth)?; - write!(w, ",CODECS=\"{}\"", &self.codecs)?; + write_some_attribute_quoted!(w, ",CODECS", &self.codecs)?; write_some_attribute!(w, ",RESOLUTION", &self.resolution)?; write_some_attribute!(w, ",FRAME-RATE", &self.frame_rate)?; write_some_attribute_quoted!(w, ",VIDEO", &self.video) @@ -331,7 +330,7 @@ impl fmt::Display for AlternativeMediaType { &AlternativeMediaType::Subtitles => "SUBTITLES", &AlternativeMediaType::ClosedCaptions => "CLOSEDCAPTIONS", }) - } + } } @@ -420,7 +419,7 @@ impl MediaPlaylist { let mut map = None; while let Some(tag) = tags.pop() { - + match tag { MediaPlaylistTag::Version(v) => { media_playlist.version = v; @@ -496,25 +495,25 @@ impl MediaPlaylist { writeln!(w, "#EXT-X-VERSION:{}", self.version)?; writeln!(w, "#EXT-X-TARGETDURATION:{}", self.target_duration)?; - if self.media_sequence != 0 { + if self.media_sequence != 0 { writeln!(w, "#EXT-X-MEDIA-SEQUENCE:{}", self.media_sequence)?; } if self.discontinuity_sequence != 0 { writeln!(w, "#EXT-X-DISCONTINUITY-SEQUENCE:{}", self.discontinuity_sequence)?; } - if self.end_list { - writeln!(w, "#EXT-X-ENDLIST")?; + if self.end_list { + writeln!(w, "#EXT-X-ENDLIST")?; } if let Some(ref v) = self.playlist_type { writeln!(w, "#EXT-X-PLAYLIST-TYPE:{}", v)?; } - if self.i_frames_only { + if self.i_frames_only { writeln!(w, "#EXT-X-I-FRAMES-ONLY")?; } if let Some(ref start) = self.start { start.write_to(w)?; } - if self.independent_segments { + if self.independent_segments { writeln!(w, "#EXT-X-INDEPENDENT-SEGMENTS")?; } for segment in &self.segments { @@ -551,7 +550,7 @@ impl fmt::Display for MediaPlaylistType { &MediaPlaylistType::Event => "EVENT", &MediaPlaylistType::Vod => "VOD", }) - } + } } impl Default for MediaPlaylistType { @@ -599,8 +598,8 @@ impl MediaSegment { byte_range.write_value_to(w)?; write!(w, "\n")?; } - if self.discontinuity { - writeln!(w, "{}", "#EXT-X-DISCONTINUITY")?; + if self.discontinuity { + writeln!(w, "{}", "#EXT-X-DISCONTINUITY")?; } if let Some(ref key) = self.key { write!(w, "#EXT-X-KEY:")?; @@ -617,7 +616,7 @@ impl MediaSegment { } if let Some(ref v) = self.daterange { writeln!(w, "#EXT-X-DATERANGE:{}", v)?; - } + } write!(w, "#EXTINF:{},", self.duration)?; @@ -658,7 +657,7 @@ impl Key { } } - pub fn write_attributes_to(&self, w: &mut T) -> std::io::Result<()> { + pub fn write_attributes_to(&self, w: &mut T) -> std::io::Result<()> { write!(w, "METHOD={}", self.method)?; write_some_attribute_quoted!(w, ",URI", &self.uri)?; write_some_attribute!(w, ",IV", &self.iv)?; From f7587aa264dafb3dcf71f1c1a755e4444a526d3c Mon Sep 17 00:00:00 2001 From: Vadim Getmanshchuk Date: Wed, 26 Feb 2020 22:52:20 -0800 Subject: [PATCH 09/16] cleanup, fn map renamed Local `fn map` -> `fn extmap` that allows to use map from Nom directly, without `nom::combinator::` --- src/lib.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index e038f68..b044c46 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -46,7 +46,7 @@ //! let mut file = std::fs::File::open("masterplaylist.m3u8").unwrap(); //! let mut bytes: Vec = Vec::new(); //! file.read_to_end(&mut bytes).unwrap(); -//! +//! //! if let IResult::Done(_, pl) = m3u8_rs::parse_master_playlist(&bytes) { //! println!("{:?}", pl); //! } @@ -61,7 +61,7 @@ //! use m3u8_rs::playlist::{MediaPlaylist, MediaPlaylistType, MediaSegment}; //! //! fn main() { -//! let playlist = MediaPlaylist { +//! let playlist = MediaPlaylist { //! version: 6, //! target_duration: 3.0, //! media_sequence: 338559, @@ -78,10 +78,10 @@ //! ], //! ..Default::default() //! }; -//! +//! //! //let mut v: Vec = Vec::new(); //! //playlist.write_to(&mut v).unwrap(); -//! +//! //! //let mut file = std::fs::File::open("playlist.m3u8").unwrap(); //! //playlist.write_to(&mut file).unwrap(); //! } @@ -131,9 +131,8 @@ use playlist::*; /// } pub fn parse_playlist(input: &[u8]) -> IResult<&[u8], Playlist> { match is_master_playlist(input) { - // XXX: get rid of the local `map` to be able to `use` this - true => nom::combinator::map(parse_master_playlist, Playlist::MasterPlaylist)(input), - false => nom::combinator::map(parse_media_playlist, Playlist::MediaPlaylist)(input), + true => map(parse_master_playlist, Playlist::MasterPlaylist)(input), + false =>map(parse_media_playlist, Playlist::MediaPlaylist)(input), } } @@ -412,8 +411,8 @@ pub fn media_segment_tag(input: &[u8]) -> IResult<&[u8], SegmentTag> { map!(do_parse!(tag!("#EXTINF:") >> e:duration_title_tag >> (e)), |(a,b)| SegmentTag::Extinf(a,b)) | map!(do_parse!(tag!("#EXT-X-BYTERANGE:") >> r:byte_range_val >> (r)), SegmentTag::ByteRange) | map!(tag!("#EXT-X-DISCONTINUITY"), |_| SegmentTag::Discontinuity) - | map!(do_parse!(tag!("#EXT-X-KEY:") >> k:key >> (k)), SegmentTag::Key) - | map!(do_parse!(tag!("#EXT-X-MAP:") >> m:map >> (m)), SegmentTag::Map) + | map!(do_parse!(tag!("#EXT-X-KEY:") >> k: key >> (k)), SegmentTag::Key) + | map!(do_parse!(tag!("#EXT-X-MAP:") >> m: extmap >> (m)), SegmentTag::Map) | map!(do_parse!(tag!("#EXT-X-PROGRAM-DATE-TIME:") >> t:consume_line >> (t)), SegmentTag::ProgramDateTime) | map!(do_parse!(tag!("#EXT-X-DATE-RANGE:") >> t:consume_line >> (t)), SegmentTag::DateRange) @@ -438,7 +437,7 @@ named!(pub duration_title_tag<(f32, Option)>, named!(pub key, map!(key_value_pairs, Key::from_hashmap)); -named!(pub map, map!(key_value_pairs, Map::from_hashmap)); +named!(pub extmap, map!(key_value_pairs, Map::from_hashmap)); // ----------------------------------------------------------------------------------------------- // Basic tags From 350109e29acbcad7e915c24f2d9e8f848e27a291 Mon Sep 17 00:00:00 2001 From: Vadim Getmanshchuk Date: Wed, 26 Feb 2020 22:57:05 -0800 Subject: [PATCH 10/16] map cleanup, leftovers --- src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index b044c46..3de4051 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -166,7 +166,7 @@ pub fn parse_playlist_res(input: &[u8]) -> Result IResult<&[u8], MasterPlaylist> { - nom::combinator::map(parse_master_playlist_tags, MasterPlaylist::from_tags)(input) + map(parse_master_playlist_tags, MasterPlaylist::from_tags)(input) } /// Parse input as a master playlist @@ -180,7 +180,7 @@ pub fn parse_master_playlist_res(input: &[u8]) -> Result IResult<&[u8], MediaPlaylist> { - nom::combinator::map(parse_media_playlist_tags, MediaPlaylist::from_tags)(input) + map(parse_media_playlist_tags, MediaPlaylist::from_tags)(input) } /// Parse input as a media playlist From 4ed378772be7cb51dd72190b2b77d9e671d7c612 Mon Sep 17 00:00:00 2001 From: Vadim Getmanshchuk Date: Wed, 4 Mar 2020 20:12:50 -0800 Subject: [PATCH 11/16] added forgotten `use` for `map` --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index 3de4051..b3adb8f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -96,6 +96,7 @@ use nom::character::complete::{digit1, multispace0, space0 }; use nom::{IResult}; use nom::{ delimited,none_of,peek,is_not,complete,terminated,tag, alt,do_parse,opt,named,map,map_res,eof,many0,take,take_until,char}; +use nom::combinator::map; use std::str; use std::f32; From b810687652507381115315d81c067f7a0a9ff8ac Mon Sep 17 00:00:00 2001 From: Vadim Getmanshchuk Date: Fri, 6 Mar 2020 21:52:22 -0800 Subject: [PATCH 12/16] fixed issue with #EXTINF without titles in #EXTINF tag, when comma `,` after segment duration immediately follows by `\n` the `title` is not getting populated and is not printed with `writeln!` The problem is created by the nom 5 conversion, where `take_until_either_and_consume!` macro was replaced with `is_not!` + `take!(1)`. However, in case when `opt!(take_until_either_and_consume!` is used, everything gets way too hairy. I couldn't to expressed that in a fairly elegant manner and instead fixed the data presentation, when a new manifest is produced. While storing `\n` in a form of `title` is a nice hack, I'm also convinced this is a better approach to data handling. --- src/playlist.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/playlist.rs b/src/playlist.rs index 509d7f9..163ddfa 100644 --- a/src/playlist.rs +++ b/src/playlist.rs @@ -622,6 +622,8 @@ impl MediaSegment { if let Some(ref v) = self.title { writeln!(w, "{}", v)?; + } else { + write!(w, "\n"); } writeln!(w, "{}", self.uri) From 13405a09eb867718afe64efa7c3f455ac9b140b3 Mon Sep 17 00:00:00 2001 From: Rutger Schoorstra Date: Fri, 6 Mar 2020 20:56:14 +0100 Subject: [PATCH 13/16] Upgraded docs to Nom 5 --- examples/with_nom_result.rs | 13 +++++------- src/lib.rs | 40 +++++++++++++++++-------------------- 2 files changed, 23 insertions(+), 30 deletions(-) diff --git a/examples/with_nom_result.rs b/examples/with_nom_result.rs index 0eaa49f..8e73c9e 100644 --- a/examples/with_nom_result.rs +++ b/examples/with_nom_result.rs @@ -3,7 +3,6 @@ extern crate m3u8_rs; use m3u8_rs::playlist::{Playlist}; use std::io::Read; -use nom::IResult; fn main() { let mut file = std::fs::File::open("playlist.m3u8").unwrap(); @@ -13,9 +12,8 @@ fn main() { let parsed = m3u8_rs::parse_playlist(&bytes); let playlist = match parsed { - IResult::Done(i, playlist) => playlist, - IResult::Error(e) => panic!("Parsing error: \n{}", e), - IResult::Incomplete(e) => panic!("Parsing error: \n{:?}", e), + Result::Ok((i, playlist)) => playlist, + Result::Err(e) => panic!("Parsing error: \n{}", e), }; match playlist { @@ -32,9 +30,8 @@ fn main_alt() { let parsed = m3u8_rs::parse_playlist(&bytes); match parsed { - IResult::Done(i, Playlist::MasterPlaylist(pl)) => println!("Master playlist:\n{:?}", pl), - IResult::Done(i, Playlist::MediaPlaylist(pl)) => println!("Media playlist:\n{:?}", pl), - IResult::Error(e) => panic!("Parsing error: \n{}", e), - IResult::Incomplete(e) => panic!("Parsing error: \n{:?}", e), + Result::Ok((i, Playlist::MasterPlaylist(pl))) => println!("Master playlist:\n{:?}", pl), + Result::Ok((i, Playlist::MediaPlaylist(pl))) => println!("Media playlist:\n{:?}", pl), + Result::Err(e) => panic!("Parsing error: \n{}", e), } } diff --git a/src/lib.rs b/src/lib.rs index b3adb8f..b635def 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,7 @@ //! A library to parse m3u8 playlists (HTTP Live Streaming) [link] //! (https://tools.ietf.org/html/draft-pantos-http-live-streaming-19). //! -//! #Examples +//! # Examples //! //! Parsing a playlist and let the parser figure out if it's a media or master playlist. //! @@ -17,19 +17,10 @@ //! let mut bytes: Vec = Vec::new(); //! file.read_to_end(&mut bytes).unwrap(); //! -//! // Option 1: fn parse_playlist_res(input) -> Result -//! match m3u8_rs::parse_playlist_res(&bytes) { -//! Ok(Playlist::MasterPlaylist(pl)) => println!("Master playlist:\n{:?}", pl), -//! Ok(Playlist::MediaPlaylist(pl)) => println!("Media playlist:\n{:?}", pl), -//! Err(e) => println!("Error: {:?}", e) -//! } -//! -//! // Option 2: fn parse_playlist(input) -> IResult<_, Playlist, _> //! match m3u8_rs::parse_playlist(&bytes) { -//! IResult::Done(i, Playlist::MasterPlaylist(pl)) => println!("Master playlist:\n{:?}", pl), -//! IResult::Done(i, Playlist::MediaPlaylist(pl)) => println!("Media playlist:\n{:?}", pl), -//! IResult::Error(e) => panic!("Parsing error: \n{}", e), -//! IResult::Incomplete(e) => panic!("Parsing error: \n{:?}", e), +//! Result::Ok((i, Playlist::MasterPlaylist(pl))) => println!("Master playlist:\n{:?}", pl), +//! Result::Ok((i, Playlist::MediaPlaylist(pl))) => println!("Media playlist:\n{:?}", pl), +//! Result::Err(e) => panic!("Parsing error: \n{}", e), //! } //! } //! ``` @@ -47,7 +38,7 @@ //! let mut bytes: Vec = Vec::new(); //! file.read_to_end(&mut bytes).unwrap(); //! -//! if let IResult::Done(_, pl) = m3u8_rs::parse_master_playlist(&bytes) { +//! if let Result::Ok((_, pl)) = m3u8_rs::parse_master_playlist(&bytes) { //! println!("{:?}", pl); //! } //! } @@ -110,10 +101,14 @@ use playlist::*; // Playlist parser // ----------------------------------------------------------------------------------------------- -/// Parse a m3u8 playlist. +/// Parse an m3u8 playlist. /// -/// #Examples +/// # Examples /// +/// ``` +/// use std::io::Read; +/// use m3u8_rs::playlist::{Playlist}; +/// /// let mut file = std::fs::File::open("playlist.m3u8").unwrap(); /// let mut bytes: Vec = Vec::new(); /// file.read_to_end(&mut bytes).unwrap(); @@ -121,15 +116,15 @@ use playlist::*; /// let parsed = m3u8_rs::parse_playlist(&bytes); /// /// let playlist = match parsed { -/// IResult::Done(i, playlist) => playlist, -/// IResult::Error(e) => panic!("Parsing error: \n{}", e), -/// IResult::Incomplete(e) => panic!("Parsing error: \n{:?}", e), +/// Result::Ok((i, playlist)) => playlist, +/// Result::Err(e) => panic!("Parsing error: \n{}", e), /// }; /// /// match playlist { /// Playlist::MasterPlaylist(pl) => println!("Master playlist:\n{:?}", pl), /// Playlist::MediaPlaylist(pl) => println!("Media playlist:\n{:?}", pl), /// } +/// ``` pub fn parse_playlist(input: &[u8]) -> IResult<&[u8], Playlist> { match is_master_playlist(input) { true => map(parse_master_playlist, Playlist::MasterPlaylist)(input), @@ -137,9 +132,10 @@ pub fn parse_playlist(input: &[u8]) -> IResult<&[u8], Playlist> { } } -/// Parse a m3u8 playlist just like `parse_playlist`. This returns a Result. -/// -/// #Examples +/// Parses an m3u8 playlist just like `parse_playlist`, except that this returns an [std::result::Result](std::result::Result) instead of a [nom::IResult](https://docs.rs/nom/1.2.3/nom/enum.IResult.html). +/// However, since [nom::IResult](nom::IResult) is now an [alias to Result](https://github.com/Geal/nom/blob/master/doc/upgrading_to_nom_5.md), this is no longer needed. +/// +/// # Examples /// /// ``` /// use m3u8_rs::playlist::{Playlist}; From ab9c554eb44637ac8e303ffb5a346c931c17d628 Mon Sep 17 00:00:00 2001 From: Rutger Schoorstra Date: Fri, 6 Mar 2020 20:56:51 +0100 Subject: [PATCH 14/16] Upgraded tests to Nom 5 --- tests/lib.rs | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/tests/lib.rs b/tests/lib.rs index 46abc68..60f02c5 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -42,7 +42,7 @@ fn print_parse_playlist_test(playlist_name: &str) -> bool { println!("Parsing playlist file: {:?}", playlist_name); let parsed = parse_playlist(input.as_bytes()); - if let IResult::Done(i,o) = parsed { + if let Result::Ok((i,o)) = parsed { println!("{:?}", o); true } @@ -117,9 +117,9 @@ fn playlist_types() { let input = getm3u(path); let is_master = is_master_playlist(input.as_bytes()); - assert!(path.to_lowercase().contains("master") == is_master); - println!("{:?} = {:?}", path, is_master); + + assert!(path.to_lowercase().contains("master") == is_master); } } @@ -147,7 +147,7 @@ fn test_key_value_pairs_trailing_equals() { fn test_key_value_pairs_multiple_quoted_values() { assert_eq!( key_value_pairs(b"BANDWIDTH=86000,URI=\"low/iframe.m3u8\",PROGRAM-ID=1,RESOLUTION=\"1x1\",VIDEO=1\nrest"), - IResult::Done( + Result::Ok(( "\nrest".as_bytes(), vec![ ("BANDWIDTH".to_string(), "86000".to_string()), @@ -156,7 +156,7 @@ fn test_key_value_pairs_multiple_quoted_values() { ("RESOLUTION".to_string(), "1x1".to_string()), ("VIDEO".to_string(), "1".to_string()) ].into_iter().collect::>() - ) + )) ); } @@ -177,10 +177,10 @@ fn test_key_value_pairs() { fn test_key_value_pair() { assert_eq!( key_value_pair(b"PROGRAM-ID=1,rest"), - IResult::Done( + Result::Ok(( "rest".as_bytes(), ("PROGRAM-ID".to_string(), "1".to_string()) - ) + )) ); } @@ -188,7 +188,7 @@ fn test_key_value_pair() { fn comment() { assert_eq!( comment_tag(b"#Hello\nxxx"), - IResult::Done("xxx".as_bytes(), "Hello".to_string()) + Result::Ok(("xxx".as_bytes(), "Hello".to_string())) ); } @@ -196,7 +196,9 @@ fn comment() { fn quotes() { assert_eq!( quoted(b"\"value\"rest"), - IResult::Done("rest".as_bytes(), "value".to_string()) + Result::Ok(("rest".as_bytes(), "value".to_string())) + ); +} ); } @@ -210,7 +212,7 @@ fn consume_empty_line() { fn float_() { assert_eq!( float(b"33.22rest"), - IResult::Done("rest".as_bytes(), 33.22f32) + Result::Ok(("rest".as_bytes(), 33.22f32)) ); } @@ -218,7 +220,7 @@ fn float_() { fn float_no_decimal() { assert_eq!( float(b"33rest"), - IResult::Done("rest".as_bytes(), 33f32) + Result::Ok(("rest".as_bytes(), 33f32)) ); } @@ -226,7 +228,7 @@ fn float_no_decimal() { fn float_should_ignore_trailing_dot() { assert_eq!( float(b"33.rest"), - IResult::Done(".rest".as_bytes(), 33f32) + Result::Ok((".rest".as_bytes(), 33f32)) ); } @@ -234,7 +236,7 @@ fn float_should_ignore_trailing_dot() { fn parse_duration_title() { assert_eq!( duration_title_tag(b"2.002,title\nrest"), - IResult::Done("rest".as_bytes(), (2.002f32, Some("title".to_string()))) + Result::Ok(("rest".as_bytes(), (2.002f32, Some("title".to_string())))) ); } @@ -279,7 +281,7 @@ fn create_and_parse_master_playlist_full() { uri: "masterplaylist-uri".into(), bandwidth: "10010010".into(), average_bandwidth: Some("10010010".into()), - codecs: "TheCODEC".into(), + codecs: Some("TheCODEC".into()), resolution: Some("1000x3000".into()), frame_rate: Some("60".into()), audio: Some("audio".into()), From 100a57078a30e1a8a371ad4c85d4b07abfce03f5 Mon Sep 17 00:00:00 2001 From: Rutger Schoorstra Date: Fri, 6 Mar 2020 20:59:15 +0100 Subject: [PATCH 15/16] Fix failed test on CLRF --- src/lib.rs | 9 ++++----- tests/lib.rs | 22 +++++++++++++++++++--- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index b635def..dc7132e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -88,7 +88,7 @@ use nom::{IResult}; use nom::{ delimited,none_of,peek,is_not,complete,terminated,tag, alt,do_parse,opt,named,map,map_res,eof,many0,take,take_until,char}; use nom::combinator::map; - +use nom::character::complete::{line_ending}; use std::str; use std::f32; use std::string; @@ -513,10 +513,9 @@ named!(pub unquoted, named!(pub consume_line, do_parse!( - l: map_res!(is_not!("\r\n"), from_utf8_slice) - >> take!(1) - >> - (l) + line: map_res!(is_not!("\r\n"), from_utf8_slice) + >> line_ending + >> (line) ) ); diff --git a/tests/lib.rs b/tests/lib.rs index 60f02c5..a41f0f1 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -199,13 +199,29 @@ fn quotes() { Result::Ok(("rest".as_bytes(), "value".to_string())) ); } + +#[test] +fn consume_line_empty() { + assert_eq!( + consume_line(b"\r\nrest"), + Result::Err(nom::Err::Error(("\r\nrest".as_bytes(), nom::error::ErrorKind::IsNot))) ); } #[test] -fn consume_empty_line() { - let line = consume_line(b"\r\nrest"); - println!("{:?}", line); +fn consume_line_n() { + assert_eq!( + consume_line(b"before\nrest"), + Result::Ok(("rest".as_bytes(), "before".into())) + ); +} + +#[test] +fn consume_line_rn() { + assert_eq!( + consume_line(b"before\r\nrest"), + Result::Ok(("rest".as_bytes(), "before".into())) + ); } #[test] From 7a882e5df0eaa6bfb967709ae2bf1df6afe9c487 Mon Sep 17 00:00:00 2001 From: Vadim Getmanshchuk Date: Sat, 21 Mar 2020 21:23:03 -0700 Subject: [PATCH 16/16] fixed warning: unused `std::result::Result` ``` warning: unused `std::result::Result` that must be used ``` I forgot `?` for the error propagation --- src/playlist.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/playlist.rs b/src/playlist.rs index 163ddfa..fba1615 100644 --- a/src/playlist.rs +++ b/src/playlist.rs @@ -623,7 +623,7 @@ impl MediaSegment { if let Some(ref v) = self.title { writeln!(w, "{}", v)?; } else { - write!(w, "\n"); + write!(w, "\n")?; } writeln!(w, "{}", self.uri)