<commit_before>extern crate regex;

use super::{Registry, Module, HandleData};
use lib::{self, Error, Request, db};
use lib::downloader::Filename;
use std::fs::remove_file;
use std::fs::remove_dir_all;
use std::fs::create_dir;
use std::path::Path;

use CODE_IN_PROGRESS;
use CONFIG;

macro_rules! regex(
    ($s:expr) => (regex::Regex::new($s).unwrap());
);

macro_rules! condition(
    ($e:expr,$y:expr) => (match $y { true => {$e;}, false => ()} );
);

lazy_static! {
// https://regex101.com/r/lZ6lC1/2
// we need to remove the / escaping!
    pub static ref REGEX_VIDEO: regex::Regex = regex!(r"https?://(www\.)?(youtube\.[a-z]{2,3}/watch\?(feature=player_embedded&)?v=[a-zA-Z0-9_-]+|youtu\.be/[a-zA-Z0-9_-]+)");
// https://regex101.com/r/aV1jS1/1
    pub static ref REGEX_PLAYLIST: regex::Regex = regex!(r"https?://(www\.)?youtube\.[a-z]{2,3}/(watch\?(feature=player_embedded&)?v=[a-zA-Z0-9_-]+.*&list=[A-Za-z0-9_-]+|playlist\?list=[a-zA-Z0-9_-]+)");
}

/// Init youtube handler, registering it
pub fn init(registry: &mut Registry) {
    registry.register(Module {
        checker: Box::new(checker_file),
        handler: Box::new(handle_file),
    });
    registry.register(Module {
        checker: Box::new(checker_playlist),
        handler: Box::new(handle_playlist),
    });
}

/// Check if the data matches for the file handler
fn checker_file(data: &Request) -> bool {
    if data.playlist {
        return false;
    }
    REGEX_VIDEO.is_match(&data.url)
}

/// Check if the data matches for the playlist handler
fn checker_playlist(data: &Request) -> bool {
    if !data.playlist {
        return false;
    }
    REGEX_PLAYLIST.is_match(&data.url)
}

/// Playlist request handler for youtube
/// If compression is enabled all files will be downloaded into one dir and zipped afterwards
/// Otherwise for every entry in the playlist a new query is created. These will be handled one after another,
/// creating a file per entry.
fn handle_playlist(handle_db: &mut HandleData, request: &mut Request) -> Result<(), Error> {
    trace!("youtube playlist handler started");
    db::set_query_code(&mut request.get_conn(), &request.qid, &CODE_IN_PROGRESS);

    let name = Filename {
            name: try!(handle_db.downloader.get_playlist_name(&request.url)),
            extension: "zip".to_string(),
    };
    let mut counter: i32 = 0;

    db::update_steps(&mut request.get_conn(), &request.qid, 1,3);
    trace!("crawling ids");
    let file_ids = try!(handle_db.downloader.get_playlist_ids(request));

    if request.compress {

        let save_path = try!(lib::format_save_path(&request.path, &name));;
        request.temp_path.push(request.qid.to_string());
        request.path = request.temp_path.clone();
        try!(create_dir(&request.path));
        let mut warnings = false;
        let mut current_url: String;
        let mut failed_log: String = String::from("Following urls couldn't be downloaded: \n");

        db::update_steps(&mut request.get_conn(), &request.qid, 2,3);
        let max_steps = file_ids.len() as i32;
        for id in file_ids.iter() {
            counter += 1;
            db::update_steps(&mut request.get_conn(), &request.qid, counter,max_steps);
            current_url = String::from("https://www.youtube.com/watch?v=");
            current_url.push_str(&id);
            request.url = current_url.clone();
            request.internal_id = counter as u64;
            match handle_file_int(handle_db, &request) {
                Err(e) => {
                    warn!("error downloading {}: {:?}", id, e);
                    failed_log.push_str(&format!("{} {:?}\n", current_url, e));
                    warnings = true;
                }
                Ok(_) => {}
            }
        }

        if warnings {
            debug!("found warnings");
            db::add_query_error(&mut request.get_conn(), &request.qid, &failed_log);
        }

        db::update_steps(&mut request.get_conn(), &request.qid, 3,3);
        trace!("starting zipping");
        try!(lib::zip_folder(&request.temp_path, &save_path));
        trace!("adding file");
        handle_db.addFile(&save_path, &name.full_name());
        trace!("removing dir {}",request.path.to_string_lossy());
        try!(remove_dir_all(&request.path));
        trace!("updating state");

    } else {
//        let file_ids = try!(handle_db.downloader.get_playlist_ids(request));

        return Err(Error::InternalError("Uncompressed playlists are not supported atm!".to_string()));
    }

    Ok(())
}

/// Handle file request
fn handle_file(handle_db: &mut HandleData, request: &mut Request) -> Result<(), Error> {
    try!(handle_file_int(handle_db, &request));

    Ok(())
}

/// Real handler, avoiding mut Request borrows for internal use
fn handle_file_int(handle_db: &mut HandleData, request: &Request) -> Result<(), Error> {
    trace!("youtube file handler started");
    if request.quality < 0 {
        try!(handle_audio(handle_db,request))
    } else {
        try!(handle_video(handle_db,request))
    }

    Ok(())
}

/// Handler for videos
fn handle_video(hdb: &mut HandleData, request: &Request) -> Result<(), Error> {
    condition!(db::set_query_code(&mut request.get_conn(), &request.qid, &CODE_IN_PROGRESS),!request.playlist);
    let mut temp_file_v = request.temp_path.clone();
    temp_file_v.push(request.internal_id.to_string());
    hdb.push(&temp_file_v);

    let mut dmca = false;

    trace!("Retriving name");
    let name = try!(get_name(&hdb, &request, true, true, &temp_file_v, &mut dmca));
    let origin_name = name.full_name();
    debug!("name: {}.{}", &name.name, &name.extension);

    let save_file = try!(lib::format_save_path(&request.path, &name));

    if dmca {
        try!(lib::move_file(&temp_file_v, &save_file));
        hdb.addFile(save_file.as_ref(), &origin_name);
        hdb.pop();
        return Ok(());
    }

    let audio_id = if name.extension == "mp4" {
        CONFIG.codecs.yt.audio_normal_mp4
    } else {
        CONFIG.codecs.yt.audio_normal_webm
    };

    condition!(db::update_steps(&mut request.get_conn(), &request.qid, 1,3),!request.playlist);
    trace!("downloading video");
    try!(hdb.downloader.download_file(&request, &temp_file_v, &request.quality.to_string()));

    condition!(db::update_steps(&mut request.get_conn(), &request.qid, 2,3),!request.playlist);
    let mut temp_file_a = request.temp_path.clone();
    temp_file_a.push(format!("{}a", request.internal_id));
    hdb.push(&temp_file_a);
    trace!("downloading audio");
    try!(hdb.downloader.download_file(&request, &temp_file_a, &audio_id.to_string()));

    condition!(db::update_steps(&mut request.get_conn(), &request.qid, 3,3),!request.playlist);
    trace!("merging");
    try!(hdb.converter.merge_files(&request.internal_id, &temp_file_v, &temp_file_a, &save_file,&mut request.get_conn()));
    if !request.playlist {
        hdb.addFile(&save_file, &origin_name);
    }
    try!(remove_file(&temp_file_a));
    hdb.pop();
    try!(remove_file(&temp_file_v));
    hdb.pop();

    Ok(())
}

/// Handler for audios
fn handle_audio(hdb: &mut HandleData, request: &Request) -> Result<(), Error> {
    condition!(db::set_query_code(&mut request.get_conn(), &request.qid, &CODE_IN_PROGRESS),!request.playlist);
    let mut dmca = false;
    let quality = try!(get_audio_quality(&request.quality));

    let mut temp_file_v = request.temp_path.clone();
    temp_file_v.push(request.internal_id.to_string());
    hdb.push(&temp_file_v);

    condition!(db::update_steps(&mut request.get_conn(), &request.qid, 1,3),!request.playlist);
    let mut name = try!(get_name(&hdb, &request, false, false, &temp_file_v, &mut dmca));

    if dmca {
        let save_file = try!(lib::format_save_path(&request.path, &name));
        try!(lib::move_file(&temp_file_v, &save_file));
        hdb.pop();
        hdb.addFile(&save_file, &name.full_name());
        return Ok(());
    }

    condition!(db::update_steps(&mut request.get_conn(), &request.qid, 2,3),!request.playlist);
    try!(hdb.downloader.download_file(&request, &temp_file_v, &quality));

    if request.quality == CONFIG.codecs.audio_raw ||
       request.quality == CONFIG.codecs.audio_source_hq {
        name.extension = String::from("m4a");
    } else if request.quality == CONFIG.codecs.audio_mp3 {
        name.extension = String::from("mp3");
    }

    condition!(db::update_steps(&mut request.get_conn(), &request.qid, 3,3),!request.playlist);
    let file = try!(lib::format_save_path(&request.path, &name));
    try!(hdb.converter.extract_audio(&request.internal_id,
                                     &temp_file_v,
                                     &file,
                                     request.quality == CONFIG.codecs.audio_mp3,
                                     &mut request.get_conn()));
    try!(remove_file(&temp_file_v));
    hdb.pop();
    if !request.playlist {
        hdb.addFile(&file, &name.full_name());
    }

    Ok(())
}

/// Retrive name of youtube video
/// If we should encounter an DMCA we'll let the lib call handle this
/// (if enabled), this gives us a downloaded file and the name
/// In this case we only need to move the file to it's destination
/// If use_quality is true, the name will be retrived for the request's quality
fn get_name<'a>(hdb: &HandleData,
                request: &Request,
                use_qality: bool,
                video: bool,
                path: &Path,
                dmca: &'a mut bool)
                -> Result<Filename, Error> {
    let quality = if use_qality {
        Some(request.quality.to_string())
    } else {
        None
    };
    Ok(match hdb.downloader.get_file_name(&request.url, quality) { // get filename
        Ok(v) => {
            v
        }
        Err(Error::DMCAError) => {
            // now request via lib.. // k if( k == Err(DownloadError::DMCAError) )
            info!("DMCA error!");
            if CONFIG.general.lib_use {
                if !request.compress {
                    db::set_query_code(&mut request.get_conn(), &request.internal_id, &CODE_IN_PROGRESS);
                }
                match hdb.downloader.lib_request_video(1,
                                                       0,
                                                       path,
                                                       request,
                                                       &request.quality.to_string(),
                                                       video) {
                    Err(err) => {
                        warn!("lib-call error {:?}", err);
                        return Err(err);
                    }
                    Ok(v) => {
                        *dmca = true;
                        v
                    }
                }
            } else {
                return Err(Error::NotAvailable);
            }
        }
        Err(e) => {
            // unknown error / restricted source etc.. abort
            error!("Unknown error: {:?}", e);
            return Err(e);
        }
    })
}

/// Get audio quality to use
fn get_audio_quality(id: &i16) -> Result<String, Error> {
    let id = if *id == CONFIG.codecs.audio_raw {
        CONFIG.codecs.yt.audio_normal_mp4
    } else if *id == CONFIG.codecs.audio_source_hq {
        CONFIG.codecs.yt.audio_normal_mp4
    } else if *id == CONFIG.codecs.audio_mp3 {
        CONFIG.codecs.yt.audio_normal_mp4
    } else {
        return Err(Error::InputError("Unknown audio quality!".to_string()));
    };
    Ok(id.to_string())
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn regex() {
        assert!(REGEX_VIDEO.is_match(r"http://youtu.be/yke-rFCOUyo"));
        assert!(REGEX_VIDEO.is_match(r"https://www.youtube.com/watch?v=IO-_EoRSpUA"));
        assert!(REGEX_VIDEO.is_match(r"https://www.youtube.com/watch?v=IO-_EoRSpUA&list=PL6DA1502C5DDC0317&index=21"));
        assert!(!REGEX_VIDEO.is_match(r"https://www.youtube.com/playlist?list=PLJYiF4qyO-fpaYWfTylcFu3VGUCVK4xfz"));

        assert!(REGEX_PLAYLIST.is_match(r"https://www.youtube.com/playlist?list=PLBCC15D0E3ED5E67A"));
        assert!(REGEX_PLAYLIST.is_match(r"https://www.youtube.com/watch?v=fMlZFRus5eQ&index=2&list=PLBCC15D0E3ED5E67A"));
        assert!(REGEX_PLAYLIST.is_match(r"https://www.youtube.com/watch?v=IO-_EoRSpUA&list=PL6DA1502C5DDC0317&index=21"));
        assert!(!REGEX_PLAYLIST.is_match(r"https://www.youtube.com/watch?v=IO-_EoRSpUA"));
    }

}
<commit_msg>fixed a bug in mod youtube not recognizing mobile urls<commit_after>extern crate regex;

use super::{Registry, Module, HandleData};
use lib::{self, Error, Request, db};
use lib::downloader::Filename;
use std::fs::remove_file;
use std::fs::remove_dir_all;
use std::fs::create_dir;
use std::path::Path;

use CODE_IN_PROGRESS;
use CONFIG;

macro_rules! regex(
    ($s:expr) => (regex::Regex::new($s).unwrap());
);

macro_rules! condition(
    ($e:expr,$y:expr) => (match $y { true => {$e;}, false => ()} );
);

lazy_static! {
// https://regex101.com/r/lZ6lC1/3
// we need to remove the / escaping!
    pub static ref REGEX_VIDEO: regex::Regex = regex!(r"https?://(www\.|m\.)?(youtube\.[a-z]{2,3}/watch\?(feature=player_embedded&)?(list=[a-zA-Z0-9_-]+&)?v=[a-zA-Z0-9_-]+|youtu\.be/[a-zA-Z0-9_-]+)");
// https://regex101.com/r/aV1jS1/2
    pub static ref REGEX_PLAYLIST: regex::Regex = regex!(r"https?://(www\.|m\.)?youtube\.[a-z]{2,4}/(watch\?(feature=player_embedded&)?(v=[a-zA-Z0-9_-]+.*&)?list=[A-Za-z0-9_-]+|playlist\?list=[a-zA-Z0-9_-]+)");
}

/// Init youtube handler, registering it
pub fn init(registry: &mut Registry) {
    registry.register(Module {
        checker: Box::new(checker_file),
        handler: Box::new(handle_file),
    });
    registry.register(Module {
        checker: Box::new(checker_playlist),
        handler: Box::new(handle_playlist),
    });
}

/// Check if the data matches for the file handler
fn checker_file(data: &Request) -> bool {
    if data.playlist {
        return false;
    }
    REGEX_VIDEO.is_match(&data.url)
}

/// Check if the data matches for the playlist handler
fn checker_playlist(data: &Request) -> bool {
    if !data.playlist {
        return false;
    }
    REGEX_PLAYLIST.is_match(&data.url)
}

/// Playlist request handler for youtube
/// If compression is enabled all files will be downloaded into one dir and zipped afterwards
/// Otherwise for every entry in the playlist a new query is created. These will be handled one after another,
/// creating a file per entry.
fn handle_playlist(handle_db: &mut HandleData, request: &mut Request) -> Result<(), Error> {
    trace!("youtube playlist handler started");
    db::set_query_code(&mut request.get_conn(), &request.qid, &CODE_IN_PROGRESS);

    let name = Filename {
            name: try!(handle_db.downloader.get_playlist_name(&request.url)),
            extension: "zip".to_string(),
    };
    let mut counter: i32 = 0;

    db::update_steps(&mut request.get_conn(), &request.qid, 1,3);
    trace!("crawling ids");
    let file_ids = try!(handle_db.downloader.get_playlist_ids(request));

    if request.compress {

        let save_path = try!(lib::format_save_path(&request.path, &name));;
        request.temp_path.push(request.qid.to_string());
        request.path = request.temp_path.clone();
        try!(create_dir(&request.path));
        let mut warnings = false;
        let mut current_url: String;
        let mut failed_log: String = String::from("Following urls couldn't be downloaded: \n");

        db::update_steps(&mut request.get_conn(), &request.qid, 2,3);
        let max_steps = file_ids.len() as i32;
        for id in file_ids.iter() {
            counter += 1;
            db::update_steps(&mut request.get_conn(), &request.qid, counter,max_steps);
            current_url = String::from("https://www.youtube.com/watch?v=");
            current_url.push_str(&id);
            request.url = current_url.clone();
            request.internal_id = counter as u64;
            match handle_file_int(handle_db, &request) {
                Err(e) => {
                    warn!("error downloading {}: {:?}", id, e);
                    failed_log.push_str(&format!("{} {:?}\n", current_url, e));
                    warnings = true;
                }
                Ok(_) => {}
            }
        }

        if warnings {
            debug!("found warnings");
            db::add_query_error(&mut request.get_conn(), &request.qid, &failed_log);
        }

        db::update_steps(&mut request.get_conn(), &request.qid, 3,3);
        trace!("starting zipping");
        try!(lib::zip_folder(&request.temp_path, &save_path));
        trace!("adding file");
        handle_db.addFile(&save_path, &name.full_name());
        trace!("removing dir {}",request.path.to_string_lossy());
        try!(remove_dir_all(&request.path));
        trace!("updating state");

    } else {
//        let file_ids = try!(handle_db.downloader.get_playlist_ids(request));

        return Err(Error::InternalError("Uncompressed playlists are not supported atm!".to_string()));
    }

    Ok(())
}

/// Handle file request
fn handle_file(handle_db: &mut HandleData, request: &mut Request) -> Result<(), Error> {
    try!(handle_file_int(handle_db, &request));

    Ok(())
}

/// Real handler, avoiding mut Request borrows for internal use
fn handle_file_int(handle_db: &mut HandleData, request: &Request) -> Result<(), Error> {
    trace!("youtube file handler started");
    if request.quality < 0 {
        try!(handle_audio(handle_db,request))
    } else {
        try!(handle_video(handle_db,request))
    }

    Ok(())
}

/// Handler for videos
fn handle_video(hdb: &mut HandleData, request: &Request) -> Result<(), Error> {
    condition!(db::set_query_code(&mut request.get_conn(), &request.qid, &CODE_IN_PROGRESS),!request.playlist);
    let mut temp_file_v = request.temp_path.clone();
    temp_file_v.push(request.internal_id.to_string());
    hdb.push(&temp_file_v);

    let mut dmca = false;

    trace!("Retriving name");
    let name = try!(get_name(&hdb, &request, true, true, &temp_file_v, &mut dmca));
    let origin_name = name.full_name();
    debug!("name: {}.{}", &name.name, &name.extension);

    let save_file = try!(lib::format_save_path(&request.path, &name));

    if dmca {
        try!(lib::move_file(&temp_file_v, &save_file));
        hdb.addFile(save_file.as_ref(), &origin_name);
        hdb.pop();
        return Ok(());
    }

    let audio_id = if name.extension == "mp4" {
        CONFIG.codecs.yt.audio_normal_mp4
    } else {
        CONFIG.codecs.yt.audio_normal_webm
    };

    condition!(db::update_steps(&mut request.get_conn(), &request.qid, 1,3),!request.playlist);
    trace!("downloading video");
    try!(hdb.downloader.download_file(&request, &temp_file_v, &request.quality.to_string()));

    condition!(db::update_steps(&mut request.get_conn(), &request.qid, 2,3),!request.playlist);
    let mut temp_file_a = request.temp_path.clone();
    temp_file_a.push(format!("{}a", request.internal_id));
    hdb.push(&temp_file_a);
    trace!("downloading audio");
    try!(hdb.downloader.download_file(&request, &temp_file_a, &audio_id.to_string()));

    condition!(db::update_steps(&mut request.get_conn(), &request.qid, 3,3),!request.playlist);
    trace!("merging");
    try!(hdb.converter.merge_files(&request.internal_id, &temp_file_v, &temp_file_a, &save_file,&mut request.get_conn()));
    if !request.playlist {
        hdb.addFile(&save_file, &origin_name);
    }
    try!(remove_file(&temp_file_a));
    hdb.pop();
    try!(remove_file(&temp_file_v));
    hdb.pop();

    Ok(())
}

/// Handler for audios
fn handle_audio(hdb: &mut HandleData, request: &Request) -> Result<(), Error> {
    condition!(db::set_query_code(&mut request.get_conn(), &request.qid, &CODE_IN_PROGRESS),!request.playlist);
    let mut dmca = false;
    let quality = try!(get_audio_quality(&request.quality));

    let mut temp_file_v = request.temp_path.clone();
    temp_file_v.push(request.internal_id.to_string());
    hdb.push(&temp_file_v);

    condition!(db::update_steps(&mut request.get_conn(), &request.qid, 1,3),!request.playlist);
    let mut name = try!(get_name(&hdb, &request, false, false, &temp_file_v, &mut dmca));

    if dmca {
        let save_file = try!(lib::format_save_path(&request.path, &name));
        try!(lib::move_file(&temp_file_v, &save_file));
        hdb.pop();
        hdb.addFile(&save_file, &name.full_name());
        return Ok(());
    }

    condition!(db::update_steps(&mut request.get_conn(), &request.qid, 2,3),!request.playlist);
    try!(hdb.downloader.download_file(&request, &temp_file_v, &quality));

    if request.quality == CONFIG.codecs.audio_raw ||
       request.quality == CONFIG.codecs.audio_source_hq {
        name.extension = String::from("m4a");
    } else if request.quality == CONFIG.codecs.audio_mp3 {
        name.extension = String::from("mp3");
    }

    condition!(db::update_steps(&mut request.get_conn(), &request.qid, 3,3),!request.playlist);
    let file = try!(lib::format_save_path(&request.path, &name));
    try!(hdb.converter.extract_audio(&request.internal_id,
                                     &temp_file_v,
                                     &file,
                                     request.quality == CONFIG.codecs.audio_mp3,
                                     &mut request.get_conn()));
    try!(remove_file(&temp_file_v));
    hdb.pop();
    if !request.playlist {
        hdb.addFile(&file, &name.full_name());
    }

    Ok(())
}

/// Retrive name of youtube video
/// If we should encounter an DMCA we'll let the lib call handle this
/// (if enabled), this gives us a downloaded file and the name
/// In this case we only need to move the file to it's destination
/// If use_quality is true, the name will be retrived for the request's quality
fn get_name<'a>(hdb: &HandleData,
                request: &Request,
                use_qality: bool,
                video: bool,
                path: &Path,
                dmca: &'a mut bool)
                -> Result<Filename, Error> {
    let quality = if use_qality {
        Some(request.quality.to_string())
    } else {
        None
    };
    Ok(match hdb.downloader.get_file_name(&request.url, quality) { // get filename
        Ok(v) => {
            v
        }
        Err(Error::DMCAError) => {
            // now request via lib.. // k if( k == Err(DownloadError::DMCAError) )
            info!("DMCA error!");
            if CONFIG.general.lib_use {
                if !request.compress {
                    db::set_query_code(&mut request.get_conn(), &request.internal_id, &CODE_IN_PROGRESS);
                }
                match hdb.downloader.lib_request_video(1,
                                                       0,
                                                       path,
                                                       request,
                                                       &request.quality.to_string(),
                                                       video) {
                    Err(err) => {
                        warn!("lib-call error {:?}", err);
                        return Err(err);
                    }
                    Ok(v) => {
                        *dmca = true;
                        v
                    }
                }
            } else {
                return Err(Error::NotAvailable);
            }
        }
        Err(e) => {
            // unknown error / restricted source etc.. abort
            error!("Unknown error: {:?}", e);
            return Err(e);
        }
    })
}

/// Get audio quality to use
fn get_audio_quality(id: &i16) -> Result<String, Error> {
    let id = if *id == CONFIG.codecs.audio_raw {
        CONFIG.codecs.yt.audio_normal_mp4
    } else if *id == CONFIG.codecs.audio_source_hq {
        CONFIG.codecs.yt.audio_normal_mp4
    } else if *id == CONFIG.codecs.audio_mp3 {
        CONFIG.codecs.yt.audio_normal_mp4
    } else {
        return Err(Error::InputError("Unknown audio quality!".to_string()));
    };
    Ok(id.to_string())
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn regex() {
		assert!(REGEX_VIDEO.is_match(r"https://m.youtube.com/watch?list=PLTXoSHLJey0RR60hjLhuUAaj_ftAdShqv&v=IO-_EoRSpUA"));
		assert!(REGEX_VIDEO.is_match(r"http://m.youtube.com/watch?v=IO-_EoRSpUA"));
        assert!(REGEX_VIDEO.is_match(r"https://m.youtube.com/watch?v=IO-_EoRSpUA"));
		assert!(REGEX_VIDEO.is_match(r"http://youtu.be/IO-_EoRSpUA"));
        assert!(REGEX_VIDEO.is_match(r"https://www.youtube.com/watch?v=IO-_EoRSpUA"));
        assert!(REGEX_VIDEO.is_match(r"https://www.youtube.com/watch?v=IO-_EoRSpUA&list=PL6DA1502C5DDC0317&index=21"));
        assert!(!REGEX_VIDEO.is_match(r"https://www.youtube.com/playlist?list=PLJYiF4qyO-fpaYWfTylcFu3VGUCVK4xfz"));

		assert!(REGEX_PLAYLIST.is_match(r"https://m.youtube.com/playlist?list=PLTXoSHLJey0RR60hjLhuUAaj_ftAdShqv"));
		assert!(REGEX_PLAYLIST.is_match(r"https://m.youtube.com/watch?list=PLTXoSHLJey0RR60hjLhuUAaj_ftAdShqv&v=IO-_EoRSpUA"));
        assert!(REGEX_PLAYLIST.is_match(r"https://www.youtube.com/playlist?list=PLBCC15D0E3ED5E67A"));
        assert!(REGEX_PLAYLIST.is_match(r"https://www.youtube.com/watch?v=IO-_EoRSpUA&index=2&list=PLBCC15D0E3ED5E67A"));
        assert!(REGEX_PLAYLIST.is_match(r"https://www.youtube.com/watch?v=IO-_EoRSpUA&list=PL6DA1502C5DDC0317&index=21"));
        assert!(!REGEX_PLAYLIST.is_match(r"https://www.youtube.com/watch?v=IO-_EoRSpUA"));
    }

}
<|endoftext|>


<commit_before>use std::any::Any;

use { GenericEvent, TOUCH };

/// Stores the touch state.
#[derive(Copy, Clone, RustcDecodable, RustcEncodable, PartialEq, Debug)]
pub enum Touch {
    /// The start of touch, for example
    /// a finger pressed down on a touch screen.
    Start,
    /// The move of touch, for example
    /// a finger moving while touching a touch screen.
    Move,
    /// The end of touch, for example
    /// taking a finger away from a touch screen.
    End,
    /// The cancel of touch, for example
    /// the window loses focus.
    Cancel,
}

/// Touch arguments
///
/// The `id` might be reused for different touches that do not overlap in time.
///
/// - Coordinates are normalized to support both touch screens and trackpads
/// - Supports both 2D and 3D touch
/// - The pressure direction vector should have maximum length 1
///
/// For 2D touch the pressure is pointed the z direction.
/// Use `.pressure()` to get the pressure magnitude.
#[derive(Copy, Clone, RustcDecodable, RustcEncodable, PartialEq, Debug)]
pub struct TouchArgs {
    /// A unique identifier for touch device.
    pub device: i64,
    /// A unique identifier for touch event.
    pub id: i64,
    /// The x coordinate of the touch position, normalized 0..1.
    pub x: f64,
    /// The y coordinate of the touch position, normalized 0..1.
    pub y: f64,
    /// The z coordinate of the touch position, normalized 0..1.
    pub z: f64,
    /// The x coordinate of the touch pressure direction.
    pub px: f64,
    /// The y coordinate of the touch pressure direction.
    pub py: f64,
    /// The z coordinate of the touch pressure direction.
    pub pz: f64,
    /// Whether the touch is in 3D.
    pub is_3d: bool,
    /// The touch state.
    pub touch: Touch,
}

impl TouchArgs {
    /// Creates arguments for 2D touch.
    pub fn new(
        device: i64,
        id: i64,
        pos: [f64; 2],
        pressure: f64,
        touch: Touch
    ) -> TouchArgs {
        TouchArgs {
            device: device,
            id: id,
            x: pos[0],
            y: pos[1],
            z: 0.0,
            is_3d: false,
            px: 0.0,
            py: 0.0,
            pz: pressure,
            touch: touch,
        }
    }

    /// Creates arguments for 3D touch.
    ///
    /// The pressure direction vector should have maximum length 1.
    pub fn new_3d(
        device: i64,
        id: i64,
        pos: [f64; 3],
        pressure: [f64; 3],
        touch: Touch
    ) -> TouchArgs {
        TouchArgs {
            device: device,
            id: id,
            x: pos[0],
            y: pos[1],
            z: pos[2],
            is_3d: true,
            px: pressure[0],
            py: pressure[1],
            pz: pressure[2],
            touch: touch,
        }
    }

    /// The position of the touch in 2D.
    pub fn position(&self) -> [f64; 2] {
        [self.x, self.y]
    }

    /// The position of the touch in 3D.
    pub fn position_3d(&self) -> [f64; 3] {
        [self.x, self.y, self.z]
    }

    /// The pressure magnitude, normalized 0..1.
    pub fn pressure(&self) -> f64 {
        (self.px * self.px + self.py * self.py + self.pz * self.pz).sqrt()
    }

    /// The pressure vector in 3D.
    pub fn pressure_3d(&self) -> [f64; 3] {
        [self.px, self.py, self.pz]
    }
}

/// When a touch is started, moved, ended or cancelled.
pub trait TouchEvent: Sized {
    /// Creates a touch event.
    fn from_touch_args(args: &TouchArgs, old_event: &Self) -> Option<Self>;
    /// Calls closure if this is a touch event.
    fn touch<U, F>(&self, f: F) -> Option<U>
        where F: FnMut(&TouchArgs) -> U;
    /// Returns touch arguments.
    fn touch_args(&self) -> Option<TouchArgs> {
        self.touch(|args| args.clone())
    }
}

impl<T> TouchEvent for T where T: GenericEvent {
    fn from_touch_args(args: &TouchArgs, old_event: &Self) -> Option<Self> {
        GenericEvent::from_args(TOUCH, args as &Any, old_event)
    }

    fn touch<U, F>(&self, mut f: F) -> Option<U>
        where F: FnMut(&TouchArgs) -> U
    {
        if self.event_id() != TOUCH {
            return None;
        }
        self.with_args(|any| {
            if let Some(args) = any.downcast_ref::<TouchArgs>() {
                Some(f(args))
            } else {
                panic!("Expected TouchArgs")
            }
        })
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_input_touch() {
        use super::super::{ Input, Motion };

        let pos = [0.0; 2];
        let e = Input::Move(Motion::Touch(
            TouchArgs::new(0, 0, pos, 1.0, Touch::Start)));
        let a: Option<Input> = TouchEvent::from_touch_args(
            &TouchArgs::new(0, 0, pos, 1.0, Touch::Start), &e);
        let b: Option<Input> = a.clone().unwrap().touch(|t|
            TouchEvent::from_touch_args(
                &TouchArgs::new(t.device, t.id, t.position(), t.pressure(), Touch::Start),
                a.as_ref().unwrap())).unwrap();
        assert_eq!(a, b);
    }

    #[test]
    fn test_event_touch() {
        use Event;
        use super::super::{ Input, Motion };

        let pos = [0.0; 2];
        let e = Event::Input(Input::Move(Motion::Touch(
            TouchArgs::new(0, 0, pos, 1.0, Touch::Start))));
        let a: Option<Event> = TouchEvent::from_touch_args(
            &TouchArgs::new(0, 0, pos, 1.0, Touch::Start), &e);
        let b: Option<Event> = a.clone().unwrap().touch(|t|
            TouchEvent::from_touch_args(
                &TouchArgs::new(t.device, t.id, t.position(), t.pressure(), Touch::Start),
                a.as_ref().unwrap())).unwrap();
        assert_eq!(a, b);
    }

    #[test]
    fn test_input_touch_3d() {
        use super::super::{ Input, Motion };

        let pos = [0.0; 3];
        let pressure = [0.0, 0.0, 1.0];
        let e = Input::Move(Motion::Touch(
            TouchArgs::new_3d(0, 0, pos, pressure, Touch::Start)));
        let a: Option<Input> = TouchEvent::from_touch_args(
            &TouchArgs::new_3d(0, 0, pos, pressure, Touch::Start), &e);
        let b: Option<Input> = a.clone().unwrap().touch(|t|
            TouchEvent::from_touch_args(
                &TouchArgs::new_3d(t.device, t.id, t.position3d(), t.pressure3d(), Touch::Start),
                a.as_ref().unwrap())).unwrap();
        assert_eq!(a, b);
    }

    #[test]
    fn test_event_touch_3d() {
        use Event;
        use super::super::{ Input, Motion };

        let pos = [0.0; 3];
        let pressure = [0.0, 0.0, 1.0];
        let e = Event::Input(Input::Move(Motion::Touch(
            TouchArgs::new_3d(0, 0, pos, pressure, Touch::Start))));
        let a: Option<Event> = TouchEvent::from_touch_args(
            &TouchArgs::new_3d(0, 0, pos, pressure, Touch::Start), &e);
        let b: Option<Event> = a.clone().unwrap().touch(|t|
            TouchEvent::from_touch_args(
                &TouchArgs::new_3d(t.device, t.id, t.position3d(), t.pressure3d(), Touch::Start),
                a.as_ref().unwrap())).unwrap();
        assert_eq!(a, b);
    }
}
<commit_msg>Fixed tests<commit_after>use std::any::Any;

use { GenericEvent, TOUCH };

/// Stores the touch state.
#[derive(Copy, Clone, RustcDecodable, RustcEncodable, PartialEq, Debug)]
pub enum Touch {
    /// The start of touch, for example
    /// a finger pressed down on a touch screen.
    Start,
    /// The move of touch, for example
    /// a finger moving while touching a touch screen.
    Move,
    /// The end of touch, for example
    /// taking a finger away from a touch screen.
    End,
    /// The cancel of touch, for example
    /// the window loses focus.
    Cancel,
}

/// Touch arguments
///
/// The `id` might be reused for different touches that do not overlap in time.
///
/// - Coordinates are normalized to support both touch screens and trackpads
/// - Supports both 2D and 3D touch
/// - The pressure direction vector should have maximum length 1
///
/// For 2D touch the pressure is pointed the z direction.
/// Use `.pressure()` to get the pressure magnitude.
#[derive(Copy, Clone, RustcDecodable, RustcEncodable, PartialEq, Debug)]
pub struct TouchArgs {
    /// A unique identifier for touch device.
    pub device: i64,
    /// A unique identifier for touch event.
    pub id: i64,
    /// The x coordinate of the touch position, normalized 0..1.
    pub x: f64,
    /// The y coordinate of the touch position, normalized 0..1.
    pub y: f64,
    /// The z coordinate of the touch position, normalized 0..1.
    pub z: f64,
    /// The x coordinate of the touch pressure direction.
    pub px: f64,
    /// The y coordinate of the touch pressure direction.
    pub py: f64,
    /// The z coordinate of the touch pressure direction.
    pub pz: f64,
    /// Whether the touch is in 3D.
    pub is_3d: bool,
    /// The touch state.
    pub touch: Touch,
}

impl TouchArgs {
    /// Creates arguments for 2D touch.
    pub fn new(
        device: i64,
        id: i64,
        pos: [f64; 2],
        pressure: f64,
        touch: Touch
    ) -> TouchArgs {
        TouchArgs {
            device: device,
            id: id,
            x: pos[0],
            y: pos[1],
            z: 0.0,
            is_3d: false,
            px: 0.0,
            py: 0.0,
            pz: pressure,
            touch: touch,
        }
    }

    /// Creates arguments for 3D touch.
    ///
    /// The pressure direction vector should have maximum length 1.
    pub fn new_3d(
        device: i64,
        id: i64,
        pos: [f64; 3],
        pressure: [f64; 3],
        touch: Touch
    ) -> TouchArgs {
        TouchArgs {
            device: device,
            id: id,
            x: pos[0],
            y: pos[1],
            z: pos[2],
            is_3d: true,
            px: pressure[0],
            py: pressure[1],
            pz: pressure[2],
            touch: touch,
        }
    }

    /// The position of the touch in 2D.
    pub fn position(&self) -> [f64; 2] {
        [self.x, self.y]
    }

    /// The position of the touch in 3D.
    pub fn position_3d(&self) -> [f64; 3] {
        [self.x, self.y, self.z]
    }

    /// The pressure magnitude, normalized 0..1.
    pub fn pressure(&self) -> f64 {
        (self.px * self.px + self.py * self.py + self.pz * self.pz).sqrt()
    }

    /// The pressure vector in 3D.
    pub fn pressure_3d(&self) -> [f64; 3] {
        [self.px, self.py, self.pz]
    }
}

/// When a touch is started, moved, ended or cancelled.
pub trait TouchEvent: Sized {
    /// Creates a touch event.
    fn from_touch_args(args: &TouchArgs, old_event: &Self) -> Option<Self>;
    /// Calls closure if this is a touch event.
    fn touch<U, F>(&self, f: F) -> Option<U>
        where F: FnMut(&TouchArgs) -> U;
    /// Returns touch arguments.
    fn touch_args(&self) -> Option<TouchArgs> {
        self.touch(|args| args.clone())
    }
}

impl<T> TouchEvent for T where T: GenericEvent {
    fn from_touch_args(args: &TouchArgs, old_event: &Self) -> Option<Self> {
        GenericEvent::from_args(TOUCH, args as &Any, old_event)
    }

    fn touch<U, F>(&self, mut f: F) -> Option<U>
        where F: FnMut(&TouchArgs) -> U
    {
        if self.event_id() != TOUCH {
            return None;
        }
        self.with_args(|any| {
            if let Some(args) = any.downcast_ref::<TouchArgs>() {
                Some(f(args))
            } else {
                panic!("Expected TouchArgs")
            }
        })
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_input_touch() {
        use super::super::{ Input, Motion };

        let pos = [0.0; 2];
        let e = Input::Move(Motion::Touch(
            TouchArgs::new(0, 0, pos, 1.0, Touch::Start)));
        let a: Option<Input> = TouchEvent::from_touch_args(
            &TouchArgs::new(0, 0, pos, 1.0, Touch::Start), &e);
        let b: Option<Input> = a.clone().unwrap().touch(|t|
            TouchEvent::from_touch_args(
                &TouchArgs::new(t.device, t.id, t.position(), t.pressure(), Touch::Start),
                a.as_ref().unwrap())).unwrap();
        assert_eq!(a, b);
    }

    #[test]
    fn test_event_touch() {
        use Event;
        use super::super::{ Input, Motion };

        let pos = [0.0; 2];
        let e = Event::Input(Input::Move(Motion::Touch(
            TouchArgs::new(0, 0, pos, 1.0, Touch::Start))));
        let a: Option<Event> = TouchEvent::from_touch_args(
            &TouchArgs::new(0, 0, pos, 1.0, Touch::Start), &e);
        let b: Option<Event> = a.clone().unwrap().touch(|t|
            TouchEvent::from_touch_args(
                &TouchArgs::new(t.device, t.id, t.position(), t.pressure(), Touch::Start),
                a.as_ref().unwrap())).unwrap();
        assert_eq!(a, b);
    }

    #[test]
    fn test_input_touch_3d() {
        use super::super::{ Input, Motion };

        let pos = [0.0; 3];
        let pressure = [0.0, 0.0, 1.0];
        let e = Input::Move(Motion::Touch(
            TouchArgs::new_3d(0, 0, pos, pressure, Touch::Start)));
        let a: Option<Input> = TouchEvent::from_touch_args(
            &TouchArgs::new_3d(0, 0, pos, pressure, Touch::Start), &e);
        let b: Option<Input> = a.clone().unwrap().touch(|t|
            TouchEvent::from_touch_args(
                &TouchArgs::new_3d(t.device, t.id, t.position_3d(), t.pressure_3d(), Touch::Start),
                a.as_ref().unwrap())).unwrap();
        assert_eq!(a, b);
    }

    #[test]
    fn test_event_touch_3d() {
        use Event;
        use super::super::{ Input, Motion };

        let pos = [0.0; 3];
        let pressure = [0.0, 0.0, 1.0];
        let e = Event::Input(Input::Move(Motion::Touch(
            TouchArgs::new_3d(0, 0, pos, pressure, Touch::Start))));
        let a: Option<Event> = TouchEvent::from_touch_args(
            &TouchArgs::new_3d(0, 0, pos, pressure, Touch::Start), &e);
        let b: Option<Event> = a.clone().unwrap().touch(|t|
            TouchEvent::from_touch_args(
                &TouchArgs::new_3d(t.device, t.id, t.position_3d(), t.pressure_3d(), Touch::Start),
                a.as_ref().unwrap())).unwrap();
        assert_eq!(a, b);
    }
}
<|endoftext|>


<commit_before>#[macro_use] extern crate log;
extern crate rand;
extern crate byteorder;
extern crate mqtt3;
extern crate netopt;

mod error;
mod sub;
mod client;

pub use sub::{
    ToSubTopics,
    ToUnSubTopics
};

pub use client::{
    Client,
    ClientOptions
};

use std::sync::Arc;
use std::ops;
use mqtt3::{QoS, ToTopicPath};
use error::Result;

const MAX_QOS: QoS = mqtt3::QoS::AtLeastOnce;

pub trait Mqttc {
    //fn ping(&mut self) -> Result<()>;
    fn publish<T: ToTopicPath, P: ToPayload>(&mut self, topic: ToTopicPath, payload: ToPayload, pubopt: PubOpt) -> Result<()>;
    fn subscribe<S: ToSubTopics>(&mut self, subs: S) -> Result<()>;
    fn unsubscribe<U: ToUnSubTopics>(&mut self, unsubs: U) -> Result<()>;
    fn disconnect(self) -> Result<()>;
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ClientState {
    Handshake,
    Connected,
    Disconnected
}

pub struct PubOpt(u8);

impl PubOpt {
    #[inline]
    pub fn at_most_once() -> PubOpt {
        PubOpt(0x00)
    }

    #[inline]
    pub fn at_least_once() -> PubOpt {
        PubOpt(0x01)
    }

    #[inline]
    pub fn exactly_once() -> PubOpt {
        PubOpt(0x02)
    }

    #[inline]
    pub fn retain() -> PubOpt {
        PubOpt(0x04)
    }

    #[inline]
    pub fn bits(&self) -> u8 {
        self.0
    }

    pub fn qos(&self) -> QoS {
        if (self.0 & PubOpt::exactly_once().bits()) != 0 {
            return QoS::ExactlyOnce;
        }
        if (self.0 & PubOpt::at_least_once().bits()) != 0 {
            return QoS::AtLeastOnce;
        }
        QoS::AtMostOnce
    }

    pub fn is_retain(&self) -> bool {
        (self.0 & PubOpt::retain().bits()) != 0
    }
}

impl ops::BitOr for PubOpt {
    type Output = PubOpt;

    #[inline]
    fn bitor(self, other: PubOpt) -> PubOpt {
        PubOpt(self.bits() | other.bits())
    }
}

impl ops::BitXor for PubOpt {
    type Output = PubOpt;

    #[inline]
    fn bitxor(self, other: PubOpt) -> PubOpt {
        PubOpt(self.bits() ^ other.bits())
    }
}

impl ops::BitAnd for PubOpt {
    type Output = PubOpt;

    #[inline]
    fn bitand(self, other: PubOpt) -> PubOpt {
        PubOpt(self.bits() & other.bits())
    }
}

impl ops::Sub for PubOpt {
    type Output = PubOpt;

    #[inline]
    fn sub(self, other: PubOpt) -> PubOpt {
        PubOpt(self.bits() & !other.bits())
    }
}

impl ops::Not for PubOpt {
    type Output = PubOpt;

    #[inline]
    fn not(self) -> PubOpt {
        PubOpt(!self.bits() & 0b111)
    }
}

pub type Payload = Arc<Vec<u8>>;

pub trait ToPayload {
    fn to_payload(&self) -> Payload;
}

impl ToPayload for String {
    fn to_payload(&self) -> Payload {
        Arc::new(self.clone().into_bytes())
    }
}

impl<'a> ToPayload for &'a str {
    fn to_payload(&self) -> Payload {
        Arc::new(self.as_bytes().to_vec())
    }
}

impl ToPayload for Vec<u8> {
    fn to_payload(&self) -> Payload {
        Arc::new(self.clone())
    }
}

impl ToPayload for Arc<Vec<u8>> {
    fn to_payload(&self) -> Payload {
        self.clone()
    }
}

#[cfg(test)]
mod test {
    use super::PubOpt;
    use mqtt3::QoS;

    #[test]
    fn pubopt_test() {
        let pubopt = PubOpt::at_least_once();
        assert_eq!(pubopt.qos(), QoS::AtLeastOnce);
        assert!(!pubopt.is_retain());

        let pubopt = PubOpt::at_most_once();
        assert_eq!(pubopt.qos(), QoS::AtMostOnce);
        assert!(!pubopt.is_retain());

        let pubopt = PubOpt::exactly_once();
        assert_eq!(pubopt.qos(), QoS::ExactlyOnce);
        assert!(!pubopt.is_retain());

        let pubopt = PubOpt::at_least_once() | PubOpt::retain();
        assert_eq!(pubopt.qos(), QoS::AtLeastOnce);
        assert!(pubopt.is_retain());

        let pubopt = PubOpt::at_most_once() | PubOpt::retain();
        assert_eq!(pubopt.qos(), QoS::AtMostOnce);
        assert!(pubopt.is_retain());

        let pubopt = PubOpt::exactly_once() | PubOpt::retain();
        assert_eq!(pubopt.qos(), QoS::ExactlyOnce);
        assert!(pubopt.is_retain());
    }
}
<commit_msg>mqttc: Add ReconnectMethod<commit_after>#[macro_use] extern crate log;
extern crate rand;
extern crate byteorder;
extern crate mqtt3;
extern crate netopt;

mod error;
mod sub;
mod client;

pub use sub::{
    ToSubTopics,
    ToUnSubTopics
};

pub use client::{
    Client,
    ClientOptions
};

use std::sync::Arc;
use std::ops;
use std::time::Duration;
use mqtt3::{QoS, ToTopicPath};
use error::Result;

const MAX_QOS: QoS = mqtt3::QoS::AtLeastOnce;

pub trait Mqttc {
    //fn ping(&mut self) -> Result<()>;
    fn publish<T: ToTopicPath, P: ToPayload>(&mut self, topic: ToTopicPath, payload: ToPayload, pubopt: PubOpt) -> Result<()>;
    fn subscribe<S: ToSubTopics>(&mut self, subs: S) -> Result<()>;
    fn unsubscribe<U: ToUnSubTopics>(&mut self, unsubs: U) -> Result<()>;
    fn disconnect(self) -> Result<()>;
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ClientState {
    Handshake,
    Connected,
    Disconnected
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ReconnectMethod {
    ForeverDisconnect,
    ReconnectAfter(Duration)
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PubOpt(u8);

impl PubOpt {
    #[inline]
    pub fn at_most_once() -> PubOpt {
        PubOpt(0x00)
    }

    #[inline]
    pub fn at_least_once() -> PubOpt {
        PubOpt(0x01)
    }

    #[inline]
    pub fn exactly_once() -> PubOpt {
        PubOpt(0x02)
    }

    #[inline]
    pub fn retain() -> PubOpt {
        PubOpt(0x04)
    }

    #[inline]
    pub fn bits(&self) -> u8 {
        self.0
    }

    pub fn qos(&self) -> QoS {
        if (self.0 & PubOpt::exactly_once().bits()) != 0 {
            return QoS::ExactlyOnce;
        }
        if (self.0 & PubOpt::at_least_once().bits()) != 0 {
            return QoS::AtLeastOnce;
        }
        QoS::AtMostOnce
    }

    pub fn is_retain(&self) -> bool {
        (self.0 & PubOpt::retain().bits()) != 0
    }
}

impl ops::BitOr for PubOpt {
    type Output = PubOpt;

    #[inline]
    fn bitor(self, other: PubOpt) -> PubOpt {
        PubOpt(self.bits() | other.bits())
    }
}

impl ops::BitXor for PubOpt {
    type Output = PubOpt;

    #[inline]
    fn bitxor(self, other: PubOpt) -> PubOpt {
        PubOpt(self.bits() ^ other.bits())
    }
}

impl ops::BitAnd for PubOpt {
    type Output = PubOpt;

    #[inline]
    fn bitand(self, other: PubOpt) -> PubOpt {
        PubOpt(self.bits() & other.bits())
    }
}

impl ops::Sub for PubOpt {
    type Output = PubOpt;

    #[inline]
    fn sub(self, other: PubOpt) -> PubOpt {
        PubOpt(self.bits() & !other.bits())
    }
}

impl ops::Not for PubOpt {
    type Output = PubOpt;

    #[inline]
    fn not(self) -> PubOpt {
        PubOpt(!self.bits() & 0b111)
    }
}

pub type Payload = Arc<Vec<u8>>;

pub trait ToPayload {
    fn to_payload(&self) -> Payload;
}

impl ToPayload for String {
    fn to_payload(&self) -> Payload {
        Arc::new(self.clone().into_bytes())
    }
}

impl<'a> ToPayload for &'a str {
    fn to_payload(&self) -> Payload {
        Arc::new(self.as_bytes().to_vec())
    }
}

impl ToPayload for Vec<u8> {
    fn to_payload(&self) -> Payload {
        Arc::new(self.clone())
    }
}

impl ToPayload for Arc<Vec<u8>> {
    fn to_payload(&self) -> Payload {
        self.clone()
    }
}

#[cfg(test)]
mod test {
    use super::PubOpt;
    use mqtt3::QoS;

    #[test]
    fn pubopt_test() {
        let pubopt = PubOpt::at_least_once();
        assert_eq!(pubopt.qos(), QoS::AtLeastOnce);
        assert!(!pubopt.is_retain());

        let pubopt = PubOpt::at_most_once();
        assert_eq!(pubopt.qos(), QoS::AtMostOnce);
        assert!(!pubopt.is_retain());

        let pubopt = PubOpt::exactly_once();
        assert_eq!(pubopt.qos(), QoS::ExactlyOnce);
        assert!(!pubopt.is_retain());

        let pubopt = PubOpt::at_least_once() | PubOpt::retain();
        assert_eq!(pubopt.qos(), QoS::AtLeastOnce);
        assert!(pubopt.is_retain());

        let pubopt = PubOpt::at_most_once() | PubOpt::retain();
        assert_eq!(pubopt.qos(), QoS::AtMostOnce);
        assert!(pubopt.is_retain());

        let pubopt = PubOpt::exactly_once() | PubOpt::retain();
        assert_eq!(pubopt.qos(), QoS::ExactlyOnce);
        assert!(pubopt.is_retain());
    }
}
<|endoftext|>


<commit_before>#[feature(globs,phase)];
#[phase(syntax, link)] extern crate log;

extern crate leveldb;
extern crate serialize;

pub mod utils {
  use leveldb::database::Database;
  use leveldb::options::{Options};

  pub fn open_database(name: ~str, create_if_missing: bool) -> Database {
    let mut opts = Options::new();
    opts.create_if_missing(create_if_missing);
    match Database::open(name, opts) {
      Ok(db) => { db },
      Err(_) => { fail!("failed to open database") }
    }
  }
}

#[cfg(test)]
mod binary_tests {
  use super::utils::{open_database};
  use leveldb::database::Database;
  use leveldb::database::binary::Interface;
  use leveldb::iterator::Iterable;
  use leveldb::options::{Options,ReadOptions,WriteOptions};

  fn db_put_simple(database: &mut Database, key: &[u8], val: &[u8]) {
    let write_opts = WriteOptions::new();
    match database.put(write_opts, key, val) {
      Ok(_) => { () },
      Err(_) => { fail!("failed to write to database") }
    }
  }

  #[test]
  fn test_create_options() {
    Options::new();
  }

  #[test]
  fn test_open_database() {
    let mut opts = Options::new();
    opts.create_if_missing(true);
    let res = Database::open(~"testdbs/create_if_missing", opts);
    assert!(res.is_ok());
  }

  #[test]
  fn test_open_non_existant_database_without_create() {
    let mut opts = Options::new();
    opts.create_if_missing(false);
    let res = Database::open(~"testdbs/missing", opts);
    assert!(res.is_err());
  }

  #[test]
  fn test_write_to_database() {
    let mut database = open_database(~"testdbs/put_simple", true);
    let write_opts = WriteOptions::new();
    let result = database.put(write_opts,
                              &[1],
                              &[1]);
    assert!(result.is_ok());
  }

  #[test]
  fn test_delete_from_database() {
    let mut database = open_database(~"testdbs/delete_simple", true);
    db_put_simple(&mut database, &[1], &[1]);

    let write2 = WriteOptions::new();
    let res2 = database.delete(write2,
                               &[1]);
    assert!(res2.is_ok());
  }

  #[test]
  fn test_get_from_empty_database() {
    let mut database = open_database(~"testdbs/get_simple", true);
    let read_opts = ReadOptions::new();
    let res = database.get(read_opts, [1,2,3]);
    match res {
      Ok(data) => { assert!(data.is_none()) },
      Err(_) => { fail!("failed reading data") }
    }
  }

  #[test]
  fn test_get_from_filled_database() {
    let mut database = open_database(~"testdbs/get_filled", true);
    db_put_simple(&mut database, &[1], &[1]);

    let read_opts = ReadOptions::new();
    let res = database.get(read_opts,
                            &[1]);
    match res {
      Ok(data) => {
        assert!(data.is_some())
        let data = data.unwrap();
        assert_eq!(data, ~[1]);
      },
      Err(_) => { fail!("failed reading data") }
    }
  }

  #[test]
  fn test_iterator() {
    let mut database = open_database(~"testdbs/iter", true);
    db_put_simple(&mut database, &[1], &[1]);
    db_put_simple(&mut database, &[2], &[2]);

    let read_opts = ReadOptions::new();
    let mut iter = database.iter(read_opts);
    assert!(iter.valid());
    assert!(iter.next().is_some());
    assert!(iter.next().is_some());
    assert!(iter.next().is_none());
  }
}

#[cfg(test)]
mod json_tests {
  use super::utils::{open_database};
  use leveldb::database::json::Interface;
  use leveldb::options::{ReadOptions,WriteOptions};
  use serialize::{Encodable};

  #[deriving(Encodable,Decodable)]
  struct ToEncode {
    test: ~str,
  }

  #[test]
  fn test_write_to_database() {
    let mut database = open_database(~"testdbs/json_put", true);
    let write_opts = WriteOptions::new();
    let key = ToEncode { test: ~"string" };
    let val = ToEncode { test: ~"string2" };
    let result = database.put(write_opts,
                              &key,
                              &val);
    assert!(result.is_ok());
  }

  #[test]
  fn test_read_from_database() {
    let mut database = open_database(~"testdbs/json_put", true);
    let write_opts = WriteOptions::new();
    let key = ToEncode { test: ~"string" };
    let val = ToEncode { test: ~"string2" };
    let result = database.put(write_opts,
                              &key,
                              &val);
    assert!(result.is_ok());
    let read_opts = ReadOptions::new();
    let read = database.get(read_opts,
                            &key);
    match read {
      Ok(data) => {
        assert!(data.is_some())
        let data = data.unwrap();
        assert!(data.is_object());
      },
      Err(_) => { fail!("failed reading data") }
    }
  }

}
<commit_msg>Use a seperated database for read tests<commit_after>#[feature(globs,phase)];
#[phase(syntax, link)] extern crate log;

extern crate leveldb;
extern crate serialize;

pub mod utils {
  use leveldb::database::Database;
  use leveldb::options::{Options};

  pub fn open_database(name: ~str, create_if_missing: bool) -> Database {
    let mut opts = Options::new();
    opts.create_if_missing(create_if_missing);
    match Database::open(name, opts) {
      Ok(db) => { db },
      Err(_) => { fail!("failed to open database") }
    }
  }
}

#[cfg(test)]
mod binary_tests {
  use super::utils::{open_database};
  use leveldb::database::Database;
  use leveldb::database::binary::Interface;
  use leveldb::iterator::Iterable;
  use leveldb::options::{Options,ReadOptions,WriteOptions};

  fn db_put_simple(database: &mut Database, key: &[u8], val: &[u8]) {
    let write_opts = WriteOptions::new();
    match database.put(write_opts, key, val) {
      Ok(_) => { () },
      Err(_) => { fail!("failed to write to database") }
    }
  }

  #[test]
  fn test_create_options() {
    Options::new();
  }

  #[test]
  fn test_open_database() {
    let mut opts = Options::new();
    opts.create_if_missing(true);
    let res = Database::open(~"testdbs/create_if_missing", opts);
    assert!(res.is_ok());
  }

  #[test]
  fn test_open_non_existant_database_without_create() {
    let mut opts = Options::new();
    opts.create_if_missing(false);
    let res = Database::open(~"testdbs/missing", opts);
    assert!(res.is_err());
  }

  #[test]
  fn test_write_to_database() {
    let mut database = open_database(~"testdbs/put_simple", true);
    let write_opts = WriteOptions::new();
    let result = database.put(write_opts,
                              &[1],
                              &[1]);
    assert!(result.is_ok());
  }

  #[test]
  fn test_delete_from_database() {
    let mut database = open_database(~"testdbs/delete_simple", true);
    db_put_simple(&mut database, &[1], &[1]);

    let write2 = WriteOptions::new();
    let res2 = database.delete(write2,
                               &[1]);
    assert!(res2.is_ok());
  }

  #[test]
  fn test_get_from_empty_database() {
    let mut database = open_database(~"testdbs/get_simple", true);
    let read_opts = ReadOptions::new();
    let res = database.get(read_opts, [1,2,3]);
    match res {
      Ok(data) => { assert!(data.is_none()) },
      Err(_) => { fail!("failed reading data") }
    }
  }

  #[test]
  fn test_get_from_filled_database() {
    let mut database = open_database(~"testdbs/get_filled", true);
    db_put_simple(&mut database, &[1], &[1]);

    let read_opts = ReadOptions::new();
    let res = database.get(read_opts,
                            &[1]);
    match res {
      Ok(data) => {
        assert!(data.is_some())
        let data = data.unwrap();
        assert_eq!(data, ~[1]);
      },
      Err(_) => { fail!("failed reading data") }
    }
  }

  #[test]
  fn test_iterator() {
    let mut database = open_database(~"testdbs/iter", true);
    db_put_simple(&mut database, &[1], &[1]);
    db_put_simple(&mut database, &[2], &[2]);

    let read_opts = ReadOptions::new();
    let mut iter = database.iter(read_opts);
    assert!(iter.valid());
    assert!(iter.next().is_some());
    assert!(iter.next().is_some());
    assert!(iter.next().is_none());
  }
}

#[cfg(test)]
mod json_tests {
  use super::utils::{open_database};
  use leveldb::database::json::Interface;
  use leveldb::options::{ReadOptions,WriteOptions};
  use serialize::{Encodable};

  #[deriving(Encodable,Decodable)]
  struct ToEncode {
    test: ~str,
  }

  #[test]
  fn test_write_to_database() {
    let mut database = open_database(~"testdbs/json_put", true);
    let write_opts = WriteOptions::new();
    let key = ToEncode { test: ~"string" };
    let val = ToEncode { test: ~"string2" };
    let result = database.put(write_opts,
                              &key,
                              &val);
    assert!(result.is_ok());
  }

  #[test]
  fn test_read_from_database() {
    let mut database = open_database(~"testdbs/json_read", true);
    let write_opts = WriteOptions::new();
    let key = ToEncode { test: ~"string" };
    let val = ToEncode { test: ~"string2" };
    let result = database.put(write_opts,
                              &key,
                              &val);
    assert!(result.is_ok());
    let read_opts = ReadOptions::new();
    let read = database.get(read_opts,
                            &key);
    match read {
      Ok(data) => {
        assert!(data.is_some())
        let data = data.unwrap();
        assert!(data.is_object());
      },
      Err(_) => { fail!("failed reading data") }
    }
  }

}
<|endoftext|>


<commit_before>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use cmp;
use io::{self, SeekFrom, Read, Write, Seek, BufRead, Error, ErrorKind};
use fmt;
use mem;

// =============================================================================
// Forwarding implementations

#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, R: Read + ?Sized> Read for &'a mut R {
    #[inline]
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        (**self).read(buf)
    }

    #[inline]
    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
        (**self).read_to_end(buf)
    }

    #[inline]
    fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
        (**self).read_to_string(buf)
    }

    #[inline]
    fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
        (**self).read_exact(buf)
    }
}
#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, W: Write + ?Sized> Write for &'a mut W {
    #[inline]
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> { (**self).write(buf) }

    #[inline]
    fn flush(&mut self) -> io::Result<()> { (**self).flush() }

    #[inline]
    fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
        (**self).write_all(buf)
    }

    #[inline]
    fn write_fmt(&mut self, fmt: fmt::Arguments) -> io::Result<()> {
        (**self).write_fmt(fmt)
    }
}
#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, S: Seek + ?Sized> Seek for &'a mut S {
    #[inline]
    fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> { (**self).seek(pos) }
}
#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, B: BufRead + ?Sized> BufRead for &'a mut B {
    #[inline]
    fn fill_buf(&mut self) -> io::Result<&[u8]> { (**self).fill_buf() }

    #[inline]
    fn consume(&mut self, amt: usize) { (**self).consume(amt) }

    #[inline]
    fn read_until(&mut self, byte: u8, buf: &mut Vec<u8>) -> io::Result<usize> {
        (**self).read_until(byte, buf)
    }

    #[inline]
    fn read_line(&mut self, buf: &mut String) -> io::Result<usize> {
        (**self).read_line(buf)
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<R: Read + ?Sized> Read for Box<R> {
    #[inline]
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        (**self).read(buf)
    }

    #[inline]
    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
        (**self).read_to_end(buf)
    }

    #[inline]
    fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
        (**self).read_to_string(buf)
    }

    #[inline]
    fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
        (**self).read_exact(buf)
    }
}
#[stable(feature = "rust1", since = "1.0.0")]
impl<W: Write + ?Sized> Write for Box<W> {
    #[inline]
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> { (**self).write(buf) }

    #[inline]
    fn flush(&mut self) -> io::Result<()> { (**self).flush() }

    #[inline]
    fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
        (**self).write_all(buf)
    }

    #[inline]
    fn write_fmt(&mut self, fmt: fmt::Arguments) -> io::Result<()> {
        (**self).write_fmt(fmt)
    }
}
#[stable(feature = "rust1", since = "1.0.0")]
impl<S: Seek + ?Sized> Seek for Box<S> {
    #[inline]
    fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> { (**self).seek(pos) }
}
#[stable(feature = "rust1", since = "1.0.0")]
impl<B: BufRead + ?Sized> BufRead for Box<B> {
    #[inline]
    fn fill_buf(&mut self) -> io::Result<&[u8]> { (**self).fill_buf() }

    #[inline]
    fn consume(&mut self, amt: usize) { (**self).consume(amt) }

    #[inline]
    fn read_until(&mut self, byte: u8, buf: &mut Vec<u8>) -> io::Result<usize> {
        (**self).read_until(byte, buf)
    }

    #[inline]
    fn read_line(&mut self, buf: &mut String) -> io::Result<usize> {
        (**self).read_line(buf)
    }
}

// =============================================================================
// In-memory buffer implementations

#[stable(feature = "rust1", since = "1.0.0")]
impl<'a> Read for &'a [u8] {
    #[inline]
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        let amt = cmp::min(buf.len(), self.len());
        let (a, b) = self.split_at(amt);
        buf[..amt].copy_from_slice(a);
        *self = b;
        Ok(amt)
    }

    #[inline]
    fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
        if buf.len() > self.len() {
            return Err(Error::new(ErrorKind::UnexpectedEof,
                                  "failed to fill whole buffer"));
        }
        let (a, b) = self.split_at(buf.len());
        buf.copy_from_slice(a);
        *self = b;
        Ok(())
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<'a> BufRead for &'a [u8] {
    #[inline]
    fn fill_buf(&mut self) -> io::Result<&[u8]> { Ok(*self) }

    #[inline]
    fn consume(&mut self, amt: usize) { *self = &self[amt..]; }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<'a> Write for &'a mut [u8] {
    #[inline]
    fn write(&mut self, data: &[u8]) -> io::Result<usize> {
        let amt = cmp::min(data.len(), self.len());
        let (a, b) = mem::replace(self, &mut []).split_at_mut(amt);
        a.copy_from_slice(&data[..amt]);
        *self = b;
        Ok(amt)
    }

    #[inline]
    fn write_all(&mut self, data: &[u8]) -> io::Result<()> {
        if self.write(data)? == data.len() {
            Ok(())
        } else {
            Err(Error::new(ErrorKind::WriteZero, "failed to write whole buffer"))
        }
    }

    #[inline]
    fn flush(&mut self) -> io::Result<()> { Ok(()) }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl Write for Vec<u8> {
    #[inline]
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.extend_from_slice(buf);
        Ok(buf.len())
    }

    #[inline]
    fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
        self.extend_from_slice(buf);
        Ok(())
    }

    #[inline]
    fn flush(&mut self) -> io::Result<()> { Ok(()) }
}

#[cfg(test)]
mod tests {
    use io::prelude::*;
    use test;

    #[bench]
    fn bench_read_slice(b: &mut test::Bencher) {
        let buf = [5; 1024];
        let mut dst = [0; 128];

        b.iter(|| {
            let mut rd = &buf[..];
            for _ in 0..8 {
                let _ = rd.read(&mut dst);
                test::black_box(&dst);
            }
        })
    }

    #[bench]
    fn bench_write_slice(b: &mut test::Bencher) {
        let mut buf = [0; 1024];
        let src = [5; 128];

        b.iter(|| {
            let mut wr = &mut buf[..];
            for _ in 0..8 {
                let _ = wr.write_all(&src);
                test::black_box(&wr);
            }
        })
    }

    #[bench]
    fn bench_read_vec(b: &mut test::Bencher) {
        let buf = vec![5; 1024];
        let mut dst = [0; 128];

        b.iter(|| {
            let mut rd = &buf[..];
            for _ in 0..8 {
                let _ = rd.read(&mut dst);
                test::black_box(&dst);
            }
        })
    }

    #[bench]
    fn bench_write_vec(b: &mut test::Bencher) {
        let mut buf = Vec::with_capacity(1024);
        let src = [5; 128];

        b.iter(|| {
            let mut wr = &mut buf[..];
            for _ in 0..8 {
                let _ = wr.write_all(&src);
                test::black_box(&wr);
            }
        })
    }
}
<commit_msg>Rollup merge of #37343 - bluss:write-doc, r=GuillaumeGomez<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use cmp;
use io::{self, SeekFrom, Read, Write, Seek, BufRead, Error, ErrorKind};
use fmt;
use mem;

// =============================================================================
// Forwarding implementations

#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, R: Read + ?Sized> Read for &'a mut R {
    #[inline]
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        (**self).read(buf)
    }

    #[inline]
    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
        (**self).read_to_end(buf)
    }

    #[inline]
    fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
        (**self).read_to_string(buf)
    }

    #[inline]
    fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
        (**self).read_exact(buf)
    }
}
#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, W: Write + ?Sized> Write for &'a mut W {
    #[inline]
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> { (**self).write(buf) }

    #[inline]
    fn flush(&mut self) -> io::Result<()> { (**self).flush() }

    #[inline]
    fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
        (**self).write_all(buf)
    }

    #[inline]
    fn write_fmt(&mut self, fmt: fmt::Arguments) -> io::Result<()> {
        (**self).write_fmt(fmt)
    }
}
#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, S: Seek + ?Sized> Seek for &'a mut S {
    #[inline]
    fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> { (**self).seek(pos) }
}
#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, B: BufRead + ?Sized> BufRead for &'a mut B {
    #[inline]
    fn fill_buf(&mut self) -> io::Result<&[u8]> { (**self).fill_buf() }

    #[inline]
    fn consume(&mut self, amt: usize) { (**self).consume(amt) }

    #[inline]
    fn read_until(&mut self, byte: u8, buf: &mut Vec<u8>) -> io::Result<usize> {
        (**self).read_until(byte, buf)
    }

    #[inline]
    fn read_line(&mut self, buf: &mut String) -> io::Result<usize> {
        (**self).read_line(buf)
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<R: Read + ?Sized> Read for Box<R> {
    #[inline]
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        (**self).read(buf)
    }

    #[inline]
    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
        (**self).read_to_end(buf)
    }

    #[inline]
    fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
        (**self).read_to_string(buf)
    }

    #[inline]
    fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
        (**self).read_exact(buf)
    }
}
#[stable(feature = "rust1", since = "1.0.0")]
impl<W: Write + ?Sized> Write for Box<W> {
    #[inline]
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> { (**self).write(buf) }

    #[inline]
    fn flush(&mut self) -> io::Result<()> { (**self).flush() }

    #[inline]
    fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
        (**self).write_all(buf)
    }

    #[inline]
    fn write_fmt(&mut self, fmt: fmt::Arguments) -> io::Result<()> {
        (**self).write_fmt(fmt)
    }
}
#[stable(feature = "rust1", since = "1.0.0")]
impl<S: Seek + ?Sized> Seek for Box<S> {
    #[inline]
    fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> { (**self).seek(pos) }
}
#[stable(feature = "rust1", since = "1.0.0")]
impl<B: BufRead + ?Sized> BufRead for Box<B> {
    #[inline]
    fn fill_buf(&mut self) -> io::Result<&[u8]> { (**self).fill_buf() }

    #[inline]
    fn consume(&mut self, amt: usize) { (**self).consume(amt) }

    #[inline]
    fn read_until(&mut self, byte: u8, buf: &mut Vec<u8>) -> io::Result<usize> {
        (**self).read_until(byte, buf)
    }

    #[inline]
    fn read_line(&mut self, buf: &mut String) -> io::Result<usize> {
        (**self).read_line(buf)
    }
}

// =============================================================================
// In-memory buffer implementations

/// Read is implemented for `&[u8]` by copying from the slice.
///
/// Note that reading updates the slice to point to the yet unread part.
/// The slice will be empty when EOF is reached.
#[stable(feature = "rust1", since = "1.0.0")]
impl<'a> Read for &'a [u8] {
    #[inline]
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        let amt = cmp::min(buf.len(), self.len());
        let (a, b) = self.split_at(amt);
        buf[..amt].copy_from_slice(a);
        *self = b;
        Ok(amt)
    }

    #[inline]
    fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
        if buf.len() > self.len() {
            return Err(Error::new(ErrorKind::UnexpectedEof,
                                  "failed to fill whole buffer"));
        }
        let (a, b) = self.split_at(buf.len());
        buf.copy_from_slice(a);
        *self = b;
        Ok(())
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<'a> BufRead for &'a [u8] {
    #[inline]
    fn fill_buf(&mut self) -> io::Result<&[u8]> { Ok(*self) }

    #[inline]
    fn consume(&mut self, amt: usize) { *self = &self[amt..]; }
}

/// Write is implemented for `&mut [u8]` by copying into the slice, overwriting
/// its data.
///
/// Note that writing updates the slice to point to the yet unwritten part.
/// The slice will be empty when it has been completely overwritten.
#[stable(feature = "rust1", since = "1.0.0")]
impl<'a> Write for &'a mut [u8] {
    #[inline]
    fn write(&mut self, data: &[u8]) -> io::Result<usize> {
        let amt = cmp::min(data.len(), self.len());
        let (a, b) = mem::replace(self, &mut []).split_at_mut(amt);
        a.copy_from_slice(&data[..amt]);
        *self = b;
        Ok(amt)
    }

    #[inline]
    fn write_all(&mut self, data: &[u8]) -> io::Result<()> {
        if self.write(data)? == data.len() {
            Ok(())
        } else {
            Err(Error::new(ErrorKind::WriteZero, "failed to write whole buffer"))
        }
    }

    #[inline]
    fn flush(&mut self) -> io::Result<()> { Ok(()) }
}

/// Write is implemented for `Vec<u8>` by appending to the vector.
/// The vector will grow as needed.
#[stable(feature = "rust1", since = "1.0.0")]
impl Write for Vec<u8> {
    #[inline]
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.extend_from_slice(buf);
        Ok(buf.len())
    }

    #[inline]
    fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
        self.extend_from_slice(buf);
        Ok(())
    }

    #[inline]
    fn flush(&mut self) -> io::Result<()> { Ok(()) }
}

#[cfg(test)]
mod tests {
    use io::prelude::*;
    use test;

    #[bench]
    fn bench_read_slice(b: &mut test::Bencher) {
        let buf = [5; 1024];
        let mut dst = [0; 128];

        b.iter(|| {
            let mut rd = &buf[..];
            for _ in 0..8 {
                let _ = rd.read(&mut dst);
                test::black_box(&dst);
            }
        })
    }

    #[bench]
    fn bench_write_slice(b: &mut test::Bencher) {
        let mut buf = [0; 1024];
        let src = [5; 128];

        b.iter(|| {
            let mut wr = &mut buf[..];
            for _ in 0..8 {
                let _ = wr.write_all(&src);
                test::black_box(&wr);
            }
        })
    }

    #[bench]
    fn bench_read_vec(b: &mut test::Bencher) {
        let buf = vec![5; 1024];
        let mut dst = [0; 128];

        b.iter(|| {
            let mut rd = &buf[..];
            for _ in 0..8 {
                let _ = rd.read(&mut dst);
                test::black_box(&dst);
            }
        })
    }

    #[bench]
    fn bench_write_vec(b: &mut test::Bencher) {
        let mut buf = Vec::with_capacity(1024);
        let src = [5; 128];

        b.iter(|| {
            let mut wr = &mut buf[..];
            for _ in 0..8 {
                let _ = wr.write_all(&src);
                test::black_box(&wr);
            }
        })
    }
}
<|endoftext|>