<commit_before>extern crate mysql;
extern crate toml;
extern crate rustc_serialize;
#[macro_use]
extern crate log;
extern crate log4rs;
#[macro_use]
extern crate lazy_static;

mod lib;

use lib::config;
use lib::db;
use lib::logger;
use lib::downloader::DownloadDB;
use lib::downloader::Downloader;
use lib::DownloadError;
use lib::converter::Converter;

use std::fs::{remove_file,remove_dir_all};

const VERSION : &'static str = "0.4";
const CONFIG_PATH : &'static str = "config.cfg";
const LOG_CONFIG: &'static str = "log.conf";
const LOG_PATTERN: &'static str = "%d{%d-%m-%Y %H:%M:%S}\t[%l]\t%f:%L \t%m";
const SLEEP_MS: u32 = 5000;
const CODE_STARTED: i8 = 0;
const CODE_IN_PROGRESS: i8 = 1;
const CODE_SUCCESS: i8 = 2;
const CODE_SUCCESS_WARNINGS: i8 = 3; // finished with warnings
const CODE_FAILED_INTERNAL: i8 = 10; // internal error
const CODE_FAILED_QUALITY: i8 = 11; // qualitz not available
const CODE_FAILED_UNAVAILABLE: i8 = 12; // source unavailable (private / removed)
const TYPE_YT_VIDEO: i16 = 0;
const TYPE_YT_PL: i16 = 1;
const TYPE_TWITCH: i16 = 2;

lazy_static! {
    pub static ref CONFIG: config::Config = {
        println!("Starting yayd-backend v{}",&VERSION);
        config::init_config() //return
    };

}

//#[repr(i8)] broken, enum not usable as of #10292
// enum QueryCodes {
//     Waiting = 0,
//     InProgress = 1,
//     Finished = 2,
//     Failed = 3,
// }

enum Thing { String(String), Bool(bool), None }

fn main() {
    logger::initialize();

    let pool = db::db_connect(db::mysql_options(), SLEEP_MS);
    debug!("cleaning db");
    db::clear_query_states(&pool);

    let converter = Converter::new(&CONFIG.general.ffmpeg_bin_dir,&CONFIG.general.mp3_quality , pool.clone());
    let mut print_pause = true;
    debug!("finished startup");
    loop {
        if let Some(mut downl_db) = db::request_entry(& pool) {
            print_pause = true;
            let qid = downl_db.qid.clone();
            db::set_query_code(&pool, &CODE_IN_PROGRESS, &downl_db.qid).ok().expect("Failed to set query code!");
            db::set_query_state(&pool.clone(),&qid, "started", false);
            let action_result: Result<Thing,DownloadError>;
            {
                let mut left_files: Vec<String> = Vec::with_capacity(2);
                if downl_db.playlist {
                    action_result = handle_playlist(& mut downl_db, &converter,&mut left_files);
                }else{
                    action_result = handle_download(& downl_db, &None, &converter,&mut left_files);
                }

                if !left_files.is_empty() {
                    trace!("cleaning up files");
                    for i in &left_files {
                        match remove_file(&i) {
                            Ok(_) => (trace!("cleaning up {}",i)),
                            Err(e) => warn!("unable to remove file '{}' {}",i,e),
                        }
                    }
                }
            }

            let code: i8 = match action_result {
                Ok(t) => {
                    match t {
                        Thing::Bool(b) => {
                            if b { CODE_SUCCESS_WARNINGS } else { CODE_SUCCESS }
                        }
                        _ => CODE_SUCCESS,
                    }
                },
                Err(e) => {
                    warn!("Error: {:?}", e);
                    if downl_db.source_type == TYPE_TWITCH {
                        match lib::cleanup_temp_folder() {
                            Err(e) => error!("error doing cleanup {:?}", e),
                            Ok(_) => (),
                        }
                    }
                    match e {
                        DownloadError::NotAvailable => CODE_FAILED_UNAVAILABLE,
                        DownloadError::ExtractorError => CODE_FAILED_UNAVAILABLE,
                        DownloadError::QualityNotAvailable => CODE_FAILED_QUALITY,
                        _ => {
                            let details = match e {
                                DownloadError::DBError(s) => s,
                                DownloadError::DownloadError(s) => s,
                                DownloadError::FFMPEGError(s) => s,
                                DownloadError::InternalError(s) => s,
                                _ => unreachable!(),
                            };
                            db::add_query_status(&pool,&qid, &details);
                            CODE_FAILED_INTERNAL
                        },
                    }
                }
            };
            db::set_query_code(&pool, &code,&qid).ok().expect("Failed to set query code!");
			db::set_null_state(&pool, &qid);

        } else {
            if print_pause { debug!("Pausing.."); print_pause = false; }
            std::thread::sleep_ms(SLEEP_MS);
        }
    }
}

/// Download handler
/// Used by the playlist/file handler to download one file
/// Based on the quality it needs to download audio & video separately and convert them together
/// In case of a playlist download it depends on the target download folder & if it should be bezipped
/// In case of a DMCA & turned on lib_use, we're expecting the lib to handle this & return the filename to us.
///
/// If it's a non-zipped single file, it's moved after a successful download, converted etc to the
/// main folder from which it should be downloadable.
/// The original non-ascii & url_encode'd name of the file is stored in the DB
fn handle_download<'a>(downl_db: &DownloadDB, folder: &Option<String>, converter: &Converter, file_db: &mut Vec<String>) -> Result<Thing,DownloadError>{
    //update progress
    let is_zipped = match *folder {
        Some(_) => true,
        None => false,
    };
    if !downl_db.compress {
        db::update_steps(&downl_db.pool.clone(),&downl_db.qid, 1, 0,false);
    }

    let download = Downloader::new(&downl_db, &CONFIG.general);
    //get filename, check for DMCA
    let mut dmca = false; // "succ." dmca -> file already downloaded

    let temp_path = lib::format_file_path(&downl_db.qid, folder.clone(), false);
    let name = match download.get_file_name() { // get filename
        Ok(v) => v,
        Err(DownloadError::DMCAError) => { //now request via lib.. // k if( k == Err(DownloadError::DMCAError) )
            info!("DMCA error!");
            if CONFIG.general.lib_use {
                match download.lib_request_video(1,0, &temp_path) {
                    Err(err) => { warn!("lib-call error {:?}", err); return Err(err); },
                    Ok(v) => { dmca = true; v },
                }
            } else {
                return Err(DownloadError::NotAvailable);
            }
        },
        Err(e) => { // unknown error / restricted source etc.. abort
            error!("Unknown error: {:?}", e);
            return Err(e);
        },
    };

    file_db.push(temp_path.clone());
    let save_path = lib::format_save_path(folder.clone(),&name, lib::get_file_ext(&download));

    trace!("Filename: {}", name);

    let is_splitted_video = if dmca {
        false
    } else {
        lib::is_split_container(&downl_db.quality, &downl_db.source_type)
    };
    let convert_audio = CONFIG.extensions.mp3.contains(&downl_db.quality);

    let total_steps = if dmca {
        if download.is_audio() {
            3
        } else {
            2
        }
    } else if is_splitted_video {
        4
    } else if download.is_audio() {
        3
    } else {
        2
    };

    if !dmca { // on dmca the called lib handles splitt videos, only audio conversion has to handled by us
        if !downl_db.compress {
            db::update_steps(&downl_db.pool.clone(),&downl_db.qid, 2, total_steps,false);
        }

        //download first file, download audio raw source if specified or video
        match download.download_file(&temp_path, convert_audio) {
            Err(e) => {file_db.pop(); return Err(e); },
            Ok(_) => (),
        }

        if is_splitted_video {
            debug!("splitted video");
            // download audio file & convert together
            if !downl_db.compress {
                db::update_steps(&downl_db.pool.clone(),&downl_db.qid, 3, total_steps,false);
            }

            let audio_path = lib::format_file_path(&downl_db.qid, folder.clone(), true);

            trace!("Downloading audio.. {}", audio_path);
            try!(download.download_file(&audio_path, true));
            file_db.push(audio_path.clone());

            if !downl_db.compress {
                db::update_steps(&downl_db.pool.clone(),&downl_db.qid, 4, total_steps,false);
            }

            match converter.merge_files(&downl_db.qid,&temp_path, &audio_path,&save_path.to_string_lossy(), !downl_db.compress) {
                Err(e) => {println!("merge error: {:?}",e); return Err(e);},
                Ok(()) => {},
            }

            try!(remove_file(&audio_path));
            file_db.pop();
        }
    }

    if !is_splitted_video { // if it's no splitted video & no audio we're moving the file, regardless of an earlier DMCA (lib call)
        debug!("no split container");
        if !download.is_audio() {
            try!(lib::move_file(&temp_path, &save_path));
        }
    }

    if download.is_audio(){ // if audio-> convert m4a to mp3 or extract m4a, directly to output file
    	debug!("is audio file");
        if !downl_db.compress {
            db::update_steps(&downl_db.pool.clone(),&downl_db.qid, total_steps, total_steps, false);
        }
        try!(converter.extract_audio(&temp_path, &save_path.to_string_lossy(), convert_audio));
        try!(remove_file(&temp_path));
    }else{
        debug!("no audio");
        if is_splitted_video {
            try!(remove_file(&temp_path));
        }
    }
    file_db.pop();

    if !is_zipped { // add file to list, except it's for zip-compression later (=folder set)
        try!(db::add_file_entry(&downl_db.pool.clone(), &downl_db.qid,&save_path.file_name().unwrap().to_string_lossy(), &name));
    }
    if !downl_db.compress {
        db::update_steps(&downl_db.pool.clone(),&downl_db.qid, total_steps, total_steps, true);
    }

    if folder.is_some() {
        Ok(Thing::String(save_path.to_string_lossy().into_owned()))
    } else {
        Ok(Thing::None)
    }
}

/// Handles a playlist request
/// If zipping isn't requested the downloads will be split up,
/// so for each video in the playlist an own query entry will be created
/// if warnings occured (unavailable video etc) the return will be true
fn handle_playlist<'a>(downl_db: & mut DownloadDB<'a>, converter: &Converter, file_db: &mut Vec<String>) -> Result<Thing, DownloadError>{
    let mut max_steps: i32 = if downl_db.compress { 4 } else { 3 };
    db::update_steps(&downl_db.pool.clone(),&downl_db.qid, 1, max_steps,false);

    let pl_id: i64 = downl_db.qid;
    downl_db.update_folder(format!("{}/{}",&CONFIG.general.temp_dir,pl_id));
    trace!("Folder:  {}",downl_db.folder);

    let db_copy = downl_db.clone();
    let download = Downloader::new(&db_copy, &CONFIG.general);

    let mut playlist_name = String::new();
    if downl_db.compress {
        playlist_name = try!(download.get_playlist_name());
        println!("pl name {}",playlist_name);
        try!(std::fs::create_dir(&downl_db.folder));
        file_db.push(downl_db.folder.clone());
    }
    db::update_steps(&downl_db.pool.clone(),&downl_db.qid, 2, max_steps,false);
    trace!("retriving playlist videos..");
    let file_ids: Vec<String> = try!(download.get_playlist_ids());

    let handler_folder = if downl_db.compress {
        Some(pl_id.to_string())
    }else {
        None
    };

    trace!("got playlist videos");
    max_steps += file_ids.len() as i32;
    let mut current_step = 2;
    let mut warnings = false;
    let mut current_url: String;
    let mut failed_log: String = String::from("Following urls couldn't be downloaded: \n");
    // we'll store all files and delete em later, so we don't need rm -rf
    let mut file_delete_list: Vec<String> = Vec::with_capacity(if downl_db.compress { file_ids.len() } else { 0 });
    for id in file_ids.iter() {
        current_step += 1;
        if downl_db.compress {
            db::update_steps(&downl_db.pool.clone(),&pl_id, current_step, max_steps,false);
        }
        current_url = format!("https://www.youtube.com/watch?v={}",id);
        downl_db.update_video(current_url.clone(), current_step as i64);
        debug!("id: {}",id);
        match handle_download(downl_db, &handler_folder, converter, file_db) {
            Err(e) => { warn!("error downloading {}: {:?}",id,e);
                        failed_log.push_str(&format!("{} {:?}\n", current_url, e));
                        warnings = true;
            },
            Ok(e) => {  if downl_db.compress {
                            match e {
                                Thing::String(v) => file_delete_list.push(v),
                                _ => {error!("handle_download not returning a filename"); panic!();},
                            }
                        }
            },
        }
    }

    if warnings {
        db::add_query_status(&downl_db.pool.clone(),&pl_id, &failed_log);
    }

    trace!("downloaded all videos");
    if downl_db.compress { // zip to file, add to db & remove all sources
        current_step += 1;
        db::update_steps(&downl_db.pool.clone(),&pl_id, current_step, max_steps,false);
        let zip_path = lib::format_save_path(None, &lib::url_sanitize(&playlist_name),"zip");
        trace!("zip file: {} \n zip source {}",zip_path.to_string_lossy(), &downl_db.folder);
        try!(lib::zip_folder(&downl_db.folder, &zip_path));
        try!(db::add_file_entry(&downl_db.pool.clone(), &pl_id,&zip_path.file_name().unwrap().to_string_lossy(), &playlist_name));

        current_step += 1;
        db::update_steps(&downl_db.pool.clone(),&pl_id, current_step, max_steps,false);
        try!(lib::delete_files(file_delete_list));
        try!(remove_dir_all(&downl_db.folder));
        file_db.pop();
    }

    Ok(Thing::Bool(warnings))
}
<commit_msg>changed to non-copy download_db<commit_after>extern crate mysql;
extern crate toml;
extern crate rustc_serialize;
#[macro_use]
extern crate log;
extern crate log4rs;
#[macro_use]
extern crate lazy_static;

mod lib;

use lib::config;
use lib::db;
use lib::logger;
use lib::downloader::DownloadDB;
use lib::downloader::Downloader;
use lib::DownloadError;
use lib::converter::Converter;

use std::fs::{remove_file,remove_dir_all};

const VERSION : &'static str = "0.4";
const CONFIG_PATH : &'static str = "config.cfg";
const LOG_CONFIG: &'static str = "log.conf";
const LOG_PATTERN: &'static str = "%d{%d-%m-%Y %H:%M:%S}\t[%l]\t%f:%L \t%m";
const SLEEP_MS: u32 = 5000;
const CODE_STARTED: i8 = 0;
const CODE_IN_PROGRESS: i8 = 1;
const CODE_SUCCESS: i8 = 2;
const CODE_SUCCESS_WARNINGS: i8 = 3; // finished with warnings
const CODE_FAILED_INTERNAL: i8 = 10; // internal error
const CODE_FAILED_QUALITY: i8 = 11; // qualitz not available
const CODE_FAILED_UNAVAILABLE: i8 = 12; // source unavailable (private / removed)
const TYPE_YT_VIDEO: i16 = 0;
const TYPE_YT_PL: i16 = 1;
const TYPE_TWITCH: i16 = 2;

lazy_static! {
    pub static ref CONFIG: config::Config = {
        println!("Starting yayd-backend v{}",&VERSION);
        config::init_config() //return
    };

}

//#[repr(i8)] broken, enum not usable as of #10292
// enum QueryCodes {
//     Waiting = 0,
//     InProgress = 1,
//     Finished = 2,
//     Failed = 3,
// }

enum Thing { String(String), Bool(bool), None }

fn main() {
    logger::initialize();

    let pool = db::db_connect(db::mysql_options(), SLEEP_MS);
    debug!("cleaning db");
    db::clear_query_states(&pool);

    let converter = Converter::new(&CONFIG.general.ffmpeg_bin_dir,&CONFIG.general.mp3_quality , pool.clone());
    let mut print_pause = true;
    debug!("finished startup");
    loop {
        if let Some(mut downl_db) = db::request_entry(& pool) {
            print_pause = true;
            let qid = downl_db.qid.clone();
            db::set_query_code(&pool, &CODE_IN_PROGRESS, &downl_db.qid).ok().expect("Failed to set query code!");
            db::set_query_state(&pool.clone(),&qid, "started", false);
            let action_result: Result<Thing,DownloadError>;
            {
                let mut left_files: Vec<String> = Vec::with_capacity(2);
                if downl_db.playlist {
                    action_result = handle_playlist(& mut downl_db, &converter,&mut left_files);
                }else{
                    action_result = handle_download(& downl_db, &None, &converter,&mut left_files);
                }

                if !left_files.is_empty() {
                    trace!("cleaning up files");
                    for i in &left_files {
                        match remove_file(&i) {
                            Ok(_) => (trace!("cleaning up {}",i)),
                            Err(e) => warn!("unable to remove file '{}' {}",i,e),
                        }
                    }
                }
            }

            let code: i8 = match action_result {
                Ok(t) => {
                    match t {
                        Thing::Bool(b) => {
                            if b { CODE_SUCCESS_WARNINGS } else { CODE_SUCCESS }
                        }
                        _ => CODE_SUCCESS,
                    }
                },
                Err(e) => {
                    warn!("Error: {:?}", e);
                    if downl_db.source_type == TYPE_TWITCH {
                        match lib::cleanup_temp_folder() {
                            Err(e) => error!("error doing cleanup {:?}", e),
                            Ok(_) => (),
                        }
                    }
                    match e {
                        DownloadError::NotAvailable => CODE_FAILED_UNAVAILABLE,
                        DownloadError::ExtractorError => CODE_FAILED_UNAVAILABLE,
                        DownloadError::QualityNotAvailable => CODE_FAILED_QUALITY,
                        _ => {
                            let details = match e {
                                DownloadError::DBError(s) => s,
                                DownloadError::DownloadError(s) => s,
                                DownloadError::FFMPEGError(s) => s,
                                DownloadError::InternalError(s) => s,
                                _ => unreachable!(),
                            };
                            db::add_query_status(&pool,&qid, &details);
                            CODE_FAILED_INTERNAL
                        },
                    }
                }
            };
            db::set_query_code(&pool, &code,&qid).ok().expect("Failed to set query code!");
			db::set_null_state(&pool, &qid);

        } else {
            if print_pause { debug!("Pausing.."); print_pause = false; }
            std::thread::sleep_ms(SLEEP_MS);
        }
    }
}

/// Download handler
/// Used by the playlist/file handler to download one file
/// Based on the quality it needs to download audio & video separately and convert them together
/// In case of a playlist download it depends on the target download folder & if it should be bezipped
/// In case of a DMCA & turned on lib_use, we're expecting the lib to handle this & return the filename to us.
///
/// If it's a non-zipped single file, it's moved after a successful download, converted etc to the
/// main folder from which it should be downloadable.
/// The original non-ascii & url_encode'd name of the file is stored in the DB
fn handle_download<'a>(downl_db: &DownloadDB, folder: &Option<String>, converter: &Converter, file_db: &mut Vec<String>) -> Result<Thing,DownloadError>{
    //update progress
    let is_zipped = match *folder {
        Some(_) => true,
        None => false,
    };
    if !downl_db.compress {
        db::update_steps(&downl_db.pool.clone(),&downl_db.qid, 1, 0,false);
    }

    let download = Downloader::new(&downl_db, &CONFIG.general);
    //get filename, check for DMCA
    let mut dmca = false; // "succ." dmca -> file already downloaded

    let temp_path = lib::format_file_path(&downl_db.qid, folder.clone(), false);
    let name = match download.get_file_name() { // get filename
        Ok(v) => v,
        Err(DownloadError::DMCAError) => { //now request via lib.. // k if( k == Err(DownloadError::DMCAError) )
            info!("DMCA error!");
            if CONFIG.general.lib_use {
                match download.lib_request_video(1,0, &temp_path) {
                    Err(err) => { warn!("lib-call error {:?}", err); return Err(err); },
                    Ok(v) => { dmca = true; v },
                }
            } else {
                return Err(DownloadError::NotAvailable);
            }
        },
        Err(e) => { // unknown error / restricted source etc.. abort
            error!("Unknown error: {:?}", e);
            return Err(e);
        },
    };

    file_db.push(temp_path.clone());
    let save_path = lib::format_save_path(folder.clone(),&name, lib::get_file_ext(&download));

    trace!("Filename: {}", name);

    let is_splitted_video = if dmca {
        false
    } else {
        lib::is_split_container(&downl_db.quality, &downl_db.source_type)
    };
    let convert_audio = CONFIG.extensions.mp3.contains(&downl_db.quality);

    let total_steps = if dmca {
        if download.is_audio() {
            3
        } else {
            2
        }
    } else if is_splitted_video {
        4
    } else if download.is_audio() {
        3
    } else {
        2
    };

    if !dmca { // on dmca the called lib handles splitt videos, only audio conversion has to handled by us
        if !downl_db.compress {
            db::update_steps(&downl_db.pool.clone(),&downl_db.qid, 2, total_steps,false);
        }

        //download first file, download audio raw source if specified or video
        match download.download_file(&temp_path, convert_audio) {
            Err(e) => {file_db.pop(); return Err(e); },
            Ok(_) => (),
        }

        if is_splitted_video {
            debug!("splitted video");
            // download audio file & convert together
            if !downl_db.compress {
                db::update_steps(&downl_db.pool.clone(),&downl_db.qid, 3, total_steps,false);
            }

            let audio_path = lib::format_file_path(&downl_db.qid, folder.clone(), true);

            trace!("Downloading audio.. {}", audio_path);
            try!(download.download_file(&audio_path, true));
            file_db.push(audio_path.clone());

            if !downl_db.compress {
                db::update_steps(&downl_db.pool.clone(),&downl_db.qid, 4, total_steps,false);
            }

            match converter.merge_files(&downl_db.qid,&temp_path, &audio_path,&save_path.to_string_lossy(), !downl_db.compress) {
                Err(e) => {println!("merge error: {:?}",e); return Err(e);},
                Ok(()) => {},
            }

            try!(remove_file(&audio_path));
            file_db.pop();
        }
    }

    if !is_splitted_video { // if it's no splitted video & no audio we're moving the file, regardless of an earlier DMCA (lib call)
        debug!("no split container");
        if !download.is_audio() {
            try!(lib::move_file(&temp_path, &save_path));
        }
    }

    if download.is_audio(){ // if audio-> convert m4a to mp3 or extract m4a, directly to output file
    	debug!("is audio file");
        if !downl_db.compress {
            db::update_steps(&downl_db.pool.clone(),&downl_db.qid, total_steps, total_steps, false);
        }
        try!(converter.extract_audio(&temp_path, &save_path.to_string_lossy(), convert_audio));
        try!(remove_file(&temp_path));
    }else{
        debug!("no audio");
        if is_splitted_video {
            try!(remove_file(&temp_path));
        }
    }
    file_db.pop();

    if !is_zipped { // add file to list, except it's for zip-compression later (=folder set)
        try!(db::add_file_entry(&downl_db.pool.clone(), &downl_db.qid,&save_path.file_name().unwrap().to_string_lossy(), &name));
    }
    if !downl_db.compress {
        db::update_steps(&downl_db.pool.clone(),&downl_db.qid, total_steps, total_steps, true);
    }

    if folder.is_some() {
        Ok(Thing::String(save_path.to_string_lossy().into_owned()))
    } else {
        Ok(Thing::None)
    }
}

/// Handles a playlist request
/// If zipping isn't requested the downloads will be split up,
/// so for each video in the playlist an own query entry will be created
/// if warnings occured (unavailable video etc) the return will be true
fn handle_playlist<'a>(downl_db: & mut DownloadDB<'a>, converter: &Converter, file_db: &mut Vec<String>) -> Result<Thing, DownloadError>{
    let mut max_steps: i32 = if downl_db.compress { 4 } else { 3 };
    db::update_steps(&downl_db.pool.clone(),&downl_db.qid, 1, max_steps,false);

    let pl_id: i64 = downl_db.qid;
    downl_db.update_folder(format!("{}/{}",&CONFIG.general.temp_dir,pl_id));
    trace!("Folder:  {}",downl_db.folder);

    let mut playlist_name;
    let file_ids: Vec<String>;
    {
        let download = Downloader::new(&downl_db, &CONFIG.general);
        playlist_name = String::new();
        if downl_db.compress {
            playlist_name = try!(download.get_playlist_name());
            println!("pl name {}",playlist_name);
            try!(std::fs::create_dir(&downl_db.folder));
            file_db.push(downl_db.folder.clone());
        }

        db::update_steps(&downl_db.pool.clone(),&downl_db.qid, 2, max_steps,false);
        trace!("retriving playlist videos..");
        file_ids = try!(download.get_playlist_ids());
    }

    let handler_folder = if downl_db.compress {
        Some(pl_id.to_string())
    }else {
        None
    };

    trace!("got playlist videos");
    max_steps += file_ids.len() as i32;
    let mut current_step = 2;
    let mut warnings = false;
    let mut current_url: String;
    let mut failed_log: String = String::from("Following urls couldn't be downloaded: \n");
    // we'll store all files and delete em later, so we don't need rm -rf
    let mut file_delete_list: Vec<String> = Vec::with_capacity(if downl_db.compress { file_ids.len() } else { 0 });
    for id in file_ids.iter() {
        current_step += 1;
        if downl_db.compress {
            db::update_steps(&downl_db.pool.clone(),&pl_id, current_step, max_steps,false);
        }
        current_url = format!("https://www.youtube.com/watch?v={}",id);
        downl_db.update_video(current_url.clone(), current_step as i64);
        debug!("id: {}",id);
        match handle_download(downl_db, &handler_folder, converter, file_db) {
            Err(e) => { warn!("error downloading {}: {:?}",id,e);
                        failed_log.push_str(&format!("{} {:?}\n", current_url, e));
                        warnings = true;
            },
            Ok(e) => {  if downl_db.compress {
                            match e {
                                Thing::String(v) => file_delete_list.push(v),
                                _ => {error!("handle_download not returning a filename"); panic!();},
                            }
                        }
            },
        }
    }

    if warnings {
        db::add_query_status(&downl_db.pool.clone(),&pl_id, &failed_log);
    }

    trace!("downloaded all videos");
    if downl_db.compress { // zip to file, add to db & remove all sources
        current_step += 1;
        db::update_steps(&downl_db.pool.clone(),&pl_id, current_step, max_steps,false);
        let zip_path = lib::format_save_path(None, &lib::url_sanitize(&playlist_name),"zip");
        trace!("zip file: {} \n zip source {}",zip_path.to_string_lossy(), &downl_db.folder);
        try!(lib::zip_folder(&downl_db.folder, &zip_path));
        try!(db::add_file_entry(&downl_db.pool.clone(), &pl_id,&zip_path.file_name().unwrap().to_string_lossy(), &playlist_name));

        current_step += 1;
        db::update_steps(&downl_db.pool.clone(),&pl_id, current_step, max_steps,false);
        try!(lib::delete_files(file_delete_list));
        try!(remove_dir_all(&downl_db.folder));
        file_db.pop();
    }

    Ok(Thing::Bool(warnings))
}
<|endoftext|>


<commit_before>#![feature(globs)] // Allow global imports

extern crate cgmath;
extern crate gl;
extern crate graphics;
extern crate native;
extern crate piston;
extern crate sdl2_game_window;

use sdl2_game_window::GameWindowSDL2;

use cgmath::array::*;
use cgmath::vector::{Vector3, Vector4};
use piston::*;
use gl::types::*;
use std::mem;
use std::ptr;
use std::str;

pub struct Color4<T> { inner: Vector4<T>, }

impl<T: Clone> Clone for Color4<T> {
  fn clone(&self) -> Color4<T> {
    Color4 { inner: self.inner.clone(), }
  }
}

impl<T: Clone> Color4<T> {
  fn new(v: &Vector4<T>) -> Color4<T> {
    Color4 { inner: v.clone(), }
  }
}

pub struct Vertex<T> {
  position: Vector3<T>,
  color: Color4<T>,
}

impl<T: Clone> Clone for Vertex<T> {
  fn clone(&self) -> Vertex<T> {
    Vertex {
      position: self.position.clone(),
      color: self.color.clone(),
    }
  }
}

impl<T: Clone> Vertex<T> {
  fn new(v: &Vector3<T>, c: &Color4<T>) -> Vertex<T> {
    Vertex {
      position: v.clone(),
      color: c.clone(),
    }
  }
}

pub struct Triangle<T> {
  vertices: [Vertex<T>, ..3],
}

impl<T: Clone> Clone for Triangle<T> {
  fn clone(&self) -> Triangle<T> {
    Triangle {
      vertices: [self.vertices[0].clone(), self.vertices[1].clone(), self.vertices[2].clone()],
    }
  }
}

pub struct App {
  vao: u32,
  vbo: u32,
  vertex_data: Vec<Triangle<GLfloat>>,
  projection_matrix: cgmath::matrix::Matrix4<GLfloat>,
  shader_program: u32,
}

pub fn translate(t: &Vector3<GLfloat>) -> cgmath::matrix::Matrix4<GLfloat> {
  cgmath::matrix::Matrix4::new(
    1.0, 0.0, 0.0, 0.0,
    0.0, 1.0, 0.0, 0.0,
    0.0, 0.0, 1.0, 0.0,
    t.x, t.y, t.z, 1.0,
  )
}

impl<T: Clone> Triangle<T> {
  fn new(
      v1: &Vertex<T>,
      v2: &Vertex<T>,
      v3: &Vertex<T>)
      -> Triangle<T> {
    Triangle {
      vertices: [v1.clone(), v2.clone(), v3.clone()],
    }
  }
}

impl Game for App {
  fn key_press(&mut self, _args: &KeyPressArgs) {
    match _args.key {
      piston::keyboard::A => {
        self.transform_projection(&translate(&Vector3::new(0.1, 0.0, 0.0)));
      },
      piston::keyboard::D => {
        self.transform_projection(&translate(&Vector3::new(-0.1, 0.0, 0.0)));
      },
      piston::keyboard::LShift => {
        self.transform_projection(&translate(&Vector3::new(0.0, 0.1, 0.0)));
      },
      piston::keyboard::Space => {
        self.transform_projection(&translate(&Vector3::new(0.0, -0.1, 0.0)));
      },
      piston::keyboard::W => {
        self.transform_projection(&translate(&Vector3::new(0.0, 0.0, 0.1)));
      },
      piston::keyboard::S => {
        self.transform_projection(&translate(&Vector3::new(0.0, 0.0, -0.1)));
      },
      _ => {},
    }
  }

  fn load(&mut self) {
    let vs = compile_shader(VS_SRC, gl::VERTEX_SHADER);
    let fs = compile_shader(FS_SRC, gl::FRAGMENT_SHADER);

    self.shader_program = link_program(vs, fs);

    unsafe {
      // Create Vertex Array Object
      gl::GenVertexArrays(1, &mut self.vao);
      gl::BindVertexArray(self.vao);

      let raw_data = mem::transmute(&self.vertex_data.slice(0, self.vertex_data.len())[0]);

      // Create a Vertex Buffer Object and copy the vertex data to it
      gl::GenBuffers(1, &mut self.vbo);
      gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo);
      gl::BufferData(gl::ARRAY_BUFFER,
                      (self.vertex_data.len() * mem::size_of::<Triangle<GLfloat>>()) as GLsizeiptr,
                      raw_data,
                      gl::STATIC_DRAW);

      gl::UseProgram(self.shader_program);
      let loc = gl::GetUniformLocation(self.shader_program, "proj_matrix".to_c_str().unwrap());
      if loc == -1 {
        println!("couldn't read matrix");
      } else {
        self.projection_matrix = cgmath::projection::frustum::<GLfloat>(-1.0, 1.0, -1.0, 1.0, 0.1, 2.0);
        self.transform_projection(&translate(&Vector3::new(0.0, 0.0, -0.1)));
        gl::UniformMatrix4fv(loc, 1, 0, mem::transmute(self.projection_matrix.ptr()));
      }
      "out_color".with_c_str(|ptr| gl::BindFragDataLocation(self.shader_program, 0, ptr));

      // Specify the layout of the vertex data
      let pos_attr = "position".with_c_str(|ptr| gl::GetAttribLocation(self.shader_program, ptr));
      gl::EnableVertexAttribArray(pos_attr as GLuint);
      let color_attr = "in_color".with_c_str(|ptr| gl::GetAttribLocation(self.shader_program, ptr));
      gl::EnableVertexAttribArray(color_attr as GLuint);

      gl::VertexAttribPointer(
          pos_attr as GLuint,
          (mem::size_of::<Vector3<GLfloat>>() / mem::size_of::<GLfloat>()) as i32,
          gl::FLOAT,
          gl::FALSE as GLboolean,
          mem::size_of::<Vertex<GLfloat>>() as i32,
          ptr::null(),
      );
      gl::VertexAttribPointer(
          color_attr as GLuint,
          (mem::size_of::<Color4<GLfloat>>() / mem::size_of::<GLfloat>()) as i32,
          gl::FLOAT,
          gl::FALSE as GLboolean,
          mem::size_of::<Vertex<GLfloat>>() as i32,
          ptr::null().offset(mem::size_of::<Vector3<GLfloat>>() as int),
      );
    }

    gl::ClearColor(0.0, 0.0, 0.0, 1.0);
  }

  fn render(&mut self, _:&RenderArgs) {
    gl::Clear(gl::COLOR_BUFFER_BIT);

    // Draw a triangle from the 3 vertices
    gl::DrawArrays(gl::TRIANGLES, 0, 3);
  }
}

impl App {
  pub fn new() -> App {
    App {
      vao: 0,
      vbo: 0,
      vertex_data: Vec::from_slice([
        Triangle::new(
          &Vertex::new(&Vector3::new( 0.0,  0.5, 0.0), &Color4::new(&Vector4::new(1.0, 0.0, 0.0, 1.0))),
          &Vertex::new(&Vector3::new( 0.5, -0.5, 0.0), &Color4::new(&Vector4::new(0.0, 1.0, 0.0, 1.0))),
          &Vertex::new(&Vector3::new(-0.5, -0.5, 0.0), &Color4::new(&Vector4::new(0.0, 0.0, 1.0, 1.0))),
        ),
      ]),
      shader_program: -1 as u32,
      projection_matrix: cgmath::matrix::Matrix4::from_value(0.0),
    }
  }

  pub fn transform_projection(&mut self, t: &cgmath::matrix::Matrix4<GLfloat>) {
    unsafe {
      let loc = gl::GetUniformLocation(self.shader_program, "proj_matrix".to_c_str().unwrap());
      if loc == -1 {
        println!("couldn't read matrix");
      } else {
        self.projection_matrix = self.projection_matrix * *t;
        gl::UniformMatrix4fv(loc, 1, 0, mem::transmute(self.projection_matrix.ptr()));
      }
    }
  }

  pub fn destroy(&mut self) {
    unsafe {
      gl::DeleteBuffers(1, &self.vbo);
      gl::DeleteVertexArrays(1, &self.vao);
    }
  }
}

// Shader sources
static VS_SRC: &'static str =
   "#version 150\n\
uniform mat4 proj_matrix;\n\
in vec3 position;\n\
in vec4 in_color;\n\
varying vec4 color;\n\
void main() {\n\
gl_Position = proj_matrix * vec4(position, 1.0);\n\
color = in_color;\n\
}";

static FS_SRC: &'static str =
   "#version 150\n\
varying vec4 color;\n\
out vec4 out_color;\n\
void main() {\n\
out_color = color;\n\
}";

fn compile_shader(src: &str, ty: GLenum) -> GLuint {
    let shader = gl::CreateShader(ty);
    unsafe {
        // Attempt to compile the shader
        src.with_c_str(|ptr| gl::ShaderSource(shader, 1, &ptr, ptr::null()));
        gl::CompileShader(shader);

        // Get the compile status
        let mut status = gl::FALSE as GLint;
        gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut status);

        // Fail on error
        if status != (gl::TRUE as GLint) {
            let mut len = 0;
            gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len);
            let mut buf = Vec::from_elem(len as uint - 1, 0u8); // subtract 1 to skip the trailing null character
            gl::GetShaderInfoLog(shader, len, ptr::mut_null(), buf.as_mut_ptr() as *mut GLchar);
            fail!("{}", str::from_utf8(buf.as_slice()).expect("ShaderInfoLog not valid utf8"));
        }
    }
    shader
}

fn link_program(vs: GLuint, fs: GLuint) -> GLuint {
    let program = gl::CreateProgram();

    gl::AttachShader(program, vs);
    gl::AttachShader(program, fs);
    gl::LinkProgram(program);

    unsafe {
        // Get the link status
        let mut status = gl::FALSE as GLint;
        gl::GetProgramiv(program, gl::LINK_STATUS, &mut status);

        // Fail on error
        if status != (gl::TRUE as GLint) {
            let mut len: GLint = 0;
            gl::GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut len);
            let mut buf = Vec::from_elem(len as uint - 1, 0u8); // subtract 1 to skip the trailing null character
            gl::GetProgramInfoLog(program, len, ptr::mut_null(), buf.as_mut_ptr() as *mut GLchar);
            fail!("{}", str::from_utf8(buf.as_slice()).expect("ProgramInfoLog not valid utf8"));
        }
    }

    program
}

fn main() {
  let mut window = GameWindowSDL2::new(
    GameWindowSettings {
      title: "playform".to_string(),
      size: [800, 600],
      fullscreen: false,
      exit_on_esc: false,
    }
  );

  App::new().run(&mut window, &GameIteratorSettings {
    updates_per_second: 30,
    max_frames_per_second: 30,
  });
}
<commit_msg>multiple triangles render properly/cleanups<commit_after>#![feature(globs)] // Allow global imports

extern crate cgmath;
extern crate gl;
extern crate graphics;
extern crate native;
extern crate piston;
extern crate sdl2_game_window;

use sdl2_game_window::GameWindowSDL2;

use cgmath::array::*;
use cgmath::vector::{Vector3, Vector4};
use piston::*;
use gl::types::*;
use std::mem;
use std::ptr;
use std::str;

pub struct Color4<T> { inner: Vector4<T>, }

impl<T: Clone> Clone for Color4<T> {
  fn clone(&self) -> Color4<T> {
    Color4 { inner: self.inner.clone(), }
  }
}

impl<T: Clone> Color4<T> {
  fn new(v: &Vector4<T>) -> Color4<T> {
    Color4 { inner: v.clone(), }
  }
}

pub struct Vertex<T> {
  position: Vector3<T>,
  color: Color4<T>,
}

impl<T: Clone> Clone for Vertex<T> {
  fn clone(&self) -> Vertex<T> {
    Vertex {
      position: self.position.clone(),
      color: self.color.clone(),
    }
  }
}

impl<T: Clone> Vertex<T> {
  fn new(v: &Vector3<T>, c: &Color4<T>) -> Vertex<T> {
    Vertex {
      position: v.clone(),
      color: c.clone(),
    }
  }
}

pub struct Triangle<T> {
  vertices: [Vertex<T>, ..3],
}

impl<T: Clone> Clone for Triangle<T> {
  fn clone(&self) -> Triangle<T> {
    Triangle {
      vertices: [self.vertices[0].clone(), self.vertices[1].clone(), self.vertices[2].clone()],
    }
  }
}

pub struct App {
  vao: u32,
  vbo: u32,
  vertex_data: Vec<Triangle<GLfloat>>,
  projection_matrix: cgmath::matrix::Matrix4<GLfloat>,
  shader_program: u32,
}

pub fn translate(t: &Vector3<GLfloat>) -> cgmath::matrix::Matrix4<GLfloat> {
  cgmath::matrix::Matrix4::new(
    1.0, 0.0, 0.0, 0.0,
    0.0, 1.0, 0.0, 0.0,
    0.0, 0.0, 1.0, 0.0,
    t.x, t.y, t.z, 1.0,
  )
}

impl<T: Clone> Triangle<T> {
  fn new(
      v1: &Vertex<T>,
      v2: &Vertex<T>,
      v3: &Vertex<T>)
      -> Triangle<T> {
    Triangle {
      vertices: [v1.clone(), v2.clone(), v3.clone()],
    }
  }
}

impl Game for App {
  fn key_press(&mut self, _args: &KeyPressArgs) {
    match _args.key {
      piston::keyboard::A => {
        self.transform_projection(&translate(&Vector3::new(0.1, 0.0, 0.0)));
      },
      piston::keyboard::D => {
        self.transform_projection(&translate(&Vector3::new(-0.1, 0.0, 0.0)));
      },
      piston::keyboard::LShift => {
        self.transform_projection(&translate(&Vector3::new(0.0, 0.1, 0.0)));
      },
      piston::keyboard::Space => {
        self.transform_projection(&translate(&Vector3::new(0.0, -0.1, 0.0)));
      },
      piston::keyboard::W => {
        self.transform_projection(&translate(&Vector3::new(0.0, 0.0, 0.1)));
      },
      piston::keyboard::S => {
        self.transform_projection(&translate(&Vector3::new(0.0, 0.0, -0.1)));
      },
      _ => {},
    }
  }

  fn load(&mut self) {
    let vs = compile_shader(VS_SRC, gl::VERTEX_SHADER);
    let fs = compile_shader(FS_SRC, gl::FRAGMENT_SHADER);

    self.shader_program = link_program(vs, fs);

    unsafe {
      // Create Vertex Array Object
      gl::GenVertexArrays(1, &mut self.vao);
      gl::BindVertexArray(self.vao);

      // Create a Vertex Buffer Object and copy the vertex data to it
      gl::GenBuffers(1, &mut self.vbo);
      gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo);
      gl::BufferData(gl::ARRAY_BUFFER,
                      (self.vertex_data.len() * mem::size_of::<Triangle<GLfloat>>()) as GLsizeiptr,
                      mem::transmute(self.vertex_data.get(0)),
                      gl::STATIC_DRAW);

      gl::UseProgram(self.shader_program);
      let loc = gl::GetUniformLocation(self.shader_program, "proj_matrix".to_c_str().unwrap());
      if loc == -1 {
        println!("couldn't read matrix");
      } else {
        self.projection_matrix = cgmath::projection::frustum::<GLfloat>(-1.0, 1.0, -1.0, 1.0, 0.1, 2.0);
        self.transform_projection(&translate(&Vector3::new(0.0, 0.0, -0.1)));
      }
      "out_color".with_c_str(|ptr| gl::BindFragDataLocation(self.shader_program, 0, ptr));

      // Specify the layout of the vertex data
      let pos_attr = "position".with_c_str(|ptr| gl::GetAttribLocation(self.shader_program, ptr));
      gl::EnableVertexAttribArray(pos_attr as GLuint);
      let color_attr = "in_color".with_c_str(|ptr| gl::GetAttribLocation(self.shader_program, ptr));
      gl::EnableVertexAttribArray(color_attr as GLuint);

      gl::VertexAttribPointer(
          pos_attr as GLuint,
          (mem::size_of::<Vector3<GLfloat>>() / mem::size_of::<GLfloat>()) as i32,
          gl::FLOAT,
          gl::FALSE as GLboolean,
          mem::size_of::<Vertex<GLfloat>>() as i32,
          ptr::null(),
      );
      gl::VertexAttribPointer(
          color_attr as GLuint,
          (mem::size_of::<Color4<GLfloat>>() / mem::size_of::<GLfloat>()) as i32,
          gl::FLOAT,
          gl::FALSE as GLboolean,
          mem::size_of::<Vertex<GLfloat>>() as i32,
          ptr::null().offset(mem::size_of::<Vector3<GLfloat>>() as int),
      );
    }

    gl::ClearColor(0.0, 0.0, 0.0, 1.0);
  }

  fn render(&mut self, _:&RenderArgs) {
    gl::Clear(gl::COLOR_BUFFER_BIT);

    gl::DrawArrays(gl::TRIANGLES, 0, 3 * self.vertex_data.len() as i32);
  }
}

impl App {
  pub fn new() -> App {
    App {
      vao: 0,
      vbo: 0,
      vertex_data: Vec::from_slice([
        Triangle::new(
          &Vertex::new(&Vector3::new( 0.0,  0.5, 0.0), &Color4::new(&Vector4::new(1.0, 0.0, 0.0, 1.0))),
          &Vertex::new(&Vector3::new( 0.5, -0.5, 0.0), &Color4::new(&Vector4::new(0.0, 1.0, 0.0, 1.0))),
          &Vertex::new(&Vector3::new(-0.5, -0.5, 0.0), &Color4::new(&Vector4::new(0.0, 0.0, 1.0, 1.0))),
        ),
        Triangle::new(
          &Vertex::new(&Vector3::new( 0.0,  0.5, 0.0), &Color4::new(&Vector4::new(1.0, 0.0, 0.0, 1.0))),
          &Vertex::new(&Vector3::new(-0.5, -0.5, 0.0), &Color4::new(&Vector4::new(0.0, 1.0, 0.0, 1.0))),
          &Vertex::new(&Vector3::new(-1.0,  0.5, 0.0), &Color4::new(&Vector4::new(0.0, 0.0, 1.0, 1.0))),
        ),
      ]),
      shader_program: -1 as u32,
      projection_matrix: cgmath::matrix::Matrix4::from_value(0.0),
    }
  }

  pub fn transform_projection(&mut self, t: &cgmath::matrix::Matrix4<GLfloat>) {
    unsafe {
      let loc = gl::GetUniformLocation(self.shader_program, "proj_matrix".to_c_str().unwrap());
      if loc == -1 {
        println!("couldn't read matrix");
      } else {
        self.projection_matrix = self.projection_matrix * *t;
        gl::UniformMatrix4fv(loc, 1, 0, mem::transmute(self.projection_matrix.ptr()));
      }
    }
  }

  pub fn destroy(&mut self) {
    unsafe {
      gl::DeleteBuffers(1, &self.vbo);
      gl::DeleteVertexArrays(1, &self.vao);
    }
  }
}

// Shader sources
static VS_SRC: &'static str =
   "#version 150\n\
uniform mat4 proj_matrix;\n\
in vec3 position;\n\
in vec4 in_color;\n\
varying vec4 color;\n\
void main() {\n\
gl_Position = proj_matrix * vec4(position, 1.0);\n\
color = in_color;\n\
}";

static FS_SRC: &'static str =
   "#version 150\n\
varying vec4 color;\n\
out vec4 out_color;\n\
void main() {\n\
out_color = color;\n\
}";

fn compile_shader(src: &str, ty: GLenum) -> GLuint {
    let shader = gl::CreateShader(ty);
    unsafe {
        // Attempt to compile the shader
        src.with_c_str(|ptr| gl::ShaderSource(shader, 1, &ptr, ptr::null()));
        gl::CompileShader(shader);

        // Get the compile status
        let mut status = gl::FALSE as GLint;
        gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut status);

        // Fail on error
        if status != (gl::TRUE as GLint) {
            let mut len = 0;
            gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len);
            let mut buf = Vec::from_elem(len as uint - 1, 0u8); // subtract 1 to skip the trailing null character
            gl::GetShaderInfoLog(shader, len, ptr::mut_null(), buf.as_mut_ptr() as *mut GLchar);
            fail!("{}", str::from_utf8(buf.as_slice()).expect("ShaderInfoLog not valid utf8"));
        }
    }
    shader
}

fn link_program(vs: GLuint, fs: GLuint) -> GLuint {
    let program = gl::CreateProgram();

    gl::AttachShader(program, vs);
    gl::AttachShader(program, fs);
    gl::LinkProgram(program);

    unsafe {
        // Get the link status
        let mut status = gl::FALSE as GLint;
        gl::GetProgramiv(program, gl::LINK_STATUS, &mut status);

        // Fail on error
        if status != (gl::TRUE as GLint) {
            let mut len: GLint = 0;
            gl::GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut len);
            let mut buf = Vec::from_elem(len as uint - 1, 0u8); // subtract 1 to skip the trailing null character
            gl::GetProgramInfoLog(program, len, ptr::mut_null(), buf.as_mut_ptr() as *mut GLchar);
            fail!("{}", str::from_utf8(buf.as_slice()).expect("ProgramInfoLog not valid utf8"));
        }
    }

    program
}

fn main() {
  let mut window = GameWindowSDL2::new(
    GameWindowSettings {
      title: "playform".to_string(),
      size: [800, 600],
      fullscreen: false,
      exit_on_esc: false,
    }
  );

  App::new().run(&mut window, &GameIteratorSettings {
    updates_per_second: 30,
    max_frames_per_second: 30,
  });
}
<|endoftext|>


<commit_before>extern crate hyper;
extern crate serde;
extern crate serde_json;

use self::hyper::{Url, Client};

use GpsCoordinates;
use CompleteGhi;

// Retrieval process:
// INPUT: GPS coordinates, month you're interested in
// OUTPUT: insolation at the given month and location
// * make an API call for the given GPS coords
// * parse the JSON into a Rust struct
// * pick the right month from that struct and return that

pub fn average_ghi(location: &GpsCoordinates) -> CompleteGhi {
    let client = Client::new();

    let mut url = Url::parse("https://developer.nrel.gov/api/solar/solar_resource/v1.json")
                      .unwrap();

    url.set_query_from_pairs(&[("api_key", ::NREL_API_KEY),
                               ("lat", &location.lat.to_string()),
                               ("lon", &location.long.to_string())]);

    // println!("{:?} {}", url, url.serialize());

    let res = client.get(url).send().unwrap();
    // println!("{:?}", res);
    assert_eq!(res.status, hyper::Ok);

    let json: serde_json::Value = serde_json::de::from_reader(res).unwrap();

    average_ghi_from_json(&json)

}

/// Parse the JSON returned by NREL's API to extract the average Global Horizontal Irradiance (GHI)
fn average_ghi_from_json(val: &serde_json::Value) -> CompleteGhi {
    let avg_ghi = val.find_path(&["outputs", "avg_ghi"])
                     .and_then(|x| x.as_object())
                     .unwrap();

    let months = avg_ghi.get("monthly").and_then(|x| x.as_object()).unwrap();

    let get_month = |month| months.get(month).and_then(|x| x.as_f64()).unwrap();

    CompleteGhi {
        annual: avg_ghi.get("annual").and_then(|x| x.as_f64()).unwrap(),
        january: get_month("jan"),
        february: get_month("feb"),
        march: get_month("mar"),
        april: get_month("apr"),
        may: get_month("may"),
        june: get_month("jun"),
        july: get_month("jul"),
        august: get_month("aug"),
        september: get_month("sep"),
        october: get_month("oct"),
        november: get_month("nov"),
        december: get_month("dec"),
    }
}

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

    #[test]
    fn ghi_test() {
        average_ghi(&location::BOSTON.coords);
        average_ghi(&location::DENVER.coords);
        average_ghi(&location::LOS_ANGELES.coords);
        average_ghi(&location::MIAMI.coords);
        average_ghi(&location::SEATTLE.coords);
    }
}
<commit_msg>remove nrel.rs<commit_after><|endoftext|>


<commit_before>extern mod extra;

pub struct Pipe<T,U>{
  priv chan: Chan<T>,
  priv port: Port<U>
}
pub struct SingleWire;

enum Message<T> {
    Exit,
    Value(T)
}
trait Wire<T: Send,U: Send> {
  fn connect(&self, f: ~fn(T)->U,port: Port<Message<T>>) -> Port<Message<U>>;
}
trait Sender<T: Send>{
  fn send(&self, t: T);
  fn shutdown(&self);

}
trait Receiver<U: Send> {
  fn recv(&self) -> Option<U>;
  fn recv_wait(&self) -> U;
}
impl <T: Send,U: Send> Pipe<Message<T>,Message<U>>{
    fn drop(&self) {
        self.shutdown(); // doesn't work
    }
}
impl <T: Send,U: Send> Pipe<Message<T>,Message<U>>{
  pub fn new(f: ~fn(Port<T>) -> Port<U>)-> Pipe<T,U> {
    let (in_port, chan): (Port<T>, Chan<T>) = stream();
    let out_port = f(in_port);
    Pipe { chan: chan,
           port: out_port}
  }
}
impl <T: Send,U: Send> Sender<T> for Pipe<Message<T>,Message<U>>{
  fn send(&self, t: T) {
      self.chan.send(Value(t));
  }

  fn shutdown(&self) {
     self.chan.send(Exit);
     loop {
       match self.port.recv() {
         Exit => return,
         _    => ()
       }
     }
  }
}
impl <T: Send,U: Send> Receiver<U> for Pipe<Message<T>,Message<U>> {
  fn recv(&self) -> Option<U>{
    match self.port.peek() {
      true  => Some(self.recv_wait()),
      false => None
    }
  }

  fn recv_wait(&self) -> U {
    match self.port.recv(){
      Exit => fail!(~"Tried to receive on 'Exit'"),
      Value(x) => x
    }
  }
}

impl<T: Send,U: Send> Wire<T,U> for SingleWire  {
      fn connect(&self, f: ~fn(T)->U,port: Port<Message<T>>) -> Port<Message<U>> {
        let (out_port, out_chan): (Port<Message<U>>, Chan<Message<U>>) = stream();
        do ::std::task::spawn_unlinked {
          loop {
            let msg = port.recv();
            match msg {
              Exit => {
                out_chan.send(Exit);
                fail!(~"Exit");
              }
              Value(x) => out_chan.send(Value(f(x)))
            }
          }
        }
        out_port
    }
}

<commit_msg>remove drop<commit_after>extern mod extra;

pub struct Pipe<T,U>{
  priv chan: Chan<T>,
  priv port: Port<U>
}
pub struct SingleWire;

enum Message<T> {
    Exit,
    Value(T)
}
trait Wire<T: Send,U: Send> {
  fn connect(&self, f: ~fn(T)->U,port: Port<Message<T>>) -> Port<Message<U>>;
}
trait Sender<T: Send>{
  fn send(&self, t: T);
  fn shutdown(&self);

}
trait Receiver<U: Send> {
  fn recv(&self) -> Option<U>;
  fn recv_wait(&self) -> U;
}

impl <T: Send,U: Send> Pipe<Message<T>,Message<U>>{
  pub fn new(f: ~fn(Port<T>) -> Port<U>)-> Pipe<T,U> {
    let (in_port, chan): (Port<T>, Chan<T>) = stream();
    let out_port = f(in_port);
    Pipe { chan: chan,
           port: out_port}
  }
}
impl <T: Send,U: Send> Sender<T> for Pipe<Message<T>,Message<U>>{
  fn send(&self, t: T) {
      self.chan.send(Value(t));
  }

  fn shutdown(&self) {
     self.chan.send(Exit);
     loop {
       match self.port.recv() {
         Exit => return,
         _    => ()
       }
     }
  }
}
impl <T: Send,U: Send> Receiver<U> for Pipe<Message<T>,Message<U>> {
  fn recv(&self) -> Option<U>{
    match self.port.peek() {
      true  => Some(self.recv_wait()),
      false => None
    }
  }

  fn recv_wait(&self) -> U {
    match self.port.recv(){
      Exit => fail!(~"Tried to receive on 'Exit'"),
      Value(x) => x
    }
  }
}

impl<T: Send,U: Send> Wire<T,U> for SingleWire  {
      fn connect(&self, f: ~fn(T)->U,port: Port<Message<T>>) -> Port<Message<U>> {
        let (out_port, out_chan): (Port<Message<U>>, Chan<Message<U>>) = stream();
        do ::std::task::spawn_unlinked {
          loop {
            let msg = port.recv();
            match msg {
              Exit => {
                out_chan.send(Exit);
                fail!(~"Exit");
              }
              Value(x) => out_chan.send(Value(f(x)))
            }
          }
        }
        out_port
    }
}

<|endoftext|>


<commit_before>
use ffi;
use super::enums::*;
use super::raw_vec::RawVec;
use super::util::FFTW_MUTEX;

use num_complex::Complex64 as c64;
use std::marker::PhantomData;

pub struct Plan<A, B> {
    plan: ffi::fftw_plan,
    phantom: PhantomData<(A, B)>,
}

impl<A, B> Drop for Plan<A, B> {
    fn drop(&mut self) {
        let lock = FFTW_MUTEX.lock().expect("Cannot get lock");
        unsafe {
            ffi::fftw_destroy_plan(self.plan);
        }
        drop(lock);
    }
}

impl<A, B> Plan<A, B> {
    /// this function modifys the array referred in plan creation
    pub unsafe fn execute(&self) { ffi::fftw_execute(self.plan); }

    pub fn r2c_1d(n: usize, in_: &mut RawVec<f64>, out: &mut RawVec<c64>, flag: FLAG) -> Self {
        let lock = FFTW_MUTEX.lock().expect("Cannot get lock");
        let plan = unsafe { ffi::fftw_plan_dft_r2c_1d(n as i32, in_.as_mut_ptr(), out.as_mut_ptr(), flag as u32) };
        drop(lock);
        Plan {
            plan: plan,
            phantom: PhantomData,
        }
    }

    pub fn c2r_1d(n: usize, in_: &mut RawVec<c64>, out: &mut RawVec<f64>, flag: FLAG) -> Self {
        let lock = FFTW_MUTEX.lock().expect("Cannot get lock");
        let plan = unsafe { ffi::fftw_plan_dft_c2r_1d(n as i32, in_.as_mut_ptr(), out.as_mut_ptr(), flag as u32) };
        drop(lock);
        Plan {
            plan: plan,
            phantom: PhantomData,
        }
    }

    pub fn c2c_1d(n: usize, in_: &mut RawVec<c64>, out: &mut RawVec<c64>, sign: SIGN, flag: FLAG) -> Self {
        let lock = FFTW_MUTEX.lock().expect("Cannot get lock");
        let plan = unsafe {
            ffi::fftw_plan_dft_1d(n as i32,
                                  in_.as_mut_ptr(),
                                  out.as_mut_ptr(),
                                  sign as i32,
                                  flag as u32)
        };
        drop(lock);
        Plan {
            plan: plan,
            phantom: PhantomData,
        }
    }
}

impl Plan<f64, f64> {
    pub fn r2r_1d(n: usize, in_: &mut RawVec<f64>, out: &mut RawVec<f64>, kind: R2R_KIND, flag: FLAG) -> Self {
        let lock = FFTW_MUTEX.lock().expect("Cannot get lock");
        let plan = unsafe {
            ffi::fftw_plan_r2r_1d(n as i32,
                                  in_.as_mut_ptr(),
                                  out.as_mut_ptr(),
                                  kind,
                                  flag as u32)
        };
        drop(lock);
        Plan {
            plan: plan,
            phantom: PhantomData,
        }
    }
}

impl Plan<f32, f32> {
    pub fn r2r_1d(n: usize, in_: &mut RawVec<f32>, out: &mut RawVec<f32>, kind: R2R_KIND, flag: FLAG) -> Self {
        let lock = FFTW_MUTEX.lock().expect("Cannot get lock");
        let plan = unsafe {
            ffi::fftwf_plan_r2r_1d(n as i32,
                                   in_.as_mut_ptr(),
                                   out.as_mut_ptr(),
                                   kind,
                                   flag as u32)
        };
        drop(lock);
        Plan {
            plan: plan,
            phantom: PhantomData,
        }
    }
}
<commit_msg>Introduce trais for plan creation<commit_after>
use ffi;
use super::enums::*;
use super::raw_vec::RawVec;
use super::util::FFTW_MUTEX;

use num_complex::Complex64 as c64;
use num_complex::Complex32 as c32;
use std::marker::PhantomData;

pub struct Plan<A, B> {
    plan: ffi::fftw_plan,
    phantom: PhantomData<(A, B)>,
}

impl<A, B> Plan<A, B> {
    /// this function modifys the array referred in plan creation
    pub unsafe fn execute(&self) { ffi::fftw_execute(self.plan); }
}

impl<A, B> Drop for Plan<A, B> {
    fn drop(&mut self) {
        let lock = FFTW_MUTEX.lock().expect("Cannot get lock");
        unsafe {
            ffi::fftw_destroy_plan(self.plan);
        }
        drop(lock);
    }
}

impl<T: R2RPlanCreate> Plan<T, T> {
    pub fn r2r_1d(n: usize, mut in_: &mut RawVec<T>, mut out: &mut RawVec<T>, kind: R2R_KIND, flag: FLAG) -> Self {
        let lock = FFTW_MUTEX.lock().expect("Cannot get lock");
        let plan = unsafe { T::r2r_1d(n, &mut in_, &mut out, kind, flag) };
        drop(lock);
        Plan {
            plan: plan,
            phantom: PhantomData,
        }
    }
}

impl<T: C2CPlanCreate> Plan<T, T> {
    pub fn c2c_1d(n: usize, mut in_: &mut RawVec<T>, mut out: &mut RawVec<T>, sign: SIGN, flag: FLAG) -> Self {
        let lock = FFTW_MUTEX.lock().expect("Cannot get lock");
        let plan = unsafe { T::c2c_1d(n, &mut in_, &mut out, sign, flag) };
        drop(lock);
        Plan {
            plan: plan,
            phantom: PhantomData,
        }
    }
}

impl<C, R> Plan<C, R>
    where (C, R): C2RPlanCreate<Real = R, Complex = C>
{
    pub fn c2r_1d(n: usize, in_: &mut RawVec<C>, out: &mut RawVec<R>, flag: FLAG) -> Self {
        let lock = FFTW_MUTEX.lock().expect("Cannot get lock");
        let plan = unsafe { <(C, R)>::c2r_1d(n, in_, out, flag) };
        drop(lock);
        Plan {
            plan: plan,
            phantom: PhantomData,
        }
    }
}

impl<R, C> Plan<R, C>
    where (C, R): C2RPlanCreate<Real = R, Complex = C>
{
    pub fn r2c_1d(n: usize, in_: &mut RawVec<R>, out: &mut RawVec<C>, flag: FLAG) -> Self {
        let lock = FFTW_MUTEX.lock().expect("Cannot get lock");
        let plan = unsafe { <(C, R)>::r2c_1d(n, in_, out, flag) };
        drop(lock);
        Plan {
            plan: plan,
            phantom: PhantomData,
        }
    }
}

pub trait R2RPlanCreate: Sized {
    unsafe fn r2r_1d(n: usize, in_: &mut RawVec<Self>, out: &mut RawVec<Self>, R2R_KIND, FLAG) -> ffi::fftw_plan;
}

impl R2RPlanCreate for f64 {
    unsafe fn r2r_1d(n: usize,
                     in_: &mut RawVec<Self>,
                     out: &mut RawVec<Self>,
                     kind: R2R_KIND,
                     flag: FLAG)
                     -> ffi::fftw_plan {
        ffi::fftw_plan_r2r_1d(n as i32,
                              in_.as_mut_ptr(),
                              out.as_mut_ptr(),
                              kind,
                              flag as u32)
    }
}

impl R2RPlanCreate for f32 {
    unsafe fn r2r_1d(n: usize,
                     in_: &mut RawVec<Self>,
                     out: &mut RawVec<Self>,
                     kind: R2R_KIND,
                     flag: FLAG)
                     -> ffi::fftw_plan {
        ffi::fftwf_plan_r2r_1d(n as i32,
                               in_.as_mut_ptr(),
                               out.as_mut_ptr(),
                               kind,
                               flag as u32)
    }
}

pub trait C2CPlanCreate: Sized {
    unsafe fn c2c_1d(n: usize, in_: &mut RawVec<Self>, out: &mut RawVec<Self>, SIGN, FLAG) -> ffi::fftw_plan;
}

impl C2CPlanCreate for c64 {
    unsafe fn c2c_1d(n: usize, i: &mut RawVec<Self>, o: &mut RawVec<Self>, s: SIGN, f: FLAG) -> ffi::fftw_plan {
        ffi::fftw_plan_dft_1d(n as i32, i.as_mut_ptr(), o.as_mut_ptr(), s as i32, f as u32)
    }
}

impl C2CPlanCreate for c32 {
    unsafe fn c2c_1d(n: usize, i: &mut RawVec<Self>, o: &mut RawVec<Self>, s: SIGN, f: FLAG) -> ffi::fftw_plan {
        ffi::fftwf_plan_dft_1d(n as i32, i.as_mut_ptr(), o.as_mut_ptr(), s as i32, f as u32)
    }
}

pub trait C2RPlanCreate {
    type Real: Sized;
    type Complex: Sized;
    unsafe fn r2c_1d(n: usize, in_: &mut RawVec<Self::Real>, out: &mut RawVec<Self::Complex>, FLAG) -> ffi::fftw_plan;
    unsafe fn c2r_1d(n: usize, in_: &mut RawVec<Self::Complex>, out: &mut RawVec<Self::Real>, FLAG) -> ffi::fftw_plan;
}

impl C2RPlanCreate for (c64, f64) {
    type Real = f64;
    type Complex = c64;
    unsafe fn r2c_1d(n: usize, i: &mut RawVec<Self::Real>, o: &mut RawVec<Self::Complex>, f: FLAG) -> ffi::fftw_plan {
        ffi::fftw_plan_dft_r2c_1d(n as i32, i.as_mut_ptr(), o.as_mut_ptr(), f as u32)
    }
    unsafe fn c2r_1d(n: usize, i: &mut RawVec<Self::Complex>, o: &mut RawVec<Self::Real>, f: FLAG) -> ffi::fftw_plan {
        ffi::fftw_plan_dft_c2r_1d(n as i32, i.as_mut_ptr(), o.as_mut_ptr(), f as u32)
    }
}

impl C2RPlanCreate for (c32, f32) {
    type Real = f32;
    type Complex = c32;
    unsafe fn r2c_1d(n: usize, i: &mut RawVec<Self::Real>, o: &mut RawVec<Self::Complex>, f: FLAG) -> ffi::fftw_plan {
        ffi::fftwf_plan_dft_r2c_1d(n as i32, i.as_mut_ptr(), o.as_mut_ptr(), f as u32)
    }
    unsafe fn c2r_1d(n: usize, i: &mut RawVec<Self::Complex>, o: &mut RawVec<Self::Real>, f: FLAG) -> ffi::fftw_plan {
        ffi::fftwf_plan_dft_c2r_1d(n as i32, i.as_mut_ptr(), o.as_mut_ptr(), f as u32)
    }
}
<|endoftext|>


<commit_before>use std::iter::{FromIterator, Extend};

use gset::GSet;

/// Implements a state based two-phase set
/// using two `GSet`s
#[derive(Clone, Hash, Debug)]
pub struct PSet<T> {
    add_set:    GSet<T>,
    remove_set: GSet<T>,
}

impl<T: Ord + Clone> PSet<T> {
    /// Creates a new `PSet`
    pub fn new() -> PSet<T> {
        PSet {
            add_set:    GSet::new(),
            remove_set: GSet::new()
        }
    }

    /// Inserts an element of type `T` into the
    /// given `PSet`. Returns `true` if the element
    /// was already in the set. Otherwise, it inserts
    /// the element and returns `false`
    pub fn insert(&mut self, value: T) -> bool {
        self.add_set.insert(value)
    }

    /// Removes the given element from the `PSet`
    pub fn remove(&mut self, value: T) -> bool {
        if self.add_set.contains(&value) {
            self.remove_set.insert(value);
        }
        return false
    }

    /// Checks if the given value of type `T` is in
    /// the set. If the check is successful, returns
    /// `true`. Otherwise returns `false`
    pub fn contains(&mut self, value: &T) -> bool {
        self.add_set.contains(&value) & !self.remove_set.contains(&value)
    }

    /// Returns the contents of the given set. This is
    /// equivalent to the set difference between the add set and the
    /// remove set
    pub fn contents(&mut self) -> Vec<T> {
        self.add_set.difference(&self.remove_set).into_iter().collect()
    }

    /// Checks if the given `PSet` is empty
    pub fn is_empty(&mut self) -> bool {
        self.contents().is_empty()
    }

    /// Returns the length of the given `PSet`
    pub fn len(&mut self) -> usize {
        self.contents().len()
    }

    /// Returns the set union between the given `PSet` and
    /// another `PSet` as a `PSet`
    pub fn union(&mut self, other: &PSet<T>) -> PSet<T> {
        PSet {
            add_set:    self.add_set.union(&other.add_set),
            remove_set: self.remove_set.union(&other.remove_set)
        }
    }

    /// Returns the set intersection between the given `PSet` and
    /// another `PSet` as a `PSet`
    pub fn intersection(&mut self, other: &PSet<T>) -> PSet<T> {
        PSet {
            add_set:    self.add_set.intersection(&other.add_set),
            remove_set: self.remove_set.union(&other.remove_set)
        }
    }
}

impl<T: Ord + Clone> FromIterator<T> for PSet<T> {
    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> PSet<T> {
        let mut set = PSet::new();
        set.extend(iter);
        set
    }
}

impl<T: Ord + Clone> Extend<T> for PSet<T> {
    #[inline]
    fn extend<Iter: IntoIterator<Item = T>>(&mut self, iter: Iter) {
        for elem in iter {
            self.insert(elem);
        }
    }
}
<commit_msg>Add an implementation for PSet difference<commit_after>use std::iter::{FromIterator, Extend};

use gset::GSet;

/// Implements a state based two-phase set
/// using two `GSet`s
#[derive(Clone, Hash, Debug)]
pub struct PSet<T> {
    add_set:    GSet<T>,
    remove_set: GSet<T>,
}

impl<T: Ord + Clone> PSet<T> {
    /// Creates a new `PSet`
    pub fn new() -> PSet<T> {
        PSet {
            add_set:    GSet::new(),
            remove_set: GSet::new()
        }
    }

    /// Inserts an element of type `T` into the
    /// given `PSet`. Returns `true` if the element
    /// was already in the set. Otherwise, it inserts
    /// the element and returns `false`
    pub fn insert(&mut self, value: T) -> bool {
        self.add_set.insert(value)
    }

    /// Removes the given element from the `PSet`
    pub fn remove(&mut self, value: T) -> bool {
        if self.add_set.contains(&value) {
            self.remove_set.insert(value);
        }
        return false
    }

    /// Checks if the given value of type `T` is in
    /// the set. If the check is successful, returns
    /// `true`. Otherwise returns `false`
    pub fn contains(&mut self, value: &T) -> bool {
        self.add_set.contains(&value) & !self.remove_set.contains(&value)
    }

    /// Returns the contents of the given set. This is
    /// equivalent to the set difference between the add set and the
    /// remove set
    pub fn contents(&mut self) -> Vec<T> {
        self.add_set.difference(&self.remove_set).into_iter().collect()
    }

    /// Checks if the given `PSet` is empty
    pub fn is_empty(&mut self) -> bool {
        self.contents().is_empty()
    }

    /// Returns the length of the given `PSet`
    pub fn len(&mut self) -> usize {
        self.contents().len()
    }

    /// Returns the set union between the given `PSet` and
    /// another `PSet` as a `PSet`
    pub fn union(&mut self, other: &PSet<T>) -> PSet<T> {
        PSet {
            add_set:    self.add_set.union(&other.add_set),
            remove_set: self.remove_set.union(&other.remove_set)
        }
    }

    /// Returns the set intersection between the given `PSet` and
    /// another `PSet` as a `PSet`
    pub fn intersection(&mut self, other: &PSet<T>) -> PSet<T> {
        PSet {
            add_set:    self.add_set.intersection(&other.add_set),
            remove_set: self.remove_set.union(&other.remove_set)
        }
    }

    /// Returns the set difference between the given `PSet` and
    /// another `PSet` as a `PSet`
    pub fn difference(&mut self, other: &PSet<T>) -> PSet<T> {
        add_set:    self.add_set.difference(&other.add_set),
        remove_set: GSet::new()
    }
}

impl<T: Ord + Clone> FromIterator<T> for PSet<T> {
    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> PSet<T> {
        let mut set = PSet::new();
        set.extend(iter);
        set
    }
}

impl<T: Ord + Clone> Extend<T> for PSet<T> {
    #[inline]
    fn extend<Iter: IntoIterator<Item = T>>(&mut self, iter: Iter) {
        for elem in iter {
            self.insert(elem);
        }
    }
}
<|endoftext|>


<commit_before>// Hound -- A WAV encoding and decoding library in Rust
// Copyright (C) 2015 Ruud van Asseldonk
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License, version 3,
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

use std::io;
use std::marker;
use super::{Sample, WavSpec};

// TODO: Can this be unified among Hound and Claxon? Copy + Paste is bad, but
// I refuse to use an external crate just to read into an array of bytes, or
// to read an integer. Such functionality should really be in the standard
// library. Practically _every_ program that does IO will need more high-level
// functionality than what the standard library currently provides.
/// Extends the functionality of `io::Read` with additional methods.
///
/// The methods may be used on any type that implements `io::Read`.
pub trait ReadExt: io::Read {
    /// Reads as many bytes as `buf` is long.
    ///
    /// This may issue multiple `read` calls internally. An error is returned
    /// if `read` read 0 bytes before the buffer is full.
    fn read_into(&mut self, buf: &mut [u8]) -> io::Result<()>;

    /// Reads `n` bytes and returns them in a vector.
    fn read_bytes(&mut self, n: usize) -> io::Result<Vec<u8>>;

    /// Reads two bytes and interprets them as a little-endian 16-bit signed integer.
    fn read_le_i16(&mut self) -> io::Result<i16>;

    /// Reads two bytes and interprets them as a little-endian 16-bit unsigned integer.
    fn read_le_u16(&mut self) -> io::Result<u16>;

    /// Reads four bytes and interprets them as a little-endian 32-bit unsigned integer.
    fn read_le_u32(&mut self) -> io::Result<u32>;
}

impl<R> ReadExt for R where R: io::Read {
    fn read_into(&mut self, buf: &mut [u8]) -> io::Result<()> {
        let mut n = 0;
        while n < buf.len() {
            let progress = try!(self.read(&mut buf[n ..]));
            if progress > 0 {
                n += progress;
            } else {
                return Err(io::Error::new(io::ErrorKind::Other,
                                          "Failed to read enough bytes."));
            }
        }
        Ok(())
    }

    fn read_bytes(&mut self, n: usize) -> io::Result<Vec<u8>> {
        let mut buf = Vec::with_capacity(n);
        // TODO: is there a safe alternative that is not crazy like draining
        // a repeat(0u8) iterator?
        unsafe { buf.set_len(n); }
        try!(self.read_into(&mut buf[..]));
        Ok(buf)
    }

    fn read_le_i16(&mut self) -> io::Result<i16> {
        self.read_le_u16().map(|x| x as i16)
    }

    fn read_le_u16(&mut self) -> io::Result<u16> {
        let mut buf = [0u8; 2];
        try!(self.read_into(&mut buf));
        Ok((buf[1] as u16) << 8 | (buf[0] as u16))
    }

    fn read_le_u32(&mut self) -> io::Result<u32> {
        let mut buf = [0u8; 4];
        try!(self.read_into(&mut buf));
        Ok((buf[3] as u32) << 24 | (buf[2] as u32) << 16 |
           (buf[1] as u32) << 8  | (buf[0] as u32) << 0)
    }
}

/// A reader that reads the WAVE format from the underlying reader.
pub struct WavReader<R> {
    /// Specification of the file as found in the fmt chunk.
    spec: WavSpec,

    /// The number of samples in the data chunk.
    ///
    /// The data chunk is limited to a 4 GiB length because its header has a
    /// 32-bit length field. A sample takes at least one byte to store, so the
    /// number of samples is always less than 2^32.
    num_samples: u32,

    /// The number of samples read so far.
    samples_read: u32,

    /// The reader from which the WAVE format is read.
    reader: R
}

/// An iterator that yields samples of type `S` read from a `WavReader`.
pub struct WavSamples<'wr, R, S> where R: 'wr {
    reader: &'wr mut WavReader<R>,
    phantom_sample: marker::PhantomData<S>
}

impl<R> WavReader<R> where R: io::Read {

    /// Reads the RIFF WAVE header, returns the supposed file size.
    fn read_wave_header(reader: &mut R) -> io::Result<u32> {
        // Every WAVE file starts with the four bytes 'RIFF' and a file length.
        // TODO: the old approach of having a slice on the stack and reading
        // into it is more cumbersome, but also avoids a heap allocation. Is
        // the compiler smart enough to avoid the heap allocation anyway? I
        // would not expect it to be.
        if "RIFF".as_bytes() != &try!(reader.read_bytes(4))[..] {
            // TODO: use custom error type
            return Err(io::Error::new(io::ErrorKind::Other, "No RIFF tag found."));
        }

        // TODO: would this be useful anywhere? Probably not, except for
        // validating files, but do we need to be so strict?
        let file_len = try!(reader.read_le_u32());

        // Next four bytes indicate the file type, which should be WAVE.
        if "WAVE".as_bytes() != &try!(reader.read_bytes(4))[..] {
            // TODO: use custom error type
            return Err(io::Error::new(io::ErrorKind::Other, "No WAVE tag found."));
        }

        Ok(file_len)
    }

    // TODO: define a custom error type to report ill-formed files.
    /// Reads the fmt chunk of the file, returns the information it provides.
    fn read_fmt_chunk(reader: &mut R) -> io::Result<WavSpec> {
        // Then a "fmt " chunk should follow.
        // TODO: is the "fmt " block always the first block? Should this be
        // flexible? It should anyway, so this hardly matters. For now, we
        // expect only an "fmt " block first, and then a "data" block.
        if "fmt ".as_bytes() != &try!(reader.read_bytes(4))[..] {
            // TODO: use custom error type
            return Err(io::Error::new(io::ErrorKind::Other, "No fmt block found."));
        }
        let fmt_chunk_len = try!(reader.read_le_u32());

        // A minimum chunk length of at least 16 is assumed. Note: actually,
        // the first 14 bytes contain enough information to0 fully specify the
        // file. I have not encountered a file with a 14-byte fmt section
        // though. If you ever encounter such file, please contact me.
        if fmt_chunk_len < 16 {
            // TODO: use custom error type
            return Err(io::Error::new(io::ErrorKind::Other, "Invalid fmt chunck size."));
        }

        // Read the WAVEFORMAT struct, as defined at
        // https://msdn.microsoft.com/en-us/library/ms713498.aspx.
        // ```
        // typedef struct {
        //     WORD  wFormatTag;
        //     WORD  nChannels;
        //     DWORD nSamplesPerSec;
        //     DWORD nAvgBytesPerSec;
        //     WORD  nBlockAlign;
        // } WAVEFORMAT;
        // ```
        // The WAVEFORMATEX struct has two more members, as defined at
        // https://msdn.microsoft.com/en-us/library/ms713497.aspx
        // ```
        // typedef struct {
        //     WORD  wFormatTag;
        //     WORD  nChannels;
        //     DWORD nSamplesPerSec;
        //     DWORD nAvgBytesPerSec;
        //     WORD  nBlockAlign;
        //     WORD  wBitsPerSample;
        //     WORD  cbSize;
        // } WAVEFORMATEX;
        // ```
        // It appears that in either case, the minimal length of the fmt
        // section is 16 bytes, meaning that it does include the
        // `wBitsPerSample` field. (The name is misleading though, because it
        // is the number of bits used to store a sample, not all of the bits
        // need to be valid for all versions of the WAVE format.)
        let format_tag = try!(reader.read_le_u16());
        let n_channels = try!(reader.read_le_u16());
        let n_samples_per_sec = try!(reader.read_le_u32());
        let n_bytes_per_sec = try!(reader.read_le_u32());
        let block_align = try!(reader.read_le_u16());
        let bits_per_sample = try!(reader.read_le_u16());

        // Two of the stored fields are redundant, and may be ignored. We do
        // validate them to fail early for ill-formed files.
        if (bits_per_sample != block_align / n_channels * 8)
        || (n_bytes_per_sec != block_align as u32 * n_samples_per_sec) {
            // TODO: use custom error type
            return Err(io::Error::new(io::ErrorKind::Other, "Incosistent fmt chunk."));
        }

        if format_tag != 1 {
            // TODO: detect the actual tag, and switch to reading WAVEFORMATEX
            // or WAVEFORMATEXTENSIBLE if indicated by the tag.
            // TODO: use a custom error type, report proper error.
            return Err(io::Error::new(io::ErrorKind::Other, "Invalid or unsupported format tag."));
        }

        // We have read 16 bytes so far. If the fmt chunk is longer, then we
        // could be dealing with WAVEFORMATEX or WAVEFORMATEXTENSIBLE. This is
        // not supported at this point.
        if fmt_chunk_len > 16 {
            panic!("wave format type not implemented yet");
        }

        let spec = WavSpec {
            channels: n_channels,
            sample_rate: n_samples_per_sec,
            bits_per_sample: bits_per_sample as u32
        };

        Ok(spec)
    }

    /// Reads the header of the data chunk and returns its length.
    fn read_data_chunk(reader: &mut R) -> io::Result<u32> {
        if "data".as_bytes() != &try!(reader.read_bytes(4))[..] {
            // TODO: use custom error type
            return Err(io::Error::new(io::ErrorKind::Other, "No data block found."));
        }
        let data_chunk_len = try!(reader.read_le_u32());
        Ok(data_chunk_len)
    }

    // TODO: define a custom error type to report ill-formed files.
    /// Attempts to create a reader that reads the WAVE format.
    ///
    /// The header is read immediately. Reading the data will be done on
    /// demand.
    pub fn new(mut reader: R) -> io::Result<WavReader<R>> {
        try!(WavReader::read_wave_header(&mut reader));

        // TODO: read chunk header first, then match on the type, and read the
        // chunk, skip it if unknown, or wait if it is the data chunk.
        let spec = try!(WavReader::read_fmt_chunk(&mut reader));
        let data_len = try!(WavReader::read_data_chunk(&mut reader));

        let num_samples = data_len / (spec.bits_per_sample / 8);
        let wav_reader = WavReader {
            spec: spec,
            num_samples: num_samples,
            samples_read: 0,
            reader: reader
        };

        Ok(wav_reader)
    }

    // TODO: Should this return by value instead? A reference is more consistent
    // with Claxon, but the type is only 80 bytes, barely larger than a pointer.
    // Is it worth the extra indirection? On the other hand, the indirection
    // is probably optimised away.
    /// Returns information about the WAVE file.
    pub fn spec(&self) -> &WavSpec {
        &self.spec
    }

    /// Returns an iterator over all samples.
    // TODO: how do we handle channels? Add an iterator that yields &[S]?
    // This is not even possible nicely if the number of channels is not known
    // in advance. It is not such a problem to have interleaved audio though,
    // if the user knows. Methods could be provided for stereo iterators, in
    // case the number of channels is known in advance?
    pub fn samples<'wr, S: Sample>(&'wr mut self) -> WavSamples<'wr, R, S> {
        WavSamples {
            reader: self,
            phantom_sample: marker::PhantomData
        }
    }
}

impl<'wr, R, S> Iterator for WavSamples<'wr, R, S>
where R: io::Read,
      S: Sample {
    type Item = io::Result<S>;

    fn next(&mut self) -> Option<io::Result<S>> {
        let reader = &mut self.reader;
        if reader.samples_read < reader.num_samples {
            reader.samples_read += 1;
            Some(Sample::read(&mut reader.reader, reader.spec.bits_per_sample))
        } else {
            None
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let samples_left = self.reader.num_samples - self.reader.samples_read;
        (samples_left as usize, Some(samples_left as usize))
    }
}

/// Tests reading the most basic wav file, one with only a WAVEFORMAT struct.
#[test]
fn read_wav_waveformat() {
    use std::fs;

    let file = fs::File::open("testsamples/waveformat-16bit-44100Hz-mono.wav")
                        .ok().expect("failed to open file");
    let buf_reader = io::BufReader::new(file);
    let mut wav_reader = WavReader::new(buf_reader)
                                   .ok().expect("failed to read header");

    assert_eq!(wav_reader.spec().channels, 1);
    assert_eq!(wav_reader.spec().sample_rate, 44100);
    assert_eq!(wav_reader.spec().bits_per_sample, 16);

    let samples: Vec<i16> = wav_reader.samples()
                                      .map(|r| r.ok().unwrap())
                                      .collect();

    // The test file has been prepared with these exact four samples.
    assert_eq!(&samples[..], &[2, -3, 5, -7]);
}

#[test]
fn read_wav_waveformat_ex() {
    // TODO: add a test sample that uses WAVEFORMATEX and verify that it can be
    // read properly.
}

#[test]
fn read_wav_waveformat_extensible() {
    // TODO: add a test sample that uses WAVEFORMATEXTENSIBLE (as produced by
    // Hound itself actually, so this should not be too hard), and verify that
    // it can be read properly.
}
<commit_msg>improve documentation<commit_after>// Hound -- A WAV encoding and decoding library in Rust
// Copyright (C) 2015 Ruud van Asseldonk
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License, version 3,
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

use std::io;
use std::marker;
use super::{Sample, WavSpec};

// TODO: Can this be unified among Hound and Claxon? Copy + Paste is bad, but
// I refuse to use an external crate just to read into an array of bytes, or
// to read an integer. Such functionality should really be in the standard
// library. Practically _every_ program that does IO will need more high-level
// functionality than what the standard library currently provides.
/// Extends the functionality of `io::Read` with additional methods.
///
/// The methods may be used on any type that implements `io::Read`.
pub trait ReadExt: io::Read {
    /// Reads as many bytes as `buf` is long.
    ///
    /// This may issue multiple `read` calls internally. An error is returned
    /// if `read` read 0 bytes before the buffer is full.
    fn read_into(&mut self, buf: &mut [u8]) -> io::Result<()>;

    /// Reads `n` bytes and returns them in a vector.
    fn read_bytes(&mut self, n: usize) -> io::Result<Vec<u8>>;

    /// Reads two bytes and interprets them as a little-endian 16-bit signed integer.
    fn read_le_i16(&mut self) -> io::Result<i16>;

    /// Reads two bytes and interprets them as a little-endian 16-bit unsigned integer.
    fn read_le_u16(&mut self) -> io::Result<u16>;

    /// Reads four bytes and interprets them as a little-endian 32-bit unsigned integer.
    fn read_le_u32(&mut self) -> io::Result<u32>;
}

impl<R> ReadExt for R where R: io::Read {
    fn read_into(&mut self, buf: &mut [u8]) -> io::Result<()> {
        let mut n = 0;
        while n < buf.len() {
            let progress = try!(self.read(&mut buf[n ..]));
            if progress > 0 {
                n += progress;
            } else {
                return Err(io::Error::new(io::ErrorKind::Other,
                                          "Failed to read enough bytes."));
            }
        }
        Ok(())
    }

    fn read_bytes(&mut self, n: usize) -> io::Result<Vec<u8>> {
        let mut buf = Vec::with_capacity(n);
        // TODO: is there a safe alternative that is not crazy like draining
        // a repeat(0u8) iterator?
        unsafe { buf.set_len(n); }
        try!(self.read_into(&mut buf[..]));
        Ok(buf)
    }

    fn read_le_i16(&mut self) -> io::Result<i16> {
        self.read_le_u16().map(|x| x as i16)
    }

    fn read_le_u16(&mut self) -> io::Result<u16> {
        let mut buf = [0u8; 2];
        try!(self.read_into(&mut buf));
        Ok((buf[1] as u16) << 8 | (buf[0] as u16))
    }

    fn read_le_u32(&mut self) -> io::Result<u32> {
        let mut buf = [0u8; 4];
        try!(self.read_into(&mut buf));
        Ok((buf[3] as u32) << 24 | (buf[2] as u32) << 16 |
           (buf[1] as u32) << 8  | (buf[0] as u32) << 0)
    }
}

/// A reader that reads the WAVE format from the underlying reader.
///
/// A `WavReader` is a streaming reader. It reads data from the underlying
/// reader on demand, and it reads no more than strictly necessary. No internal
/// buffering is performed on the underlying reader.
pub struct WavReader<R> {
    /// Specification of the file as found in the fmt chunk.
    spec: WavSpec,

    /// The number of samples in the data chunk.
    ///
    /// The data chunk is limited to a 4 GiB length because its header has a
    /// 32-bit length field. A sample takes at least one byte to store, so the
    /// number of samples is always less than 2^32.
    num_samples: u32,

    /// The number of samples read so far.
    samples_read: u32,

    /// The reader from which the WAVE format is read.
    reader: R
}

/// An iterator that yields samples of type `S` read from a `WavReader`.
pub struct WavSamples<'wr, R, S> where R: 'wr {
    reader: &'wr mut WavReader<R>,
    phantom_sample: marker::PhantomData<S>
}

impl<R> WavReader<R> where R: io::Read {

    /// Reads the RIFF WAVE header, returns the supposed file size.
    fn read_wave_header(reader: &mut R) -> io::Result<u32> {
        // Every WAVE file starts with the four bytes 'RIFF' and a file length.
        // TODO: the old approach of having a slice on the stack and reading
        // into it is more cumbersome, but also avoids a heap allocation. Is
        // the compiler smart enough to avoid the heap allocation anyway? I
        // would not expect it to be.
        if "RIFF".as_bytes() != &try!(reader.read_bytes(4))[..] {
            // TODO: use custom error type
            return Err(io::Error::new(io::ErrorKind::Other, "No RIFF tag found."));
        }

        // TODO: would this be useful anywhere? Probably not, except for
        // validating files, but do we need to be so strict?
        let file_len = try!(reader.read_le_u32());

        // Next four bytes indicate the file type, which should be WAVE.
        if "WAVE".as_bytes() != &try!(reader.read_bytes(4))[..] {
            // TODO: use custom error type
            return Err(io::Error::new(io::ErrorKind::Other, "No WAVE tag found."));
        }

        Ok(file_len)
    }

    // TODO: define a custom error type to report ill-formed files.
    /// Reads the fmt chunk of the file, returns the information it provides.
    fn read_fmt_chunk(reader: &mut R) -> io::Result<WavSpec> {
        // Then a "fmt " chunk should follow.
        // TODO: is the "fmt " block always the first block? Should this be
        // flexible? It should anyway, so this hardly matters. For now, we
        // expect only an "fmt " block first, and then a "data" block.
        if "fmt ".as_bytes() != &try!(reader.read_bytes(4))[..] {
            // TODO: use custom error type
            return Err(io::Error::new(io::ErrorKind::Other, "No fmt block found."));
        }
        let fmt_chunk_len = try!(reader.read_le_u32());

        // A minimum chunk length of at least 16 is assumed. Note: actually,
        // the first 14 bytes contain enough information to0 fully specify the
        // file. I have not encountered a file with a 14-byte fmt section
        // though. If you ever encounter such file, please contact me.
        if fmt_chunk_len < 16 {
            // TODO: use custom error type
            return Err(io::Error::new(io::ErrorKind::Other, "Invalid fmt chunck size."));
        }

        // Read the WAVEFORMAT struct, as defined at
        // https://msdn.microsoft.com/en-us/library/ms713498.aspx.
        // ```
        // typedef struct {
        //     WORD  wFormatTag;
        //     WORD  nChannels;
        //     DWORD nSamplesPerSec;
        //     DWORD nAvgBytesPerSec;
        //     WORD  nBlockAlign;
        // } WAVEFORMAT;
        // ```
        // The WAVEFORMATEX struct has two more members, as defined at
        // https://msdn.microsoft.com/en-us/library/ms713497.aspx
        // ```
        // typedef struct {
        //     WORD  wFormatTag;
        //     WORD  nChannels;
        //     DWORD nSamplesPerSec;
        //     DWORD nAvgBytesPerSec;
        //     WORD  nBlockAlign;
        //     WORD  wBitsPerSample;
        //     WORD  cbSize;
        // } WAVEFORMATEX;
        // ```
        // It appears that in either case, the minimal length of the fmt
        // section is 16 bytes, meaning that it does include the
        // `wBitsPerSample` field. (The name is misleading though, because it
        // is the number of bits used to store a sample, not all of the bits
        // need to be valid for all versions of the WAVE format.)
        let format_tag = try!(reader.read_le_u16());
        let n_channels = try!(reader.read_le_u16());
        let n_samples_per_sec = try!(reader.read_le_u32());
        let n_bytes_per_sec = try!(reader.read_le_u32());
        let block_align = try!(reader.read_le_u16());
        let bits_per_sample = try!(reader.read_le_u16());

        // Two of the stored fields are redundant, and may be ignored. We do
        // validate them to fail early for ill-formed files.
        if (bits_per_sample != block_align / n_channels * 8)
        || (n_bytes_per_sec != block_align as u32 * n_samples_per_sec) {
            // TODO: use custom error type
            return Err(io::Error::new(io::ErrorKind::Other, "Incosistent fmt chunk."));
        }

        if format_tag != 1 {
            // TODO: detect the actual tag, and switch to reading WAVEFORMATEX
            // or WAVEFORMATEXTENSIBLE if indicated by the tag.
            // TODO: use a custom error type, report proper error.
            return Err(io::Error::new(io::ErrorKind::Other, "Invalid or unsupported format tag."));
        }

        // We have read 16 bytes so far. If the fmt chunk is longer, then we
        // could be dealing with WAVEFORMATEX or WAVEFORMATEXTENSIBLE. This is
        // not supported at this point.
        if fmt_chunk_len > 16 {
            panic!("wave format type not implemented yet");
        }

        let spec = WavSpec {
            channels: n_channels,
            sample_rate: n_samples_per_sec,
            bits_per_sample: bits_per_sample as u32
        };

        Ok(spec)
    }

    /// Reads the header of the data chunk and returns its length.
    fn read_data_chunk(reader: &mut R) -> io::Result<u32> {
        if "data".as_bytes() != &try!(reader.read_bytes(4))[..] {
            // TODO: use custom error type
            return Err(io::Error::new(io::ErrorKind::Other, "No data block found."));
        }
        let data_chunk_len = try!(reader.read_le_u32());
        Ok(data_chunk_len)
    }

    // TODO: define a custom error type to report ill-formed files.
    /// Attempts to create a reader that reads the WAVE format.
    ///
    /// The header is read immediately. Reading the data will be done on
    /// demand.
    pub fn new(mut reader: R) -> io::Result<WavReader<R>> {
        try!(WavReader::read_wave_header(&mut reader));

        // TODO: read chunk header first, then match on the type, and read the
        // chunk, skip it if unknown, or wait if it is the data chunk.
        let spec = try!(WavReader::read_fmt_chunk(&mut reader));
        let data_len = try!(WavReader::read_data_chunk(&mut reader));

        let num_samples = data_len / (spec.bits_per_sample / 8);
        let wav_reader = WavReader {
            spec: spec,
            num_samples: num_samples,
            samples_read: 0,
            reader: reader
        };

        Ok(wav_reader)
    }

    // TODO: Should this return by value instead? A reference is more consistent
    // with Claxon, but the type is only 80 bits, barely larger than a pointer.
    // Is it worth the extra indirection? On the other hand, the indirection
    // is probably optimised away.
    /// Returns information about the WAVE file.
    pub fn spec(&self) -> &WavSpec {
        &self.spec
    }

    /// Returns an iterator over all samples.
    ///
    /// The channel data is is interleaved. The iterator is streaming. That is,
    /// if you call this method once, read a few samples, and call this method
    /// again, the second iterator will not start again from the beginning of
    /// the file, it will continue where the first iterator stopped.
    pub fn samples<'wr, S: Sample>(&'wr mut self) -> WavSamples<'wr, R, S> {
        WavSamples {
            reader: self,
            phantom_sample: marker::PhantomData
        }
    }
}

impl<'wr, R, S> Iterator for WavSamples<'wr, R, S>
where R: io::Read,
      S: Sample {
    type Item = io::Result<S>;

    fn next(&mut self) -> Option<io::Result<S>> {
        let reader = &mut self.reader;
        if reader.samples_read < reader.num_samples {
            reader.samples_read += 1;
            Some(Sample::read(&mut reader.reader, reader.spec.bits_per_sample))
        } else {
            None
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let samples_left = self.reader.num_samples - self.reader.samples_read;
        (samples_left as usize, Some(samples_left as usize))
    }
}

/// Tests reading the most basic wav file, one with only a WAVEFORMAT struct.
#[test]
fn read_wav_waveformat() {
    use std::fs;

    let file = fs::File::open("testsamples/waveformat-16bit-44100Hz-mono.wav")
                        .ok().expect("failed to open file");
    let buf_reader = io::BufReader::new(file);
    let mut wav_reader = WavReader::new(buf_reader)
                                   .ok().expect("failed to read header");

    assert_eq!(wav_reader.spec().channels, 1);
    assert_eq!(wav_reader.spec().sample_rate, 44100);
    assert_eq!(wav_reader.spec().bits_per_sample, 16);

    let samples: Vec<i16> = wav_reader.samples()
                                      .map(|r| r.ok().unwrap())
                                      .collect();

    // The test file has been prepared with these exact four samples.
    assert_eq!(&samples[..], &[2, -3, 5, -7]);
}

#[test]
fn read_wav_waveformat_ex() {
    // TODO: add a test sample that uses WAVEFORMATEX and verify that it can be
    // read properly.
}

#[test]
fn read_wav_waveformat_extensible() {
    // TODO: add a test sample that uses WAVEFORMATEXTENSIBLE (as produced by
    // Hound itself actually, so this should not be too hard), and verify that
    // it can be read properly.
}
<|endoftext|>