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

mod lib;

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

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

static VERSION : &'static str = "0.1"; // String not valid
static SLEEP_MS: u32 = 5000;
static CODE_FAILED: i8 = 3;
static CODE_SUCCESS: i8 = 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,
// }

fn main() {

    let pool = lib::db_connect(lib::mysql_options(), SLEEP_MS);

    let converter = Converter::new(&CONFIG.general.ffmpeg_bin,&CONFIG.general.mp3_quality , pool.clone());
    let mut print_pause = true;
    loop {
        if let Some(result) = lib::request_entry(& pool) {
            print_pause = true;
            let qid = result.qid.clone();                 //&QueryCodes::InProgress as i32
            lib::set_query_code(&mut pool.get_conn().unwrap(), &1, &result.qid).ok().expect("Failed to set query code!");
            lib::set_query_state(&pool.clone(),&qid, "started", false);

            let succes;
            {
                let mut left_files: Vec<String> = Vec::with_capacity(2);
                if result.playlist {
                    succes = match handle_playlist(result, &converter,&mut left_files) {
                        Ok(v) => Ok(v),
                        Err(e) => {println!("Playlist Error: {:?}", e); Err(e) }
                    };
                }else{
                    succes = match handle_download(result, &None, &converter,&mut left_files) {
                        Ok(v) => Ok(v),
                        Err(e) => {println!("Download Error: {:?}", e); Err(e) }
                    };
                }

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

            let code: i8 = if succes.is_ok() {
                CODE_SUCCESS//QueryCode
            } else {
                CODE_FAILED//QueryCode
            };
            lib::set_query_code(&mut pool.get_conn().unwrap(), &code,&qid).ok().expect("Failed to set query code!");

            let state = if code == 2 {
                "finished"
            } else {
                "failed"
            };
            lib::set_query_state(&pool.clone(),&qid, state, true);

        } else {
            if print_pause { println!("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 we need to download the file via the socket connector,
///which will output a mp3, or if requested, the video but with the highest quality.
///Thus in case of a DMCA we can't pick a quality anymore.
///Also the filename depends on the socket output then.
///
///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<Option<String>,DownloadError>{
    //update progress
    let is_zipped = match *folder {
        Some(_) => true,
        None => false,
    };
    if !downl_db.compress {
        lib::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 name = match download.get_file_name() { // get filename
        Ok(v) => v,
        Err(DownloadError::DMCAError) => { //now request via lib.. // k if( k == Err(DownloadError::DMCAError) )
            println!("DMCA error!");
            match download.lib_request_video() {
                Err(err) => { println!("Offliberty-call error {:?}", err); return Err(err); },
                Ok(v) => { dmca = true; v },
            }
        },
        Err(e) => { // unknown error / restricted source etc.. abort
            println!("Unknown error: {:?}", e);
            return Err(e);
        },
    };

    let name_http_valid = lib::format_file_name(&name, &download, &downl_db.qid);

    let file_path = lib::format_file_path(&downl_db.qid, folder.clone(), false);
    file_db.push(file_path.clone());
    let save_path = lib::format_save_path(folder.clone(),&name, &download, &downl_db.qid);

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

    let is_splitted_video = if dmca {
        false
    } else {
        lib::is_split_container(&downl_db.quality)
    };
    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 {
        if !downl_db.compress {
            lib::update_steps(&downl_db.pool.clone(),&downl_db.qid, 2, total_steps,false);
        }

        //download first file, download audio raw source if specified or video
        try!(download.download_file(&file_path, convert_audio));

        if is_splitted_video {
            // download audio file & convert together
            lib::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);
            file_db.push(audio_path.clone());

            println!("Downloading audio.. {}", audio_path);
            try!(download.download_file(&audio_path, true));

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

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

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

        }else{ // we're already done, only need to copy if it's not raw audio source
            if !download.is_audio() {
                try!(lib::move_file(&file_path, &save_path));
            }
        }
    }

    if download.is_audio(){ // if audio-> convert m4a to mp3, which converts directly to downl. dir
        if !downl_db.compress {
            lib::update_steps(&downl_db.pool.clone(),&downl_db.qid, total_steps, total_steps, false);
        }
        try!(converter.extract_audio(&file_path, &save_path,convert_audio));
        try!(remove_file(&file_path));
    }else{
        if is_splitted_video {
            try!(remove_file(&file_path));
        }else{
            try!(lib::move_file(&file_path, &save_path));
        }
    }
    file_db.pop();

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

    if folder.is_some() {
        Ok(Some(save_path))
    } else {
        Ok(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
fn handle_playlist(mut downl_db: DownloadDB, converter: &Converter, file_db: &mut Vec<String>) -> Result<Option<String>, DownloadError>{
    let mut max_steps: i32 = if downl_db.compress { 4 } else { 3 };
    lib::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.save_dir,pl_id));
    println!("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());
        playlist_name = lib::url_encode(&playlist_name);
        println!("pl name {}",playlist_name);
        try!(std::fs::create_dir(&downl_db.folder));
        file_db.push(downl_db.folder.clone());
    }
    lib::update_steps(&downl_db.pool.clone(),&downl_db.qid, 2, max_steps,false);
    println!("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
    };

    println!("got em");
    max_steps += file_ids.len() as i32;
    let mut current_step = 2;
    let mut current_url: String;
    // 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 {
            lib::update_steps(&downl_db.pool.clone(),&downl_db.qid, current_step, max_steps,false);
        }
        downl_db.update_video(format!("https://wwww.youtube.com/watch?v={}",id), current_step as i64);
        println!("id: {}",id);
        let db_copy = downl_db.clone();
        match handle_download(db_copy, &handler_folder, converter, file_db) {
            Err(e) => println!("error downloading {}: {:?}",id,e),
            Ok(e) => {  if downl_db.compress {
                            file_delete_list.push(e.unwrap());
                        }
                },
        }
    }

    println!("downloaded all videos");
    if downl_db.compress { // zip to file, add to db & remove all sources
        let zip_name = format!("{}.zip",playlist_name);
        let zip_file = format!("{}/{}",&CONFIG.general.save_dir,zip_name);
        try!(lib::zip_folder(&downl_db.folder, &zip_file));
        lib::add_file_entry(&downl_db.pool.clone(), &pl_id,&zip_name, &playlist_name);
        try!(lib::delete_files(file_delete_list));
        try!(remove_dir(downl_db.folder));
        file_db.pop();
    }

    Ok(None)
}
<commit_msg>another progress update error (move->copy problem..)<commit_after>extern crate mysql;
extern crate toml;
extern crate rustc_serialize;
#[macro_use]
extern crate lazy_static;

mod lib;

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

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

static VERSION : &'static str = "0.1"; // String not valid
static SLEEP_MS: u32 = 5000;
static CODE_FAILED: i8 = 3;
static CODE_SUCCESS: i8 = 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,
// }

fn main() {

    let pool = lib::db_connect(lib::mysql_options(), SLEEP_MS);

    let converter = Converter::new(&CONFIG.general.ffmpeg_bin,&CONFIG.general.mp3_quality , pool.clone());
    let mut print_pause = true;
    loop {
        if let Some(result) = lib::request_entry(& pool) {
            print_pause = true;
            let qid = result.qid.clone();                 //&QueryCodes::InProgress as i32
            lib::set_query_code(&mut pool.get_conn().unwrap(), &1, &result.qid).ok().expect("Failed to set query code!");
            lib::set_query_state(&pool.clone(),&qid, "started", false);

            let succes;
            {
                let mut left_files: Vec<String> = Vec::with_capacity(2);
                if result.playlist {
                    succes = match handle_playlist(result, &converter,&mut left_files) {
                        Ok(v) => Ok(v),
                        Err(e) => {println!("Playlist Error: {:?}", e); Err(e) }
                    };
                }else{
                    succes = match handle_download(result, &None, &converter,&mut left_files) {
                        Ok(v) => Ok(v),
                        Err(e) => {println!("Download Error: {:?}", e); Err(e) }
                    };
                }

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

            let code: i8 = if succes.is_ok() {
                CODE_SUCCESS//QueryCode
            } else {
                CODE_FAILED//QueryCode
            };
            lib::set_query_code(&mut pool.get_conn().unwrap(), &code,&qid).ok().expect("Failed to set query code!");

            let state = if code == 2 {
                "finished"
            } else {
                "failed"
            };
            lib::set_query_state(&pool.clone(),&qid, state, true);

        } else {
            if print_pause { println!("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 we need to download the file via the socket connector,
///which will output a mp3, or if requested, the video but with the highest quality.
///Thus in case of a DMCA we can't pick a quality anymore.
///Also the filename depends on the socket output then.
///
///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<Option<String>,DownloadError>{
    //update progress
    let is_zipped = match *folder {
        Some(_) => true,
        None => false,
    };
    if !downl_db.compress {
        lib::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 name = match download.get_file_name() { // get filename
        Ok(v) => v,
        Err(DownloadError::DMCAError) => { //now request via lib.. // k if( k == Err(DownloadError::DMCAError) )
            println!("DMCA error!");
            match download.lib_request_video() {
                Err(err) => { println!("Offliberty-call error {:?}", err); return Err(err); },
                Ok(v) => { dmca = true; v },
            }
        },
        Err(e) => { // unknown error / restricted source etc.. abort
            println!("Unknown error: {:?}", e);
            return Err(e);
        },
    };

    let name_http_valid = lib::format_file_name(&name, &download, &downl_db.qid);

    let file_path = lib::format_file_path(&downl_db.qid, folder.clone(), false);
    file_db.push(file_path.clone());
    let save_path = lib::format_save_path(folder.clone(),&name, &download, &downl_db.qid);

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

    let is_splitted_video = if dmca {
        false
    } else {
        lib::is_split_container(&downl_db.quality)
    };
    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 {
        if !downl_db.compress {
            lib::update_steps(&downl_db.pool.clone(),&downl_db.qid, 2, total_steps,false);
        }

        //download first file, download audio raw source if specified or video
        try!(download.download_file(&file_path, convert_audio));

        if is_splitted_video {
            // download audio file & convert together
            lib::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);
            file_db.push(audio_path.clone());

            println!("Downloading audio.. {}", audio_path);
            try!(download.download_file(&audio_path, true));

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

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

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

        }else{ // we're already done, only need to copy if it's not raw audio source
            if !download.is_audio() {
                try!(lib::move_file(&file_path, &save_path));
            }
        }
    }

    if download.is_audio(){ // if audio-> convert m4a to mp3, which converts directly to downl. dir
        if !downl_db.compress {
            lib::update_steps(&downl_db.pool.clone(),&downl_db.qid, total_steps, total_steps, false);
        }
        try!(converter.extract_audio(&file_path, &save_path,convert_audio));
        try!(remove_file(&file_path));
    }else{
        if is_splitted_video {
            try!(remove_file(&file_path));
        }else{
            try!(lib::move_file(&file_path, &save_path));
        }
    }
    file_db.pop();

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

    if folder.is_some() {
        Ok(Some(save_path))
    } else {
        Ok(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
fn handle_playlist(mut downl_db: DownloadDB, converter: &Converter, file_db: &mut Vec<String>) -> Result<Option<String>, DownloadError>{
    let mut max_steps: i32 = if downl_db.compress { 4 } else { 3 };
    lib::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.save_dir,pl_id));
    println!("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());
        playlist_name = lib::url_encode(&playlist_name);
        println!("pl name {}",playlist_name);
        try!(std::fs::create_dir(&downl_db.folder));
        file_db.push(downl_db.folder.clone());
    }
    lib::update_steps(&downl_db.pool.clone(),&downl_db.qid, 2, max_steps,false);
    println!("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
    };

    println!("got em");
    max_steps += file_ids.len() as i32;
    let mut current_step = 2;
    let mut current_url: String;
    // 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 {
            lib::update_steps(&downl_db.pool.clone(),&pl_id, current_step, max_steps,false);
        }
        downl_db.update_video(format!("https://wwww.youtube.com/watch?v={}",id), current_step as i64);
        println!("id: {}",id);
        let db_copy = downl_db.clone();
        match handle_download(db_copy, &handler_folder, converter, file_db) {
            Err(e) => println!("error downloading {}: {:?}",id,e),
            Ok(e) => {  if downl_db.compress {
                            file_delete_list.push(e.unwrap());
                        }
                },
        }
    }

    println!("downloaded all videos");
    if downl_db.compress { // zip to file, add to db & remove all sources
        let zip_name = format!("{}.zip",playlist_name);
        let zip_file = format!("{}/{}",&CONFIG.general.save_dir,zip_name);
        try!(lib::zip_folder(&downl_db.folder, &zip_file));
        lib::add_file_entry(&downl_db.pool.clone(), &pl_id,&zip_name, &playlist_name);
        try!(lib::delete_files(file_delete_list));
        try!(remove_dir(downl_db.folder));
        file_db.pop();
    }

    Ok(None)
}
<|endoftext|>


<commit_before>extern crate git2;
extern crate clap;

use git2::*;
use std::env;
use std::path::Path;
use std::path::PathBuf;
use std::process::Command;
use std::process::exit;

const CENTRAL_NAME:    &'static str = "bsw/central";
const AUTOMATION_USER: &'static str = "automation";

// FIXME: hardcoded path
const TMP_REPO_DIR:    &'static str = "/home/christian/gerrit_testsite/tmp_automation_repo";

fn module_review_upload(project: &str, newrev: &str) {

  let tmp_repo = Repository::init_bare(TMP_REPO_DIR).unwrap();
  let _ = in_tmp_repo("fetch --all");

  transfer_to_tmp(newrev);
  let parent_commit_obj = tmp_repo.revparse_single(CENTRAL_NAME).unwrap();
  let mut parent_commit_oid = parent_commit_obj.as_commit().unwrap().id();

  let module_name = Path::new(project).components().last().unwrap();
  let s = module_name.as_ref().to_str().unwrap();
  let oldrev = format!(
    "{}",tmp_repo.revparse_single(&format!("remotes/modules/{}/master",s)).unwrap().id()
    );

  {
    let old = tmp_repo.revparse_single(&oldrev).unwrap().id();
    let new = tmp_repo.revparse_single(&newrev).unwrap().id();

    if !tmp_repo.graph_descendant_of(new,old).unwrap() {
      println!(".");
      println!("===========================================================");
      println!("======== Commit not based on master, rebase first! ========");
      println!("===========================================================");
      return;
    }
  }

  let walk = {
    let mut walk = tmp_repo.revwalk().unwrap();
    walk.set_sorting( Sort::from_bits(5).unwrap());
    let _ = walk.push_range(&format!("{}..{}", oldrev, newrev));
    walk
  };

  println!("===== project path: {}", project);
  println!("===== Apply commits from {} to {}", oldrev, newrev);

  for rev in walk {
    let newrev = format!("{}",rev.unwrap());
    if oldrev == newrev { continue; }
    println!("===== Apply commit {}", newrev);

    let module_commit_obj = tmp_repo.revparse_single(&newrev).unwrap();
    let module_commit = module_commit_obj.as_commit().unwrap();
    let module_tree = module_commit.tree().unwrap();

    let parent_commit = tmp_repo.find_commit(parent_commit_oid).unwrap();

    let new_tree = {
      let master_tree = parent_commit.tree().unwrap();
      let new_tree_oid = module_to_subfolder(Path::new(module_name.as_ref()), &module_tree, &master_tree);
      tmp_repo.find_tree(new_tree_oid).unwrap()
    };

    parent_commit_oid = make_commit(&tmp_repo, &new_tree, module_commit, &vec!(&parent_commit)).unwrap();

  }

  println!(""); println!("");
  println!("===================== Doing actual upload in central git ========================");
  let x = push_from_tmp(
    &tmp_repo,
    &tmp_repo.find_commit(parent_commit_oid).unwrap(),
    CENTRAL_NAME,
    "refs/for/master"
    ).unwrap();
  println!("{}", x);
  println!("==== The review upload may have worked, even if it says error below. Look UP! ====");
}

fn central_submit(newrev: &str) {
  println!("central_submit");

  let module_names = get_module_names(newrev);
  let tmp_repo = setup_tmp_repo(&module_names);
  transfer_to_tmp(newrev);

  let central_commit_obj = tmp_repo.revparse_single(newrev).unwrap();
  let central_commit = central_commit_obj.as_commit().unwrap();
  let central_tree = central_commit.tree().unwrap();
  let _ = tmp_repo.branch(CENTRAL_NAME,central_commit,true);

  for module_name in module_names {
    let module_master_commit_obj =
      tmp_repo.revparse_single(&format!("remotes/modules/{}/master",module_name)).unwrap();
    let module_master_commit =
      module_master_commit_obj.as_commit().unwrap();
    let _ = tmp_repo.branch(&format!("modules/{}",module_name),module_master_commit,true);

    let parents = vec!(module_master_commit);
    let old_tree_oid = module_master_commit.tree().unwrap().id();

    let module_path = { let mut p = PathBuf::new(); p.push("modules"); p.push(&module_name); p };

    let new_tree_oid = central_tree.get_path(&module_path).unwrap().id();

    if new_tree_oid != old_tree_oid {

      let new_tree = tmp_repo.find_tree(new_tree_oid).unwrap();

      let module_commit = make_commit(&tmp_repo, &new_tree, central_commit, &parents);
      let x = push_from_tmp(
        &tmp_repo,
        &tmp_repo.find_commit(module_commit.unwrap()).unwrap(),
        &format!("bsw/modules/{}",module_name),
        "master"
        ).unwrap();
      println!("{}", x);
    }
  }
}

fn transfer_to_tmp(rev: &str) {
  Command::new("git")
    .arg("branch").arg("-f").arg("tmp").arg(rev)
    .output().expect("failed to call git");

  Command::new("git")
    .arg("push").arg("--force").arg(TMP_REPO_DIR).arg("tmp")
    .output().expect("failed to call git");

  Command::new("git")
    .arg("branch").arg("-D").arg("tmp")
    .output().expect("failed to call git");
}

fn in_tmp_repo(cmd: &str) -> Result<String, std::io::Error> {
  let args: Vec<&str> = cmd.split(" ").collect();
  Command::new("git")
    .env("GIT_DIR",TMP_REPO_DIR)
    .args(&args).output().map(|output|
                              format!("{}", String::from_utf8_lossy(&output.stderr)))
}

fn setup_tmp_repo(modules: &Vec<String>) -> Repository {
  let repo = Repository::init_bare(TMP_REPO_DIR).unwrap();

  if !repo.find_remote("central_repo").is_ok() {
    let _ = repo.remote("central_repo",
                        &format!("ssh://{}@gerrit-test-git/{}.git",AUTOMATION_USER,CENTRAL_NAME)
                       );
  }

  for module in modules.iter() {
    let output = Command::new("ssh")
      .arg("-p").arg("29418")
      .arg("gerrit-test-git")
      .arg("gerrit")
      .arg("create-project")
      .arg(format!("bsw/modules/{}",module))
      .arg("--empty-commit")
      .output()
      .expect("failed to create project");

    println!("create-project: {}", String::from_utf8_lossy(&output.stderr));

    let remote_url = format!("ssh://{}@gerrit-test-git:29418/bsw/modules/{}.git",
                             AUTOMATION_USER,
                             module
                            );

    let remote_name = format!("modules/{}",module);
    if !repo.find_remote(&remote_name).is_ok() {
      let _ = repo.remote(&remote_name, &remote_url);
    }
  }

  let _ = in_tmp_repo("fetch --all");

  return repo;
}

fn module_to_subfolder(path: &Path, module_tree: &Tree, master_tree: &Tree) -> Oid {
  let mpath = Path::new("modules");
  let modules_oid = master_tree.get_path(mpath).unwrap().id();
  let tmp_repo = Repository::init_bare(TMP_REPO_DIR).unwrap();

  let modules_tree = tmp_repo.find_tree(modules_oid).unwrap();
  let mut mbuilder = tmp_repo.treebuilder(Some(&modules_tree)).unwrap();
  mbuilder.insert(path, module_tree.id(), 0o0040000).expect("mbuilder insert failed"); // GIT_FILEMODE_TREE
  let mtree = mbuilder.write().unwrap();

  let mut builder = tmp_repo.treebuilder(Some(master_tree)).unwrap();
  builder.insert(mpath, mtree, 0o0040000).expect("builder insert failed"); // GIT_FILEMODE_TREE
  let r = builder.write().unwrap();
  println!("module_to_subfolder {}", r);
  return r;
}

fn get_module_names(rev: &str) -> Vec<String> {
  let central_repo = Repository::open(".").unwrap();

  let object = central_repo.revparse_single(rev).unwrap();
  let commit = object.as_commit().unwrap();
  let tree = commit.tree().unwrap();

  let modules_o = tree.get_path(&Path::new("modules")).unwrap()
    .to_object(&central_repo).unwrap();
  let modules = modules_o.as_tree().unwrap();

  let mut names = Vec::<String>::new();
  for module in modules.iter() {
    names.push(module.name().unwrap().to_string());
  }
  return names;
}

fn push_from_tmp(tmp_repo: &Repository,
                 commit: &Commit,
                 repo: &str ,to: &str)
  -> Result<String, std::io::Error> {
    let _ = tmp_repo.set_head_detached(commit.id());
    in_tmp_repo(
      &format!("push ssh://{}@gerrit-test-git:29418/{}.git HEAD:{}",
               AUTOMATION_USER,
               repo,
               to
              )
      )
  }

fn make_commit(repo: &Repository, tree: &Tree, base: &Commit, parents: &[&Commit]) -> Option<Oid> {
  if parents.len() != 0 {
    let _ = repo.set_head_detached(parents[0].id());
  }
  repo.commit(
    Some("HEAD"),
    &base.author(),
    &base.committer(),
    &base.message().unwrap_or("no message"),
    tree,
    parents
    ).ok()
}

fn main() { exit(main_ret()); } fn main_ret() -> i32 {

  let args = clap::App::new("centralgithook")
    .arg(clap::Arg::with_name("oldrev").long("oldrev").takes_value(true))
    .arg(clap::Arg::with_name("newrev").long("newrev").takes_value(true))
    .arg(clap::Arg::with_name("project").long("project").takes_value(true))
    .arg(clap::Arg::with_name("refname").long("refname").takes_value(true))
    .arg(clap::Arg::with_name("uploader").long("uploader").takes_value(true))
    .arg(clap::Arg::with_name("commit").long("commit").takes_value(true))
    .arg(clap::Arg::with_name("change").long("change").takes_value(true))
    .arg(clap::Arg::with_name("change-url").long("change-url").takes_value(true))
    .arg(clap::Arg::with_name("change-owner").long("change-owner").takes_value(true))
    .arg(clap::Arg::with_name("branch").long("branch").takes_value(true))
    .arg(clap::Arg::with_name("submitter").long("submitter").takes_value(true))
    .arg(clap::Arg::with_name("topic").long("topic").takes_value(true))
    .get_matches();

  let newrev = args.value_of("newrev").unwrap_or("");
  let project = args.value_of("project").unwrap_or("");
  let refname = args.value_of("refname").unwrap_or("");
  let commit = args.value_of("commit").unwrap_or("");

  if let Some(hook) = env::args().nth(0) {
    let is_review = refname == "refs/for/master";
    let is_module = project != CENTRAL_NAME;
    let is_update = hook.ends_with("ref-update");
    let is_submit = hook.ends_with("change-merged");

    // // TODO
    // let uploader = args.value_of("uploader").unwrap_or("");
    // if !is_review && !uploader.contains("Automation") {
    //   println!("only push to refs/for/master");
    //   return 1;
    // }

    if is_submit { central_submit(commit); }
    else if !is_module && is_update && !is_review { central_submit(newrev); }
    else if is_module && is_update && is_review { module_review_upload(project,newrev); return 1; }
  }

  return 0;
}

<commit_msg>better error handling<commit_after>extern crate git2;
extern crate clap;

use git2::*;
use std::env;
use std::path::Path;
use std::path::PathBuf;
use std::process::Command;
use std::process::exit;

const CENTRAL_NAME:    &'static str = "bsw/central";
const AUTOMATION_USER: &'static str = "automation";

// FIXME: hardcoded path
const TMP_REPO_DIR:    &'static str = "/home/christian/gerrit_testsite/tmp_automation_repo";

fn module_review_upload(project: &str, newrev: &str) -> Result<(),git2::Error> {

  let tmp_repo = try!(Repository::init_bare(TMP_REPO_DIR));
  let _ = in_tmp_repo("fetch --all");

  transfer_to_tmp(newrev);
  let parent_commit_obj = try!(tmp_repo.revparse_single(CENTRAL_NAME));
  let mut parent_commit_oid: git2::Oid = try!(parent_commit_obj
                                              .as_commit()
                                              .map(|x| x.id())
                                              .ok_or(git2::Error::from_str("could not get commit from obj")));

  let module_name: &str = try!(Path::new(project)
                               .components()
                               .last()
                               .map(|x| x.as_os_str().to_str().expect("not a valid unicode string"))
                               .ok_or(git2::Error::from_str("needs to be valid name")));
  let module_path = format!("remotes/modules/{}/master", module_name);
  let object = try!(tmp_repo.revparse_single(&module_path).map(|x| x.id()));
  let oldrev = format!("{}", object);

  {
    let old = try!(tmp_repo.revparse_single(&oldrev).map(|x| x.id()));
    let new = try!(tmp_repo.revparse_single(&newrev).map(|x| x.id()));

    if !try!(tmp_repo.graph_descendant_of(new,old)) {
      println!(".");
      println!("===========================================================");
      println!("======== Commit not based on master, rebase first! ========");
      println!("===========================================================");
      return Ok(());
    }
  }

  let walk = {
    let mut walk = try!(tmp_repo.revwalk());
    walk.set_sorting( Sort::from_bits(5).unwrap());
    let _ = walk.push_range(&format!("{}..{}", oldrev, newrev));
    walk
  };

  println!("===== project path: {}", project);
  println!("===== Apply commits from {} to {}", oldrev, newrev);

  for rev in walk {
    let newrev = format!("{}", try!(rev));
    if oldrev == newrev { continue; }
    println!("===== Apply commit {}", newrev);

    let module_commit_obj = try!(tmp_repo.revparse_single(&newrev));
    let module_commit = try!(module_commit_obj.as_commit()
                             .ok_or(git2::Error::from_str("object is not actually a commit")));
    let module_tree = try!(module_commit.tree());

    let parent_commit = try!(tmp_repo.find_commit(parent_commit_oid));

    let new_tree = {
      let master_tree: Tree = try!(parent_commit.tree());
      let new_tree_oid = module_to_subfolder(
        Path::new(module_name),
        &module_tree,
        &master_tree);
      try!(tmp_repo.find_tree(new_tree_oid))
    };

    parent_commit_oid = make_commit(&tmp_repo, &new_tree, module_commit, &vec!(&parent_commit)).unwrap();

  }

  println!(""); println!("");
  println!("===================== Doing actual upload in central git ========================");
  let x = push_from_tmp(
    &tmp_repo,
    &tmp_repo.find_commit(parent_commit_oid).unwrap(),
    CENTRAL_NAME,
    "refs/for/master"
    ).unwrap();
  println!("{}", x);
  println!("==== The review upload may have worked, even if it says error below. Look UP! ====");
  Ok(())
}

fn central_submit(newrev: &str) {
  println!("central_submit");

  let module_names = get_module_names(newrev);
  let tmp_repo = setup_tmp_repo(&module_names);
  transfer_to_tmp(newrev);

  let central_commit_obj = tmp_repo.revparse_single(newrev).unwrap();
  let central_commit = central_commit_obj.as_commit().unwrap();
  let central_tree = central_commit.tree().unwrap();
  let _ = tmp_repo.branch(CENTRAL_NAME,central_commit,true);

  for module_name in module_names {
    let module_master_commit_obj =
      tmp_repo.revparse_single(&format!("remotes/modules/{}/master",module_name)).unwrap();
    let module_master_commit =
      module_master_commit_obj.as_commit().unwrap();
    let _ = tmp_repo.branch(&format!("modules/{}",module_name),module_master_commit,true);

    let parents = vec!(module_master_commit);
    let old_tree_oid = module_master_commit.tree().unwrap().id();

    let module_path = { let mut p = PathBuf::new(); p.push("modules"); p.push(&module_name); p };

    let new_tree_oid = central_tree.get_path(&module_path).unwrap().id();

    if new_tree_oid != old_tree_oid {

      let new_tree = tmp_repo.find_tree(new_tree_oid).unwrap();

      let module_commit = make_commit(&tmp_repo, &new_tree, central_commit, &parents);
      let x = push_from_tmp(
        &tmp_repo,
        &tmp_repo.find_commit(module_commit.unwrap()).unwrap(),
        &format!("bsw/modules/{}",module_name),
        "master"
        ).unwrap();
      println!("{}", x);
    }
  }
}

fn transfer_to_tmp(rev: &str) {
  Command::new("git")
    .arg("branch").arg("-f").arg("tmp").arg(rev)
    .output().expect("failed to call git");

  Command::new("git")
    .arg("push").arg("--force").arg(TMP_REPO_DIR).arg("tmp")
    .output().expect("failed to call git");

  Command::new("git")
    .arg("branch").arg("-D").arg("tmp")
    .output().expect("failed to call git");
}

fn in_tmp_repo(cmd: &str) -> Result<String, std::io::Error> {
  let args: Vec<&str> = cmd.split(" ").collect();
  Command::new("git")
    .env("GIT_DIR",TMP_REPO_DIR)
    .args(&args).output().map(|output|
                              format!("{}", String::from_utf8_lossy(&output.stderr)))
}

fn setup_tmp_repo(modules: &Vec<String>) -> Repository {
  let repo = Repository::init_bare(TMP_REPO_DIR).unwrap();

  if !repo.find_remote("central_repo").is_ok() {
    let _ = repo.remote("central_repo",
                        &format!("ssh://{}@gerrit-test-git/{}.git",AUTOMATION_USER,CENTRAL_NAME)
                       );
  }

  for module in modules.iter() {
    let output = Command::new("ssh")
      .arg("-p").arg("29418")
      .arg("gerrit-test-git")
      .arg("gerrit")
      .arg("create-project")
      .arg(format!("bsw/modules/{}",module))
      .arg("--empty-commit")
      .output()
      .expect("failed to create project");

    println!("create-project: {}", String::from_utf8_lossy(&output.stderr));

    let remote_url = format!("ssh://{}@gerrit-test-git:29418/bsw/modules/{}.git",
                             AUTOMATION_USER,
                             module
                            );

    let remote_name = format!("modules/{}",module);
    if !repo.find_remote(&remote_name).is_ok() {
      let _ = repo.remote(&remote_name, &remote_url);
    }
  }

  let _ = in_tmp_repo("fetch --all");

  return repo;
}

fn module_to_subfolder(path: &Path, module_tree: &Tree, master_tree: &Tree) -> Oid {
  let mpath = Path::new("modules");
  let modules_oid = master_tree.get_path(mpath).unwrap().id();
  let tmp_repo = Repository::init_bare(TMP_REPO_DIR).unwrap();

  let modules_tree = tmp_repo.find_tree(modules_oid).unwrap();
  let mut mbuilder = tmp_repo.treebuilder(Some(&modules_tree)).unwrap();
  mbuilder.insert(path, module_tree.id(), 0o0040000).expect("mbuilder insert failed"); // GIT_FILEMODE_TREE
  let mtree = mbuilder.write().unwrap();

  let mut builder = tmp_repo.treebuilder(Some(master_tree)).unwrap();
  builder.insert(mpath, mtree, 0o0040000).expect("builder insert failed"); // GIT_FILEMODE_TREE
  let r = builder.write().unwrap();
  println!("module_to_subfolder {}", r);
  return r;
}

fn get_module_names(rev: &str) -> Vec<String> {
  let central_repo = Repository::open(".").unwrap();

  let object = central_repo.revparse_single(rev).unwrap();
  let commit = object.as_commit().unwrap();
  let tree = commit.tree().unwrap();

  let modules_o = tree.get_path(&Path::new("modules")).unwrap()
    .to_object(&central_repo).unwrap();
  let modules = modules_o.as_tree().unwrap();

  let mut names = Vec::<String>::new();
  for module in modules.iter() {
    names.push(module.name().unwrap().to_string());
  }
  return names;
}

fn push_from_tmp(tmp_repo: &Repository,
                 commit: &Commit,
                 repo: &str ,to: &str)
  -> Result<String, std::io::Error> {
    let _ = tmp_repo.set_head_detached(commit.id());
    in_tmp_repo(
      &format!("push ssh://{}@gerrit-test-git:29418/{}.git HEAD:{}",
               AUTOMATION_USER,
               repo,
               to
              )
      )
  }

fn make_commit(repo: &Repository, tree: &Tree, base: &Commit, parents: &[&Commit]) -> Option<Oid> {
  if parents.len() != 0 {
    let _ = repo.set_head_detached(parents[0].id());
  }
  repo.commit(
    Some("HEAD"),
    &base.author(),
    &base.committer(),
    &base.message().unwrap_or("no message"),
    tree,
    parents
    ).ok()
}

fn main() { exit(main_ret()); } fn main_ret() -> i32 {

  let args = clap::App::new("centralgithook")
    .arg(clap::Arg::with_name("oldrev").long("oldrev").takes_value(true))
    .arg(clap::Arg::with_name("newrev").long("newrev").takes_value(true))
    .arg(clap::Arg::with_name("project").long("project").takes_value(true))
    .arg(clap::Arg::with_name("refname").long("refname").takes_value(true))
    .arg(clap::Arg::with_name("uploader").long("uploader").takes_value(true))
    .arg(clap::Arg::with_name("commit").long("commit").takes_value(true))
    .arg(clap::Arg::with_name("change").long("change").takes_value(true))
    .arg(clap::Arg::with_name("change-url").long("change-url").takes_value(true))
    .arg(clap::Arg::with_name("change-owner").long("change-owner").takes_value(true))
    .arg(clap::Arg::with_name("branch").long("branch").takes_value(true))
    .arg(clap::Arg::with_name("submitter").long("submitter").takes_value(true))
    .arg(clap::Arg::with_name("topic").long("topic").takes_value(true))
    .get_matches();

  let newrev = args.value_of("newrev").unwrap_or("");
  let project = args.value_of("project").unwrap_or("");
  let refname = args.value_of("refname").unwrap_or("");
  let commit = args.value_of("commit").unwrap_or("");

  if let Some(hook) = env::args().nth(0) {
    let is_review = refname == "refs/for/master";
    let is_module = project != CENTRAL_NAME;
    let is_update = hook.ends_with("ref-update");
    let is_submit = hook.ends_with("change-merged");

    // // TODO
    // let uploader = args.value_of("uploader").unwrap_or("");
    // if !is_review && !uploader.contains("Automation") {
    //   println!("only push to refs/for/master");
    //   return 1;
    // }

    if is_submit { central_submit(commit); }
    else if !is_module && is_update && !is_review { central_submit(newrev); }
    else if is_module && is_update && is_review { let _ = module_review_upload(project,newrev); return 1; }
  }

  return 0;
}

<|endoftext|>


<commit_before>use std::io;
use std::io::prelude::*;

fn print_prompt() {
    print!("db > ");
    io::stdout().flush().unwrap();
}

fn main() {
    println!("Hello, world!");
    let stdin = io::stdin();
    let lines = stdin.lock().lines();

    print_prompt();
    for line in lines {
        if let Ok(input) = line {
            if input.starts_with(".exit") {
                break
            } else {
                println!("Unrecognized command '{}'", input);
            }
        }
        print_prompt();
    }
}
<commit_msg>Part 2<commit_after>use std::io;
use std::io::prelude::*;

fn print_prompt() {
    print!("db > ");
    io::stdout().flush().unwrap();
}

enum MetaCommand {
    Exit
}

fn do_meta_command(command: &str) -> Result<MetaCommand, ParseError> {
    if command.starts_with(".exit") {
        Ok(MetaCommand::Exit)
    } else {
        Err(ParseError::Unrecognized)
    }
}

enum ParseError {
    Unrecognized
}

enum Statement {
    Insert, Select
}

fn prepare_statement(input: &str) -> Result<Statement, ParseError> {
    if input.starts_with("insert") {
        Ok(Statement::Insert)
    }  else if input.starts_with("select") {
        Ok(Statement::Select)
    } else {
        Err(ParseError::Unrecognized)
    }
}

fn execute_statement(statement: Statement) {
  match statement {
    Statement::Insert => println!("This is where we would do an insert."),
    Statement::Select => println!("This is where we would do a select.")
  }
}

fn main() {
    println!("Hello, world!");
    let stdin = io::stdin();
    let lines = stdin.lock().lines();

    print_prompt();
    for line in lines {
        if let Ok(input) = line {
            if let Some('.') = input.chars().next() {
                match do_meta_command(&input) {
                    Ok(MetaCommand::Exit) => break,
                    Err(ParseError::Unrecognized) => println!("Unrecognized command '{}'", input)
                }
            }

            match prepare_statement(&input) {
                Ok(statement) => {
                    execute_statement(statement);
                    println!("Executed.")
                }
                Err(ParseError::Unrecognized) => println!("Unrecognized keyword at start of {}", input),
            }
        }
        print_prompt();
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate capnp;
extern crate capnp_rpc;
extern crate ramp;

use std::sync::mpsc::channel;
use std::thread;
use std::sync::{RwLock, Arc};

use ramp::Database;

use capnp::capability::{Server};
use capnp_rpc::ez_rpc::EzRpcServer;

// this is generated automatically as part of the build process
// see build.rs
pub mod ramp_capnp {
  include!(concat!(env!("OUT_DIR"), "/ramp_capnp.rs"));
}
use ramp_capnp::ramp_interface;

type DB = Arc<RwLock<Database>>;

struct RampServer {
    db:DB
}

impl RampServer {
    fn new() -> RampServer {
        // create a new Database, wrap in RwLock and Arc
        let db = Arc::new(RwLock::new(Database::new()));
        RampServer{db:db}
    }
}

impl ramp_interface::Server for RampServer {
    fn prepare(&mut self, mut context: ramp_interface::PrepareContext) {
        let (params, mut results) = context.get();
        let key = params.get_key().unwrap();
        let value = params.get_value().unwrap();
        let timestamp = params.get_value().unwrap();
    }

    fn commit(&mut self, mut context: ramp_interface::CommitContext) {

    }
}

fn main() {
    println!("Hello, world!");

    let ramp = RampServer::new();

    println!("Goodbye forever.");
}
<commit_msg>setting context.done<commit_after>#[macro_use]
extern crate capnp;
extern crate capnp_rpc;
extern crate ramp;

use std::sync::mpsc::channel;
use std::thread;
use std::sync::{RwLock, Arc};

use ramp::Database;

use capnp::capability::{Server};
use capnp_rpc::ez_rpc::EzRpcServer;

// this is generated automatically as part of the build process
// see build.rs
pub mod ramp_capnp {
  include!(concat!(env!("OUT_DIR"), "/ramp_capnp.rs"));
}
use ramp_capnp::ramp_interface;

type DB = Arc<RwLock<Database>>;

struct RampServer {
    db:DB
}

impl RampServer {
    fn new() -> RampServer {
        // create a new Database, wrap in RwLock and Arc
        let db = Arc::new(RwLock::new(Database::new()));
        RampServer{db:db}
    }
}

impl ramp_interface::Server for RampServer {
    fn prepare(&mut self, mut context: ramp_interface::PrepareContext) {
        {
            let (params, mut results) = context.get();
            let key = params.get_key().unwrap();
            let value = params.get_value().unwrap();
            let timestamp = params.get_value().unwrap();
            let d = self.db.write(); // hold lock till prepare is done
        }

        context.done();
    }

    fn commit(&mut self, mut context: ramp_interface::CommitContext) {

    }
}

fn main() {
    println!("Hello, world!");

    let ramp = RampServer::new();

    println!("Goodbye forever.");
}
<|endoftext|>


<commit_before>#![crate_id = "rboy"]
#![license = "MIT"]

#![feature(phase)]
#[phase(plugin, link)] extern crate log;

extern crate native;
extern crate getopts;
extern crate sync;
extern crate time;
extern crate sdl;
#[cfg(test)]
extern crate test;

use cpu::CPU;
use sync::DuplexStream;
use sync::{Arc,RWLock};
use std::comm::{Disconnected,Empty};

mod register;
mod mbc;
mod mmu;
mod cpu;
mod serial;
mod timer;
mod keypad;
mod gpu;
mod sound;
mod gbmode;
mod util;

static SCALE: uint = 2;

#[cfg(not(test))]
#[start]
fn start(argc: int, argv: **u8) -> int { native::start(argc, argv, main) }

fn main() {
	let args = std::os::args();
	let opts = [ getopts::optflag("s", "serial", "Output serial to stdout"), getopts::optflag("c", "classic", "Force Classic mode") ];
	let matches = match getopts::getopts(args.tail(), opts) {
		Ok(m) => { m }
		Err(f) => { println!("{}", f.to_err_msg()); return }
	};

	let filename = if !matches.free.is_empty() {
		matches.free.get(0).clone()
	} else {
		println!("{}", getopts::usage(args.get(0).clone().append(" <filename>").as_slice(), opts));
		return;
	};

	sdl::init([sdl::InitVideo]);
	sdl::wm::set_caption("RBoy - A gameboy in Rust", "rboy");
	let screen = match sdl::video::set_video_mode(160*SCALE as int, 144*SCALE as int, 32, [sdl::video::HWSurface], [sdl::video::DoubleBuf]) {
		Ok(screen) => screen,
		Err(err) => fail!("failed to open screen: {}", err),
	};

	let (sdlstream, cpustream) = sync::duplex();
	let rawscreen = [0x00u8,.. 160*144*3];
	let arc = Arc::new(RWLock::new(rawscreen));
	let arc2 = arc.clone();
	native::task::spawn(proc() cpuloop(&cpustream, arc2, filename.as_slice(), &matches));

	let mut timer = std::io::timer::Timer::new().unwrap();
	let periodic = timer.periodic(8);

	'main : loop {
		periodic.recv();
		match sdlstream.try_recv() {
			Err(Disconnected) => { break 'main },
			Ok(_) => recalculate_screen(&screen, &arc),
			Err(Empty) => {},
		}
		'event : loop {
			match sdl::event::poll_event() {
				sdl::event::QuitEvent => break 'main,
				sdl::event::NoEvent => break 'event,
				sdl::event::KeyEvent(sdl::event::EscapeKey, _, _, _)
					=> break 'main,
				sdl::event::KeyEvent(sdl::event::LShiftKey, true, _, _)
					=> sdlstream.send(SpeedUp),
				sdl::event::KeyEvent(sdl::event::LShiftKey, false, _, _)
					=> sdlstream.send(SlowDown),
				sdl::event::KeyEvent(sdlkey, true, _, _) => {
					match sdl_to_keypad(sdlkey) {
						Some(key) => sdlstream.send(KeyDown(key)),
						None => {},
					}
				},
				sdl::event::KeyEvent(sdlkey, false, _, _) => {
					match sdl_to_keypad(sdlkey) {
						Some(key) => sdlstream.send(KeyUp(key)),
						None => {},
					}
				},
				_ => {}
			}
		}
	}
}

fn sdl_to_keypad(key: sdl::event::Key) -> Option<keypad::KeypadKey> {
	match key {
		sdl::event::ZKey => Some(keypad::A),
		sdl::event::XKey => Some(keypad::B),
		sdl::event::UpKey => Some(keypad::Up),
		sdl::event::DownKey => Some(keypad::Down),
		sdl::event::LeftKey => Some(keypad::Left),
		sdl::event::RightKey => Some(keypad::Right),
		sdl::event::SpaceKey => Some(keypad::Select),
		sdl::event::ReturnKey => Some(keypad::Start),
		_ => None,
	}
}

fn recalculate_screen(screen: &sdl::video::Surface, arc: &Arc<RWLock<[u8,.. 160*144*3]>>) {
	let data = arc.read();
	for y in range(0u, 144) {
		for x in range(0u, 160) {
			screen.fill_rect(
				Some(sdl::Rect { x: (x*SCALE) as i16, y: (y*SCALE) as i16, w: SCALE as u16, h: SCALE as u16 }),
				sdl::video::RGB(data[y*160*3 + x*3 + 0],
				                data[y*160*3 + x*3 + 1],
				                data[y*160*3 + x*3 + 2])
			);
		}
	}
	screen.flip();
}

enum GBEvent {
	KeyUp(keypad::KeypadKey),
	KeyDown(keypad::KeypadKey),
	SpeedUp,
	SlowDown,
}

fn cpuloop(channel: &DuplexStream<uint, GBEvent>, arc: Arc<RWLock<[u8,.. 160*144*3]>>, filename: &str, matches: &getopts::Matches) {
	let opt_c = match matches.opt_present("classic") {
		true => CPU::new(filename),
		false => CPU::new_cgb(filename),
	};
	let mut c = match opt_c
	{
		Some(cpu) => { cpu },
		None => { error!("Could not get a valid gameboy"); return; },
	};
	c.mmu.serial.tostdout = matches.opt_present("serial");

	let mut timer = std::io::timer::Timer::new().unwrap();
	let mut periodic = timer.periodic(8);

	let waitticks = (4194.304 * 4.0) as uint;

	let mut ticks = 0;
	'cpuloop: loop {
		while ticks < waitticks {
			ticks += c.cycle();
			if c.mmu.gpu.updated {
				c.mmu.gpu.updated = false;
				let mut data = arc.write();
				for i in range(0, data.len()) { data[i] = c.mmu.gpu.data[i]; }
				if channel.send_opt(0).is_err() { break 'cpuloop };
			}
		}
		ticks -= waitticks;
		periodic.recv();

		match channel.try_recv() {
			Ok(event) => match event {
				KeyUp(key) => c.mmu.keypad.keyup(key),
				KeyDown(key) => c.mmu.keypad.keydown(key),
				SpeedUp => periodic = timer.periodic(1),
				SlowDown => periodic = timer.periodic(8),
			},
			Err(Empty) => {},
			Err(Disconnected) => { break },
		};
	}
}
<commit_msg>Fixed moving of libsync and DuplexStream<commit_after>#![crate_id = "rboy"]
#![license = "MIT"]

#![feature(phase)]
#[phase(plugin, link)] extern crate log;

extern crate native;
extern crate getopts;
extern crate time;
extern crate sdl;
#[cfg(test)]
extern crate test;

use cpu::CPU;
use std::sync::{Arc,RWLock};
use std::comm::{DuplexStream,Disconnected,Empty};

mod register;
mod mbc;
mod mmu;
mod cpu;
mod serial;
mod timer;
mod keypad;
mod gpu;
mod sound;
mod gbmode;
mod util;

static SCALE: uint = 2;

#[cfg(not(test))]
#[start]
fn start(argc: int, argv: **u8) -> int { native::start(argc, argv, main) }

fn main() {
	let args = std::os::args();
	let opts = [ getopts::optflag("s", "serial", "Output serial to stdout"), getopts::optflag("c", "classic", "Force Classic mode") ];
	let matches = match getopts::getopts(args.tail(), opts) {
		Ok(m) => { m }
		Err(f) => { println!("{}", f.to_err_msg()); return }
	};

	let filename = if !matches.free.is_empty() {
		matches.free.get(0).clone()
	} else {
		println!("{}", getopts::usage(args.get(0).clone().append(" <filename>").as_slice(), opts));
		return;
	};

	sdl::init([sdl::InitVideo]);
	sdl::wm::set_caption("RBoy - A gameboy in Rust", "rboy");
	let screen = match sdl::video::set_video_mode(160*SCALE as int, 144*SCALE as int, 32, [sdl::video::HWSurface], [sdl::video::DoubleBuf]) {
		Ok(screen) => screen,
		Err(err) => fail!("failed to open screen: {}", err),
	};

	let (sdlstream, cpustream) = std::comm::duplex();
	let rawscreen = [0x00u8,.. 160*144*3];
	let arc = Arc::new(RWLock::new(rawscreen));
	let arc2 = arc.clone();
	native::task::spawn(proc() cpuloop(&cpustream, arc2, filename.as_slice(), &matches));

	let mut timer = std::io::timer::Timer::new().unwrap();
	let periodic = timer.periodic(8);

	'main : loop {
		periodic.recv();
		match sdlstream.try_recv() {
			Err(Disconnected) => { break 'main },
			Ok(_) => recalculate_screen(&screen, &arc),
			Err(Empty) => {},
		}
		'event : loop {
			match sdl::event::poll_event() {
				sdl::event::QuitEvent => break 'main,
				sdl::event::NoEvent => break 'event,
				sdl::event::KeyEvent(sdl::event::EscapeKey, _, _, _)
					=> break 'main,
				sdl::event::KeyEvent(sdl::event::LShiftKey, true, _, _)
					=> sdlstream.send(SpeedUp),
				sdl::event::KeyEvent(sdl::event::LShiftKey, false, _, _)
					=> sdlstream.send(SlowDown),
				sdl::event::KeyEvent(sdlkey, true, _, _) => {
					match sdl_to_keypad(sdlkey) {
						Some(key) => sdlstream.send(KeyDown(key)),
						None => {},
					}
				},
				sdl::event::KeyEvent(sdlkey, false, _, _) => {
					match sdl_to_keypad(sdlkey) {
						Some(key) => sdlstream.send(KeyUp(key)),
						None => {},
					}
				},
				_ => {}
			}
		}
	}
}

fn sdl_to_keypad(key: sdl::event::Key) -> Option<keypad::KeypadKey> {
	match key {
		sdl::event::ZKey => Some(keypad::A),
		sdl::event::XKey => Some(keypad::B),
		sdl::event::UpKey => Some(keypad::Up),
		sdl::event::DownKey => Some(keypad::Down),
		sdl::event::LeftKey => Some(keypad::Left),
		sdl::event::RightKey => Some(keypad::Right),
		sdl::event::SpaceKey => Some(keypad::Select),
		sdl::event::ReturnKey => Some(keypad::Start),
		_ => None,
	}
}

fn recalculate_screen(screen: &sdl::video::Surface, arc: &Arc<RWLock<[u8,.. 160*144*3]>>) {
	let data = arc.read();
	for y in range(0u, 144) {
		for x in range(0u, 160) {
			screen.fill_rect(
				Some(sdl::Rect { x: (x*SCALE) as i16, y: (y*SCALE) as i16, w: SCALE as u16, h: SCALE as u16 }),
				sdl::video::RGB(data[y*160*3 + x*3 + 0],
				                data[y*160*3 + x*3 + 1],
				                data[y*160*3 + x*3 + 2])
			);
		}
	}
	screen.flip();
}

enum GBEvent {
	KeyUp(keypad::KeypadKey),
	KeyDown(keypad::KeypadKey),
	SpeedUp,
	SlowDown,
}

fn cpuloop(channel: &DuplexStream<uint, GBEvent>, arc: Arc<RWLock<[u8,.. 160*144*3]>>, filename: &str, matches: &getopts::Matches) {
	let opt_c = match matches.opt_present("classic") {
		true => CPU::new(filename),
		false => CPU::new_cgb(filename),
	};
	let mut c = match opt_c
	{
		Some(cpu) => { cpu },
		None => { error!("Could not get a valid gameboy"); return; },
	};
	c.mmu.serial.tostdout = matches.opt_present("serial");

	let mut timer = std::io::timer::Timer::new().unwrap();
	let mut periodic = timer.periodic(8);

	let waitticks = (4194.304 * 4.0) as uint;

	let mut ticks = 0;
	'cpuloop: loop {
		while ticks < waitticks {
			ticks += c.cycle();
			if c.mmu.gpu.updated {
				c.mmu.gpu.updated = false;
				let mut data = arc.write();
				for i in range(0, data.len()) { data[i] = c.mmu.gpu.data[i]; }
				if channel.send_opt(0).is_err() { break 'cpuloop };
			}
		}
		ticks -= waitticks;
		periodic.recv();

		match channel.try_recv() {
			Ok(event) => match event {
				KeyUp(key) => c.mmu.keypad.keyup(key),
				KeyDown(key) => c.mmu.keypad.keydown(key),
				SpeedUp => periodic = timer.periodic(1),
				SlowDown => periodic = timer.periodic(8),
			},
			Err(Empty) => {},
			Err(Disconnected) => { break },
		};
	}
}
<|endoftext|>


<commit_before>// Copyright 2017 Jeremy Wall <jeremy@marzhillstudios.com>
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
#[macro_use]
extern crate clap;
extern crate ucglib;

use std::cell::RefCell;
use std::error::Error;
use std::fs::File;
use std::io;
use std::path::{Path, PathBuf};
use std::process;
use std::rc::Rc;

use ucglib::build;
use ucglib::build::assets::{Cache, MemoryCache};
use ucglib::build::Val;
use ucglib::convert::traits;
use ucglib::convert::ConverterRegistry;

// TODO(jwall): List the target output types automatically.
fn do_flags<'a, 'b>() -> clap::App<'a, 'b> {
    clap_app!(
        ucg =>
            (version: crate_version!())
            (author: crate_authors!())
            (about: "Universal Configuration Grammar compiler.")
            (@subcommand inspect =>
             (about: "Inspect a specific symbol in a ucg file.")
             (@arg sym: --sym +takes_value +required "Specify a specific binding in the ucg file to output.")
             (@arg target: --format +required +takes_value "Inspect output type. (flags, json, env, exec)")
             (@arg INPUT: +required "Input ucg file to inspect symbol from.")
            )
            (@subcommand build =>
             (about: "Build a list of ucg files.")
             (@arg recurse: -r +required conflicts_with[INPUT] "Whether we should recurse in directories or not.")
             (@arg INPUT: ... "Input ucg files or directories to build. If not provided then build the contents of the current directory.")
            )
            (@subcommand test =>
             (about: "Check a list of ucg files for errors and run test assertions.")
             (@arg recurse: -r +required conflicts_with[INPUT] "Whether we should recurse or not.")
             (@arg INPUT: ... "Input ucg files or directories to run test assertions for. If not provided it will scan the current directory for files with _test.ucg")
            )
            (@subcommand converters =>
             (about: "list the available converters")
            )
    )
}

fn run_converter(c: &traits::Converter, v: Rc<Val>, f: Option<&str>) -> traits::Result {
    let mut file: Box<std::io::Write> = match f {
        Some(f) => {
            let mut path_buf = PathBuf::from(f);
            path_buf.set_extension(c.file_ext());
            let new_path = path_buf.to_str().unwrap();
            Box::new(try!(File::create(&new_path)))
        }
        None => Box::new(io::stdout()),
    };
    c.convert(v, file.as_mut())
}

fn build_file(
    file: &str,
    validate: bool,
    cache: Rc<RefCell<Cache>>,
) -> Result<build::Builder, Box<Error>> {
    let root = PathBuf::from(file);
    let mut builder = build::Builder::new(root.parent().unwrap(), cache);
    if validate {
        builder.enable_validate_mode();
    }
    try!(builder.build_file(file));
    if validate {
        println!("{}", builder.assert_collector.summary);
    }
    Ok(builder)
}

fn do_validate(file: &str, cache: Rc<RefCell<Cache>>) -> bool {
    println!("Validating {}", file);
    match build_file(file, true, cache) {
        Ok(b) => {
            if b.assert_collector.success {
                println!("File {} Pass\n", file);
            } else {
                println!("File {} Fail\n", file);
                return false;
            }
        }
        Err(msg) => {
            eprintln!("Err: {}", msg);
            return false;
        }
    }
    return true;
}

fn do_compile(file: &str, cache: Rc<RefCell<Cache>>, registry: &ConverterRegistry) -> bool {
    println!("Building {}", file);
    let builder = match build_file(file, false, cache.clone()) {
        Ok(builder) => builder,
        Err(err) => {
            eprintln!("{}", err);
            return false;
        }
    };
    let (typ, val) = match builder.out_lock {
        Some((ref typ, ref val)) => (typ, val.clone()),
        None => {
            eprintln!("Build results in no artifacts.");
            return false;
        }
    };
    match registry.get_converter(typ) {
        Some(converter) => {
            run_converter(converter, val, Some(file)).unwrap();
            eprintln!("Build successful");
            return true;
        }
        None => {
            eprintln!("No such converter {}", typ);
            return false;
        }
    }
}

fn visit_ucg_files(
    path: &Path,
    recurse: bool,
    validate: bool,
    cache: Rc<RefCell<Cache>>,
    registry: &ConverterRegistry,
) -> Result<bool, Box<Error>> {
    let our_path = String::from(path.to_string_lossy());
    let mut result = true;
    // TODO(jwall): Report the failing files at the bottom.
    let mut summary = String::new();
    if path.is_dir() {
        let mut dir_iter = try!(std::fs::read_dir(path)).peekable();
        loop {
            let entry = match dir_iter.next() {
                Some(e) => e,
                None => {
                    println!("Exiting the loop");
                    break;
                }
            };
            let next_item = try!(entry);
            let next_path = next_item.path();
            let path_as_string = String::from(next_path.to_string_lossy());
            if next_path.is_dir() && recurse {
                if let Err(e) =
                    visit_ucg_files(&next_path, recurse, validate, cache.clone(), registry)
                {
                    eprintln!("{}", e);
                    result = false;
                }
            } else {
                if validate && path_as_string.ends_with("_test.ucg") {
                    if !do_validate(&path_as_string, cache.clone()) {
                        result = false;
                        summary.push_str(format!("{} - FAIL\n", path_as_string).as_str())
                    } else {
                        summary.push_str(format!("{} - PASS\n", path_as_string).as_str())
                    }
                } else if !validate && path_as_string.ends_with(".ucg") {
                    if !do_compile(&path_as_string, cache.clone(), registry) {
                        result = false;
                    }
                }
            }
        }
    } else if validate && our_path.ends_with("_test.ucg") {
        if !do_validate(&our_path, cache) {
            result = false;
            summary.push_str(format!("{} - FAIL\n", our_path).as_str());
        } else {
            summary.push_str(format!("{} - PASS\n", &our_path).as_str());
        }
    } else if !validate {
        if !do_compile(&our_path, cache, registry) {
            result = false;
        }
    }
    if validate {
        println!("RESULTS:");
        println!("{}", summary);
    }
    Ok(result)
}

fn inspect_command(
    matches: &clap::ArgMatches,
    cache: Rc<RefCell<Cache>>,
    registry: &ConverterRegistry,
) {
    let file = matches.value_of("INPUT").unwrap();
    let sym = matches.value_of("sym");
    let target = matches.value_of("target").unwrap();
    let root = PathBuf::from(file);
    let mut builder = build::Builder::new(root.parent().unwrap(), cache);
    match registry.get_converter(target) {
        Some(converter) => {
            // TODO(jwall): We should warn if this is a test file.
            let result = builder.build_file(file);
            if !result.is_ok() {
                eprintln!("{:?}", result.err().unwrap());
                process::exit(1);
            }
            let val = match sym {
                Some(sym_name) => builder.get_out_by_name(sym_name),
                None => builder.last,
            };
            match val {
                Some(value) => {
                    // We use None here because we always output to stdout for an inspect.
                    run_converter(converter, value, None).unwrap();
                    eprintln!("Build successful");
                    process::exit(0);
                }
                None => {
                    eprintln!("Build results in no value.");
                    process::exit(1);
                }
            }
        }
        None => {
            eprintln!("No such converter {}", target);
            process::exit(1);
        }
    }
}

fn build_command(
    matches: &clap::ArgMatches,
    cache: Rc<RefCell<Cache>>,
    registry: &ConverterRegistry,
) {
    let files = matches.values_of("INPUT");
    let recurse = matches.is_present("recurse");
    let mut ok = true;
    if files.is_none() {
        let curr_dir = std::env::current_dir().unwrap();
        let ok = visit_ucg_files(curr_dir.as_path(), recurse, false, cache.clone(), &registry);
        if let Ok(false) = ok {
            process::exit(1)
        }
    }
    for file in files.unwrap() {
        let pb = PathBuf::from(file);
        if let Ok(false) = visit_ucg_files(&pb, recurse, false, cache.clone(), &registry) {
            ok = false;
        }
    }
    if !ok {
        process::exit(1)
    }
}

fn test_command(
    matches: &clap::ArgMatches,
    cache: Rc<RefCell<Cache>>,
    registry: &ConverterRegistry,
) {
    let files = matches.values_of("INPUT");
    let recurse = matches.is_present("recurse");
    if files.is_none() {
        let curr_dir = std::env::current_dir().unwrap();
        let ok = visit_ucg_files(curr_dir.as_path(), recurse, true, cache.clone(), &registry);
        if let Ok(false) = ok {
            process::exit(1)
        }
    } else {
        let mut ok = true;
        for file in files.unwrap() {
            let pb = PathBuf::from(file);
            //if pb.is_dir() {
            if let Ok(false) =
                visit_ucg_files(pb.as_path(), recurse, true, cache.clone(), &registry)
            {
                ok = false;
            }
        }
        if !ok {
            process::exit(1)
        }
    }
    process::exit(0);
}

fn converters_command(registry: &ConverterRegistry) {
    println!("Available converters:");
    println!("");
    for (name, c) in registry.get_converter_list().iter() {
        println!("- {}", name);
        println!("  Description: {}", c.description());
        println!("  Output Extension: `.{}`", c.file_ext());
        println!("");
    }
}

fn main() {
    let mut app = do_flags();
    let app_matches = app.clone().get_matches();
    let cache: Rc<RefCell<Cache>> = Rc::new(RefCell::new(MemoryCache::new()));
    let registry = ConverterRegistry::make_registry();
    if let Some(matches) = app_matches.subcommand_matches("inspect") {
        inspect_command(matches, cache, &registry);
    } else if let Some(matches) = app_matches.subcommand_matches("build") {
        build_command(matches, cache, &registry);
    } else if let Some(matches) = app_matches.subcommand_matches("test") {
        test_command(matches, cache, &registry);
    } else if let Some(_) = app_matches.subcommand_matches("converters") {
        converters_command(&registry)
    } else {
        app.print_help().unwrap();
        println!("");
    }
}
<commit_msg>FIXES: Fixed bug when recursing through directories.<commit_after>// Copyright 2017 Jeremy Wall <jeremy@marzhillstudios.com>
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
#[macro_use]
extern crate clap;
extern crate ucglib;

use std::cell::RefCell;
use std::error::Error;
use std::fs::File;
use std::io;
use std::path::{Path, PathBuf};
use std::process;
use std::rc::Rc;

use ucglib::build;
use ucglib::build::assets::{Cache, MemoryCache};
use ucglib::build::Val;
use ucglib::convert::traits;
use ucglib::convert::ConverterRegistry;

// TODO(jwall): List the target output types automatically.
fn do_flags<'a, 'b>() -> clap::App<'a, 'b> {
    clap_app!(
        ucg =>
            (version: crate_version!())
            (author: crate_authors!())
            (about: "Universal Configuration Grammar compiler.")
            (@subcommand inspect =>
             (about: "Inspect a specific symbol in a ucg file.")
             (@arg sym: --sym +takes_value +required "Specify a specific binding in the ucg file to output.")
             (@arg target: --format +required +takes_value "Inspect output type. (flags, json, env, exec)")
             (@arg INPUT: +required "Input ucg file to inspect symbol from.")
            )
            (@subcommand build =>
             (about: "Build a list of ucg files.")
             (@arg recurse: -r +required conflicts_with[INPUT] "Whether we should recurse in directories or not.")
             (@arg INPUT: ... "Input ucg files or directories to build. If not provided then build the contents of the current directory.")
            )
            (@subcommand test =>
             (about: "Check a list of ucg files for errors and run test assertions.")
             (@arg recurse: -r +required conflicts_with[INPUT] "Whether we should recurse or not.")
             (@arg INPUT: ... "Input ucg files or directories to run test assertions for. If not provided it will scan the current directory for files with _test.ucg")
            )
            (@subcommand converters =>
             (about: "list the available converters")
            )
    )
}

fn run_converter(c: &traits::Converter, v: Rc<Val>, f: Option<&str>) -> traits::Result {
    let mut file: Box<std::io::Write> = match f {
        Some(f) => {
            let mut path_buf = PathBuf::from(f);
            path_buf.set_extension(c.file_ext());
            let new_path = path_buf.to_str().unwrap();
            Box::new(try!(File::create(&new_path)))
        }
        None => Box::new(io::stdout()),
    };
    c.convert(v, file.as_mut())
}

fn build_file(
    file: &str,
    validate: bool,
    cache: Rc<RefCell<Cache>>,
) -> Result<build::Builder, Box<Error>> {
    let root = PathBuf::from(file);
    let mut builder = build::Builder::new(root.parent().unwrap(), cache);
    if validate {
        builder.enable_validate_mode();
    }
    try!(builder.build_file(file));
    if validate {
        println!("{}", builder.assert_collector.summary);
    }
    Ok(builder)
}

fn do_validate(file: &str, cache: Rc<RefCell<Cache>>) -> bool {
    println!("Validating {}", file);
    match build_file(file, true, cache) {
        Ok(b) => {
            if b.assert_collector.success {
                println!("File {} Pass\n", file);
            } else {
                println!("File {} Fail\n", file);
                return false;
            }
        }
        Err(msg) => {
            eprintln!("Err: {}", msg);
            return false;
        }
    }
    return true;
}

fn do_compile(file: &str, cache: Rc<RefCell<Cache>>, registry: &ConverterRegistry) -> bool {
    println!("Building {}", file);
    let builder = match build_file(file, false, cache.clone()) {
        Ok(builder) => builder,
        Err(err) => {
            eprintln!("{}", err);
            return false;
        }
    };
    let (typ, val) = match builder.out_lock {
        Some((ref typ, ref val)) => (typ, val.clone()),
        None => {
            eprintln!("Build results in no artifacts.");
            return false;
        }
    };
    match registry.get_converter(typ) {
        Some(converter) => {
            run_converter(converter, val, Some(file)).unwrap();
            eprintln!("Build successful");
            return true;
        }
        None => {
            eprintln!("No such converter {}", typ);
            return false;
        }
    }
}

fn visit_ucg_files(
    path: &Path,
    recurse: bool,
    validate: bool,
    cache: Rc<RefCell<Cache>>,
    registry: &ConverterRegistry,
) -> Result<bool, Box<Error>> {
    let our_path = String::from(path.to_string_lossy());
    let mut result = true;
    // TODO(jwall): Report the failing files at the bottom.
    let mut summary = String::new();
    if path.is_dir() {
        let mut dir_iter = try!(std::fs::read_dir(path)).peekable();
        loop {
            let entry = match dir_iter.next() {
                Some(e) => e,
                None => {
                    break;
                }
            };
            let next_item = try!(entry);
            let next_path = next_item.path();
            let path_as_string = String::from(next_path.to_string_lossy());
            if next_path.is_dir() && recurse {
                if let Err(e) =
                    visit_ucg_files(&next_path, recurse, validate, cache.clone(), registry)
                {
                    eprintln!("{}", e);
                    result = false;
                }
            } else {
                if validate && path_as_string.ends_with("_test.ucg") {
                    if !do_validate(&path_as_string, cache.clone()) {
                        result = false;
                        summary.push_str(format!("{} - FAIL\n", path_as_string).as_str())
                    } else {
                        summary.push_str(format!("{} - PASS\n", path_as_string).as_str())
                    }
                } else if !validate && path_as_string.ends_with(".ucg") {
                    if !do_compile(&path_as_string, cache.clone(), registry) {
                        result = false;
                    }
                }
            }
        }
    } else if validate && our_path.ends_with("_test.ucg") {
        if !do_validate(&our_path, cache) {
            result = false;
            summary.push_str(format!("{} - FAIL\n", our_path).as_str());
        } else {
            summary.push_str(format!("{} - PASS\n", &our_path).as_str());
        }
    } else if !validate {
        if !do_compile(&our_path, cache, registry) {
            result = false;
        }
    }
    if validate && !summary.is_empty() {
        println!("RESULTS:");
        println!("{}", summary);
    }
    Ok(result)
}

fn inspect_command(
    matches: &clap::ArgMatches,
    cache: Rc<RefCell<Cache>>,
    registry: &ConverterRegistry,
) {
    let file = matches.value_of("INPUT").unwrap();
    let sym = matches.value_of("sym");
    let target = matches.value_of("target").unwrap();
    let root = PathBuf::from(file);
    let mut builder = build::Builder::new(root.parent().unwrap(), cache);
    match registry.get_converter(target) {
        Some(converter) => {
            // TODO(jwall): We should warn if this is a test file.
            let result = builder.build_file(file);
            if !result.is_ok() {
                eprintln!("{:?}", result.err().unwrap());
                process::exit(1);
            }
            let val = match sym {
                Some(sym_name) => builder.get_out_by_name(sym_name),
                None => builder.last,
            };
            match val {
                Some(value) => {
                    // We use None here because we always output to stdout for an inspect.
                    run_converter(converter, value, None).unwrap();
                    eprintln!("Build successful");
                    process::exit(0);
                }
                None => {
                    eprintln!("Build results in no value.");
                    process::exit(1);
                }
            }
        }
        None => {
            eprintln!("No such converter {}", target);
            process::exit(1);
        }
    }
}

fn build_command(
    matches: &clap::ArgMatches,
    cache: Rc<RefCell<Cache>>,
    registry: &ConverterRegistry,
) {
    let files = matches.values_of("INPUT");
    let recurse = matches.is_present("recurse");
    let mut ok = true;
    if files.is_none() {
        let curr_dir = std::env::current_dir().unwrap();
        let ok = visit_ucg_files(curr_dir.as_path(), recurse, false, cache.clone(), &registry);
        if let Ok(false) = ok {
            process::exit(1)
        }
        process::exit(0);
    }
    for file in files.unwrap() {
        let pb = PathBuf::from(file);
        if let Ok(false) = visit_ucg_files(&pb, recurse, false, cache.clone(), &registry) {
            ok = false;
        }
    }
    if !ok {
        process::exit(1)
    }
}

fn test_command(
    matches: &clap::ArgMatches,
    cache: Rc<RefCell<Cache>>,
    registry: &ConverterRegistry,
) {
    let files = matches.values_of("INPUT");
    let recurse = matches.is_present("recurse");
    if files.is_none() {
        let curr_dir = std::env::current_dir().unwrap();
        let ok = visit_ucg_files(curr_dir.as_path(), recurse, true, cache.clone(), &registry);
        if let Ok(false) = ok {
            process::exit(1)
        }
    } else {
        let mut ok = true;
        for file in files.unwrap() {
            let pb = PathBuf::from(file);
            //if pb.is_dir() {
            if let Ok(false) =
                visit_ucg_files(pb.as_path(), recurse, true, cache.clone(), &registry)
            {
                ok = false;
            }
        }
        if !ok {
            process::exit(1)
        }
    }
    process::exit(0);
}

fn converters_command(registry: &ConverterRegistry) {
    println!("Available converters:");
    println!("");
    for (name, c) in registry.get_converter_list().iter() {
        println!("- {}", name);
        println!("  Description: {}", c.description());
        println!("  Output Extension: `.{}`", c.file_ext());
        println!("");
    }
}

fn main() {
    let mut app = do_flags();
    let app_matches = app.clone().get_matches();
    let cache: Rc<RefCell<Cache>> = Rc::new(RefCell::new(MemoryCache::new()));
    let registry = ConverterRegistry::make_registry();
    if let Some(matches) = app_matches.subcommand_matches("inspect") {
        inspect_command(matches, cache, &registry);
    } else if let Some(matches) = app_matches.subcommand_matches("build") {
        build_command(matches, cache, &registry);
    } else if let Some(matches) = app_matches.subcommand_matches("test") {
        test_command(matches, cache, &registry);
    } else if let Some(_) = app_matches.subcommand_matches("converters") {
        converters_command(&registry)
    } else {
        app.print_help().unwrap();
        println!("");
    }
}
<|endoftext|>


<commit_before>// The MIT License (MIT)
//
// Copyright (c) 2016 Tim Fennell
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

//!
//! Command line program that can be sandwiched between pipes to effectively increase
//! the size of the pipe buffer.  Since linux pipes are generally limited to `64k` it
//! is sometimes useful to provide significantly more buffering between programs in a
//! pipe in order to smooth out any "lumpiness" in the flow of data.
//!

mod ringbuffer;

#[macro_use] extern crate clap;
extern crate regex;

use std::io;
use std::io::{Read,Write};
use std::sync::{Arc, Mutex, Condvar};
use std::thread;
use clap::{Arg, App};
use ringbuffer::RingBuffer;
use regex::Regex;

// How big should the thread-local buffers for the reader and writer threads be
const THREAD_BUFFER_SIZE: usize = 1024 * 64;

/// Main function that coordinates argument parsing and then delegates to the
/// `run()` function to do the actual work.
pub fn main() {
    let matches =
        App::new("pipebuffer")
            .version(crate_version!())
            .about("A tool to rapidly buffer and re-emit data in unix pipelines.")
            .arg(Arg::with_name("size")
                     .short("s").long("size")
                     .help("The size, in bytes or with k[b]/m[b]/g[b] suffix.")
                     .default_value("256m"))
            .get_matches();

    let buffer_size = match parse_memory(matches.value_of("size").unwrap()) {
        Some(size) => size,
        None       => {
            println!("{}", matches.usage());
            println!("Error: Argument {} is not a valid size.", matches.value_of("size").unwrap());
            std::process::exit(1)
        }
    };

    run(buffer_size);
}

/// Parses memory unit values from strings. Specifically accepts any value
/// that is an integer number followed optionally by `k/kb/m/mb/g/gb/p/pb` in
/// either upper or lower case. If the value can be parsed returns a
/// `Some(bytes)`, otherwise returns a None.
fn parse_memory(s: &str) -> Option<usize> {
    match Regex::new("^([0-9]+)([kmgp])b?").unwrap().captures(&s.to_lowercase()) {
        None => None,
        Some(groups) => {
            let num : usize = groups.at(1).unwrap().parse().unwrap();
            let exp = match groups.at(2) {
                Some("k") => 1,
                Some("m") => 2,
                Some("g") => 3,
                Some("p") => 4,
                _         => panic!("Unreachable")
            };
            Some(num * (1024 as usize).pow(exp))
        }
    }
}

/// Funtion that uses a pair of threads to move data from Stdin to Stdout
/// with a RungBuffer in the middle.
fn run(buffer_size: usize) {
    // The shared ring buffer and the thread handles
    let ring = Arc::new(Mutex::new(RingBuffer::new(buffer_size)));
    let cond = Arc::new(Condvar::new());

    // Setup the writer thread
    let writer_handle = {
        let ring = ring.clone();
        let cond = cond.clone();
        thread::spawn(move || {
            let mut bytes: [u8; THREAD_BUFFER_SIZE] = [0; THREAD_BUFFER_SIZE];
            let mut output = io::stdout();
            'main_loop : loop {
                let n = {
                    // Lock the buffer, but wait on it if it's empty
                    let mut buffer = ring.lock().unwrap();
                    while buffer.is_empty() {
                        if buffer.is_closed() { break 'main_loop; }
                        else { buffer = cond.wait(buffer).unwrap(); }
                    }

                    // Fetch from the buffer, and notify writers if we went from full to not full
                    let was_full = buffer.is_full();
                    let n = buffer.get(&mut bytes);
                    if was_full && n > 0 { cond.notify_one(); }
                    n
                }; // lock released here

                // Write the data, if any, to stdout
                let mut start = 0;
                while start < n { start += output.write(&bytes[start..n]).unwrap(); }
                output.flush().unwrap();
            }
        })
    };

    // Setup this thread as the reader thread
    let mut bytes: [u8; THREAD_BUFFER_SIZE] = [0; THREAD_BUFFER_SIZE];
    let mut input = io::stdin();
    loop {
        let n = input.read(&mut bytes).unwrap();
        let mut buffer = ring.lock().unwrap();

        if n == 0 { // input stream is closed
            buffer.close();
            cond.notify_one();
            break;
        }
        else {
            let mut start = 0;
            while start < n {
                while buffer.is_full() {
                    buffer = cond.wait(buffer).unwrap();
                }
                let was_empty = buffer.is_empty();
                start += buffer.put(&bytes[start..n]);
                if was_empty { cond.notify_one(); }
             }
        }
    }

    writeln!(&mut io::stderr(), "Attempting to join on the writer.").unwrap();
    writer_handle.join().unwrap();
}
<commit_msg>Added tests for parsing memory amounts.<commit_after>// The MIT License (MIT)
//
// Copyright (c) 2016 Tim Fennell
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

//!
//! Command line program that can be sandwiched between pipes to effectively increase
//! the size of the pipe buffer.  Since linux pipes are generally limited to `64k` it
//! is sometimes useful to provide significantly more buffering between programs in a
//! pipe in order to smooth out any "lumpiness" in the flow of data.
//!

mod ringbuffer;

#[macro_use] extern crate clap;
extern crate regex;

use std::io;
use std::io::{Read,Write};
use std::sync::{Arc, Mutex, Condvar};
use std::thread;
use clap::{Arg, App};
use ringbuffer::RingBuffer;
use regex::Regex;

// How big should the thread-local buffers for the reader and writer threads be
const THREAD_BUFFER_SIZE: usize = 1024 * 64;

/// Main function that coordinates argument parsing and then delegates to the
/// `run()` function to do the actual work.
pub fn main() {
    let matches =
        App::new("pipebuffer")
            .version(crate_version!())
            .about("A tool to rapidly buffer and re-emit data in unix pipelines.")
            .arg(Arg::with_name("size")
                     .short("s").long("size")
                     .help("The size, in bytes or with k[b]/m[b]/g[b] suffix.")
                     .default_value("256m"))
            .get_matches();

    let buffer_size = match parse_memory(matches.value_of("size").unwrap()) {
        Some(size) => size,
        None       => {
            println!("{}", matches.usage());
            println!("Error: Argument {} is not a valid size.", matches.value_of("size").unwrap());
            std::process::exit(1)
        }
    };

    run(buffer_size);
}

/// Parses memory unit values from strings. Specifically accepts any value
/// that is an integer number followed optionally by `k/kb/m/mb/g/gb/p/pb` in
/// either upper or lower case. If the value can be parsed returns a
/// `Some(bytes)`, otherwise returns a None.
fn parse_memory(s: &str) -> Option<usize> {
    match Regex::new("^([0-9]+)([kmgp])?b?$").unwrap().captures(&s.to_lowercase()) {
        None => None,
        Some(groups) => {
            let num : Option<usize> = groups.at(1).unwrap().parse().ok();
            let exp = match groups.at(2) {
                Some("k") => 1,
                Some("m") => 2,
                Some("g") => 3,
                Some("p") => 4,
                _         => 0
            };
            num.map(|n| n * (1024 as usize).pow(exp))
        }
    }
}

/// Funtion that uses a pair of threads to move data from Stdin to Stdout
/// with a RungBuffer in the middle.
fn run(buffer_size: usize) {
    // The shared ring buffer and the thread handles
    let ring = Arc::new(Mutex::new(RingBuffer::new(buffer_size)));
    let cond = Arc::new(Condvar::new());

    // Setup the writer thread
    let writer_handle = {
        let ring = ring.clone();
        let cond = cond.clone();
        thread::spawn(move || {
            let mut bytes: [u8; THREAD_BUFFER_SIZE] = [0; THREAD_BUFFER_SIZE];
            let mut output = io::stdout();
            'main_loop : loop {
                let n = {
                    // Lock the buffer, but wait on it if it's empty
                    let mut buffer = ring.lock().unwrap();
                    while buffer.is_empty() {
                        if buffer.is_closed() { break 'main_loop; }
                        else { buffer = cond.wait(buffer).unwrap(); }
                    }

                    // Fetch from the buffer, and notify writers if we went from full to not full
                    let was_full = buffer.is_full();
                    let n = buffer.get(&mut bytes);
                    if was_full && n > 0 { cond.notify_one(); }
                    n
                }; // lock released here

                // Write the data, if any, to stdout
                let mut start = 0;
                while start < n { start += output.write(&bytes[start..n]).unwrap(); }
                output.flush().unwrap();
            }
        })
    };

    // Setup this thread as the reader thread
    let mut bytes: [u8; THREAD_BUFFER_SIZE] = [0; THREAD_BUFFER_SIZE];
    let mut input = io::stdin();
    loop {
        let n = input.read(&mut bytes).unwrap();
        let mut buffer = ring.lock().unwrap();

        if n == 0 { // input stream is closed
            buffer.close();
            cond.notify_one();
            break;
        }
        else {
            let mut start = 0;
            while start < n {
                while buffer.is_full() {
                    buffer = cond.wait(buffer).unwrap();
                }
                let was_empty = buffer.is_empty();
                start += buffer.put(&bytes[start..n]);
                if was_empty { cond.notify_one(); }
             }
        }
    }

    writeln!(&mut io::stderr(), "Attempting to join on the writer.").unwrap();
    writer_handle.join().unwrap();
}

////////////////////////////////////////////////////////////////////////////////
// Tests only beyond this point
////////////////////////////////////////////////////////////////////////////////

#[test]
fn test_parse_mem_bytes() -> () {
    assert!(parse_memory("1") == Some::<usize>(1));
    assert!(parse_memory("1024") == Some::<usize>(1024));
    assert!(parse_memory("1000000000") == Some::<usize>(1000000000));
    assert!(parse_memory("10000000000000000000000000000") == None);
}

#[test]
fn test_parse_mem_suffixed() -> () {
    assert!(parse_memory("1k")      == Some::<usize>(1024));
    assert!(parse_memory("99k")     == Some::<usize>(99 * 1024));
    assert!(parse_memory("99kb")    == Some::<usize>(99 * 1024));
    assert!(parse_memory("99K")     == Some::<usize>(99 * 1024));
    assert!(parse_memory("99KB")    == Some::<usize>(99 * 1024));

    assert!(parse_memory("1m")      == Some::<usize>(1024*1024));
    assert!(parse_memory("10m")     == Some::<usize>(10*1024*1024));
    assert!(parse_memory("101m")    == Some::<usize>(101*1024*1024));
    assert!(parse_memory("1024m")   == Some::<usize>(1024*1024*1024));

    assert!(parse_memory("6g")      == Some::<usize>(6*1024*1024*1024));
    assert!(parse_memory("60g")     == Some::<usize>(60*1024*1024*1024));

    assert!(parse_memory("1p")     == Some::<usize>(1024*1024*1024*1024));
}

#[test]
fn test_parse_mem_fails() -> () {
    assert!(parse_memory("") == None);
    assert!(parse_memory("k") == None);
    assert!(parse_memory("kb") == None);
    assert!(parse_memory("foo") == None);
    assert!(parse_memory("not1024m") == None);
    assert!(parse_memory("-12g") == None);
    assert!(parse_memory("12x") == None);
    assert!(parse_memory("7y") == None);
    assert!(parse_memory("1024x1024") == None);
    assert!(parse_memory("1024mi") == None);
}
<|endoftext|>


<commit_before>// Implements http://rosettacode.org/wiki/Menu

use std::io;

// Print the menu followed by the prompt
fn print_both(menu: &[&str], prompt: &str) {

    // Iterate through array and print index, period, and menu item
    for (i, item) in menu.iter().enumerate() {
        println!("{}. {}", i, item);
    }

    // Print the prompt
    println!("{}", prompt);
}

// Grab the next line of input
fn next_input() -> Option<uint> {

    let line = io::stdin().read_line().unwrap();
    let input: Option<uint> = line.trim().parse();
    return input;
}

fn select<'a>(menu: &'a [&str], prompt: &str) -> &'a str {

    // Check if menu is empty
    if menu.len() == 0 {
        return "";
    }

    // Loop until user inputs a valid menu index
    loop {

        print_both(menu, prompt);

        let input = next_input();

        let num = match input {
            Some(num) => num,
            None      => continue
        };

        if let 0...3 = num {
            return menu[num];
        }
    }
}

#[cfg(not(test))]
fn main() {

    let prompt = "Choose one.";
    let items = ["fee fie", "huff and puff", "mirror mirror", "tick tock"];
    let menu = items.slice(0,4);

    println!("{}", select(menu, prompt));
}

#[test]
fn test_empty_menu() {
    let prompt = "Choose one.";
    let items = ["fee fie", "huff and puff", "mirror mirror", "tick tock"];
    let menu = items.slice(0,0);
    assert_eq!(select(menu, prompt), "");
}

// Need to add more tests but having trouble simulating std input
<commit_msg>implement comments by pfalabella<commit_after>// Implements http://rosettacode.org/wiki/Menu

use std::io;

// Print the menu followed by the prompt
fn print_both(menu: &[&str], prompt: &str) {

    // Iterate through array and print index, period, and menu item
    for (i, item) in menu.iter().enumerate() {
        println!("{}. {}", i, item);
    }

    // Print the prompt
    println!("{}", prompt);
}

// Grab the next line of input
fn next_input() -> Option<uint> {

    let line = io::stdin().read_line().unwrap();
    let input: Option<uint> = line.trim().parse();
    input
}

fn select<'a>(menu: &'a [&str], prompt: &str) -> &'a str {

    // Check if menu is empty
    if menu.len() == 0 {
        return "";
    }

    // Loop until user inputs a valid menu index
    loop {

        print_both(menu, prompt);

        let input = next_input();

        let num = match input {
            Some(num) => num,
            None      => continue
        };

        if num < menu.len() {
            return menu[num];
        }
    }
}

#[cfg(not(test))]
fn main() {

    let prompt = "Choose one.";
    let menu = &["fee fie", "huff and puff", "mirror mirror", "tick tock"];
    println!("{}", select(menu, prompt));
}

#[test]
fn test_empty_menu() {
    let prompt = "Choose one.";
    let menu = &[];
    assert_eq!(select(menu, prompt), "");
}
<|endoftext|>