<commit_before>extern crate getopts;
extern crate izzet;
extern crate toml;

use getopts::{Matches, Options};
use izzet::error::{Error, Result};
use izzet::post;
use izzet::gen;
use izzet::get_opener;
use izzet::config::Config;
use std::env;
use std::fs::DirBuilder;
use std::path::PathBuf;
use std::process;
use std::io::Write;

const PROG_NAME: &str = env!("CARGO_PKG_NAME");

fn create_site(m: &Matches) -> Result<()> {
    let dir = m.free.get(1)
        .map(|s| PathBuf::from(s))
        .unwrap_or(env::current_dir()?);

    let opener = get_opener(m.opt_present("force"));

    // create a default configuration file
    let config: Config = Default::default();
    let config = toml::to_string(&config)?;
    opener.open(dir.join(izzet::CONFIG_FILE))
          .and_then(|mut f| f.write(config.as_bytes()))
          .map_err(|e| format!("fail to create {}: {}", izzet::CONFIG_FILE, e))?;

    // create other files
    // XXX may need to do more things here later
    for filename in izzet::SITE_FILES {
        opener.open(dir.join(filename))
              .map_err(|e| format!("fail to create {}: {}", filename, e))?;
    }

    // create directories
    for dirname in izzet::SITE_DIRS {
        DirBuilder::new()
            .recursive(m.opt_present("force"))
            .create(dir.join(dirname))
            .map_err(|e| format!("fail to create {}: {}", dirname, e))?;
    }

    // create default templates
    for &(filename, html) in izzet::SITE_TEMPLATES {
        let mut file = opener.open(dir.join(izzet::TEMPLATES_DIR)
                                      .join(filename))
                             .map_err(|e| format!("fail to create {}: {}", filename, e))?;
        file.write(html)?;
    }

    Ok(())
}

fn usage(opts: &Options) {
    println!("{}", opts.usage(&format!("usage: {} <options> <args>", PROG_NAME)));
}

fn run(m: Matches) -> Result<()> {
    // Note that now we have no configuration file yet
    if m.opt_present("new") {
        create_site(&m)?;
        return Ok(());
    }

    // Load config file as a basis which may be overwritten
    // later by the command-line options.
    let mut config = Config::from_file(m.opt_str("config")
                                        .map(|p| PathBuf::from(p))
                                        .unwrap_or(env::current_dir()?)
                                        .join(izzet::CONFIG_FILE))?;

    config.force = Some(m.opt_present("force"));

    if m.opt_present("article") || m.opt_present("page") {
        let link = m.free.get(1)
            .ok_or(Error::new("fail to get the link of article".to_string()))?;
        post::create_post(link.clone(), config, m.opt_present("article"))?;
    }
    else if m.opt_present("gen") {
        config.in_dir = m.opt_str("input");
        config.out_dir = m.opt_str("output");
        gen::generate(config)?;
    }

    Ok(())
}

fn main() {
    let mut opts = Options::new();

    // One of these flags should be specified
    opts.optflag("n", "new", "Initialize an empty site at the given location.");
    opts.optflag("a", "article", "Create an article with the given permalink.");
    opts.optflag("p", "page", "Create a page with the given permalink.");
    opts.optflag("g", "gen", "Generate site, can be used along with -i and -o \
                              to specify the input and output location.");
    opts.optflag("f", "force", "Overwrite existing files when creating articles, \
                                generating site output files, etc.");

    opts.optopt("c", "config", "Run with the given configuration file. By default \
                                configuration file will be looked for under the \
                                current directory.", "CONFIG");
    opts.optopt("i", "input", "Input site directory. Read input files from current \
                              directory by default.", "INPUT");
    opts.optopt("o", "output", "Output site directory. Write to current directory \
                               by default.", "OUTPUT");

    opts.optflag("h", "help", "Show this help message.");
    opts.optflag("V", "version", "Display version information.");

    let matches = match opts.parse(env::args()) {
        Ok(m) => m,
        Err(e) => {
            eprintln!("{}\ntry `{1} -h` or `{1} --help` to see the help.", e, PROG_NAME);
            process::exit(1);
        },
    };

    if matches.opt_present("help") {
        usage(&opts);
        return;
    }

    if matches.opt_present("version") {
        println!("{} {}", PROG_NAME, env!("CARGO_PKG_VERSION"));
        return;
    }

    let mutex_opts = ["new", "article", "gen", "page"];

    match mutex_opts.iter()
                    .map(|o| matches.opt_present(o) as u32)
                    .sum::<u32>() {
        0 => {
            println!("nothing to do.");
            process::exit(1);
        },
        1 => (),
        _ => {
            eprintln!("only one of `-n', `-a', `-p' and `-g' could be specified");
            process::exit(1);
        }
    };

    if let Err(e) = run(matches) {
        eprintln!("{}", e);
        process::exit(1);
    }
}
<commit_msg>Simplify command dispatch<commit_after>extern crate getopts;
extern crate izzet;
extern crate toml;

use getopts::{Matches, Options};
use izzet::error::{Error, Result};
use izzet::post;
use izzet::gen;
use izzet::get_opener;
use izzet::config::Config;
use std::env;
use std::fs::DirBuilder;
use std::path::PathBuf;
use std::process;
use std::io::Write;

const PROG_NAME: &str = env!("CARGO_PKG_NAME");

fn create_site(m: &Matches) -> Result<()> {
    let dir = m.free.get(1)
        .map(|s| PathBuf::from(s))
        .unwrap_or(env::current_dir()?);

    let opener = get_opener(m.opt_present("force"));

    // create a default configuration file
    let config: Config = Default::default();
    let config = toml::to_string(&config)?;
    opener.open(dir.join(izzet::CONFIG_FILE))
          .and_then(|mut f| f.write(config.as_bytes()))
          .map_err(|e| format!("fail to create {}: {}", izzet::CONFIG_FILE, e))?;

    // create other files
    // XXX may need to do more things here later
    for filename in izzet::SITE_FILES {
        opener.open(dir.join(filename))
              .map_err(|e| format!("fail to create {}: {}", filename, e))?;
    }

    // create directories
    for dirname in izzet::SITE_DIRS {
        DirBuilder::new()
            .recursive(m.opt_present("force"))
            .create(dir.join(dirname))
            .map_err(|e| format!("fail to create {}: {}", dirname, e))?;
    }

    // create default templates
    for &(filename, html) in izzet::SITE_TEMPLATES {
        let mut file = opener.open(dir.join(izzet::TEMPLATES_DIR)
                                      .join(filename))
                             .map_err(|e| format!("fail to create {}: {}", filename, e))?;
        file.write(html)?;
    }

    Ok(())
}

fn usage(opts: &Options) {
    println!("{}", opts.usage(&format!("usage: {} <options> <args>", PROG_NAME)));
}

fn run(m: Matches, action: &str) -> Result<()> {
    // Note that now we have no configuration file yet
    if action == "new" {
        create_site(&m)?;
        return Ok(());
    }

    // Load config file as a basis which may be overwritten
    // later by the command-line options.
    let mut config = Config::from_file(m.opt_str("config")
                                        .map(|p| PathBuf::from(p))
                                        .unwrap_or(env::current_dir()?)
                                        .join(izzet::CONFIG_FILE))?;

    config.force = Some(m.opt_present("force"));

    match action {
        "article" => {
            let link = m.free.get(1)
                .ok_or(Error::new("need the link of the article".to_string()))?;
            post::create_post(link.clone(), config, true)?;
        },

        "page" => {
            let link = m.free.get(1)
                .ok_or(Error::new("need the link of the page".to_string()))?;
            post::create_post(link.clone(), config, false)?;
        },

        "gen" => {
            config.in_dir = m.opt_str("input");
            config.out_dir = m.opt_str("output");
            gen::generate(config)?;
        },

        _ => {
            eprintln!("unknown action {}", action);
            process::exit(1);
        }
    }

    Ok(())
}

fn main() {
    let mut opts = Options::new();

    // One of these flags should be specified
    opts.optflag("n", "new", "Initialize an empty site at the given location.");
    opts.optflag("a", "article", "Create an article with the given permalink.");
    opts.optflag("p", "page", "Create a page with the given permalink.");
    opts.optflag("g", "gen", "Generate site, can be used along with -i and -o \
                              to specify the input and output location.");
    opts.optflag("f", "force", "Overwrite existing files when creating articles, \
                                generating site output files, etc.");

    opts.optopt("c", "config", "Run with the given configuration file. By default \
                                configuration file will be looked for under the \
                                current directory.", "CONFIG");
    opts.optopt("i", "input", "Input site directory. Read input files from current \
                              directory by default.", "INPUT");
    opts.optopt("o", "output", "Output site directory. Write to current directory \
                               by default.", "OUTPUT");

    opts.optflag("h", "help", "Show this help message.");
    opts.optflag("V", "version", "Display version information.");

    let matches = match opts.parse(env::args()) {
        Ok(m) => m,
        Err(e) => {
            eprintln!("{}\ntry `{1} -h` or `{1} --help` to see the help.", e, PROG_NAME);
            process::exit(1);
        },
    };

    if matches.opt_present("help") {
        usage(&opts);
        return;
    }

    if matches.opt_present("version") {
        println!("{} {}", PROG_NAME, env!("CARGO_PKG_VERSION"));
        return;
    }

    let mutex_opts = ["new", "article", "gen", "page"];
    let action = mutex_opts
        .iter()
        .filter(|o| matches.opt_present(o))
        .collect::<Vec<_>>();

    match action.len() {
        0 => {
            println!("nothing to do");
            return;
        },
        1 => {
            if let Err(e) = run(matches, action.first().unwrap()) {
                eprintln!("{}", e);
                process::exit(1);
            }
        },
        _ => {
            eprintln!("only one of `-n', `-a', `-p' and `-g' could be specified");
            process::exit(1);
        }
    }
}
<|endoftext|>


<commit_before>#![feature(plugin)]
#![plugin(docopt_macros)]

#[macro_use]
extern crate docopt;
extern crate env_logger;
extern crate log;
extern crate rustc_serialize;
extern crate website;

use env_logger::LogBuilder;
use log::LogLevelFilter;

const DEFAULT_PORT: u16 = 9000;

docopt!(Args derive Debug, r"
My personal website.

Usage:
    website [<port>]
", arg_port: Option<u16>);

fn main() {
    LogBuilder::new().filter(None, LogLevelFilter::Info).init().unwrap();

    let args: Args = Args::docopt().decode().unwrap_or_else(|e| e.exit());
    website::listen(("localhost", args.arg_port.unwrap_or(DEFAULT_PORT)))
}
<commit_msg>fix(cli): add help flags<commit_after>#![feature(plugin)]
#![plugin(docopt_macros)]

#[macro_use]
extern crate docopt;
extern crate env_logger;
extern crate log;
extern crate rustc_serialize;
extern crate website;

use env_logger::LogBuilder;
use log::LogLevelFilter;

const DEFAULT_PORT: u16 = 9000;

docopt!(Args derive Debug, r"
My personal website.

Usage:
    website [<port>]
    website (-h | --help)
", arg_port: Option<u16>);

fn main() {
    LogBuilder::new().filter(None, LogLevelFilter::Info).init().unwrap();

    let args: Args = Args::docopt().decode().unwrap_or_else(|e| e.exit());
    website::listen(("localhost", args.arg_port.unwrap_or(DEFAULT_PORT)))
}
<|endoftext|>


<commit_before>extern crate crossterm;
#[macro_use]
extern crate downcast;
extern crate git_version;
extern crate indexmap;
#[macro_use]
extern crate lazy_static;
extern crate libc;
extern crate nix;
#[macro_use]
extern crate prolog_parser;
#[macro_use]
extern crate ref_thread_local;

use git_version::git_version;
use nix::sys::signal;

mod prolog;

use crate::prolog::machine::*;
use crate::prolog::machine::streams::*;
use crate::prolog::read::*;

use std::env;
use std::sync::atomic::Ordering;

extern fn handle_sigint(signal: libc::c_int) {
    let signal = signal::Signal::from_c_int(signal).unwrap();
    if signal == signal::Signal::SIGINT {
	INTERRUPT.store(true, Ordering::Relaxed);
    }
}

fn main() {
    let handler = signal::SigHandler::Handler(handle_sigint);
    unsafe { signal::signal(signal::Signal::SIGINT, handler) }.unwrap();

    if env::args().skip(1).any(|a| a == "-v" || a == "--version") {
        println!("{:}", git_version!());
        return;
    }

    let mut wam = Machine::new(readline::input_stream(), Stream::stdout());
    wam.run_top_level();
}
<commit_msg>main: fix version reporting without git<commit_after>extern crate crossterm;
#[macro_use]
extern crate downcast;
extern crate git_version;
extern crate indexmap;
#[macro_use]
extern crate lazy_static;
extern crate libc;
extern crate nix;
#[macro_use]
extern crate prolog_parser;
#[macro_use]
extern crate ref_thread_local;

use git_version::git_version;
use nix::sys::signal;

mod prolog;

use crate::prolog::machine::*;
use crate::prolog::machine::streams::*;
use crate::prolog::read::*;

use std::env;
use std::sync::atomic::Ordering;

extern fn handle_sigint(signal: libc::c_int) {
    let signal = signal::Signal::from_c_int(signal).unwrap();
    if signal == signal::Signal::SIGINT {
	INTERRUPT.store(true, Ordering::Relaxed);
    }
}

fn main() {
    let handler = signal::SigHandler::Handler(handle_sigint);
    unsafe { signal::signal(signal::Signal::SIGINT, handler) }.unwrap();

    if env::args().skip(1).any(|a| a == "-v" || a == "--version") {
        println!("{:}", git_version!(cargo_prefix = "cargo:", fallback = "unknown"));
        return;
    }

    let mut wam = Machine::new(readline::input_stream(), Stream::stdout());
    wam.run_top_level();
}
<|endoftext|>


<commit_before>// Copyright 2015 Robert Helmer <rhelmer@rhelmer.org>. See the LICENSE
// file at the top-level directory of this distribution.

/**
  * Symbolapi - a microservice which accepts lists of symbol+addresses, and returns
  * a list of symbolicated functions.
  *
  * The goal is to take an HTTP JSON request such as:
  * {"stacks":[
  *  [[0,11723767],[1, 65802]]],
  *  "memoryMap":[
  *    ["xul.pdb","44E4EC8C2F41492B9369D6B9A059577C2"],
  *    ["wntdll.pdb","D74F79EB1F8D4A45ABCD2F476CCABACC2"]
  *  ],
  *  "version":4
  * }
  *
  * This would download the corresponding symbol files (e.g. from S3), and return the
  * function names for the corresponding addresses like so:
  * {"symbolicatedStacks": [
  *   [
  *     "XREMain::XRE_mainRun() (in xul.pdb)",
  *     "KiUserCallbackDispatcher (in wntdll.pdb)"]
  *   ],
  *   "knownModules": [true, true]
  * }
  */

extern crate breakpad_symbols;
extern crate hyper;
#[macro_use]
extern crate log;
extern crate log4rs;
extern crate rustc_serialize;
extern crate toml;

use std::io::{Read, Write};
use std::path::{Path, PathBuf};
use std::thread;

use breakpad_symbols::SymbolFile;
use hyper::Client;
use hyper::server::{Server, Request, Response};
use hyper::status::StatusCode;
use rustc_serialize::json;

/**
  * Incoming JSON request format.
  */
// required JSON keys are non-snakecase
#[allow(non_snake_case)]
#[derive(RustcDecodable)]
pub struct SymbolRequest {
    memoryMap: Vec<(String,String)>,
    // TODO check that key actually fits in 8-bit int
    stacks: Vec<Vec<(i8,i64)>>,
    version: u8,
}

/**
  * Outgoing JSON response format.
  */
// required JSON keys are non-snakecase
#[allow(non_snake_case)]
#[derive(RustcEncodable)]
pub struct SymbolResponse {
    symbolicatedStacks: Vec<Vec<String>>,
    knownModules: Vec<bool>,
}

fn main() {
    log4rs::init_file("config/log.toml", Default::default()).unwrap();

    let address = "0.0.0.0:8080";

    info!("Listening on {}", address);
    Server::http(address).unwrap().handle(server).unwrap();
}

/**
  * Receives single HTTP requests and demuxes to symbols file fetches from S3 bucket.
  */
fn server(mut req: Request, mut res: Response) {
    info!("incoming connection from {}", req.remote_addr);

    match req.method {
        hyper::Post => {
            let mut res = res.start().unwrap();

            let mut buffer = String::new();
            let _ = req.read_to_string(&mut buffer);
            debug!("raw POST: {:?}", &buffer);

            let decoded: SymbolRequest = json::decode(&buffer).unwrap();

            debug!("decoded memoryMap: {:?}", decoded.memoryMap);
            debug!("decoded stacks: {:?}", decoded.stacks);
            debug!("decoded version: {}", decoded.version);

            let symbol_url = get_config("symbol_urls.public");
            let symbol_response = client(symbol_url, decoded.memoryMap);
            let _ = res.write_all(symbol_response.as_bytes());

            res.end().unwrap();
        },
        _ => { *res.status_mut() = StatusCode::MethodNotAllowed },
    }

    debug!("finished serving request");
}

/**
  * Creates multiple client connections and aggregates result.
  */
fn client(url: String, memory_map: Vec<(String,String)>) -> String {
    let mut handles = vec![];

    for (debug_file, debug_id) in memory_map {
        let pdb = debug_file.find(".pdb").unwrap();
        let (symbol_name, _) = debug_file.split_at(pdb);
        let symbol_file = format!("{}.sym", symbol_name);
        let this_url = format!("{}/{}/{}/{}", url, debug_file, debug_id, symbol_file);

        // TODO get from config
        let mut symbol_path = PathBuf::new();
        symbol_path.push("testdata/symbols");
        symbol_path.push(&debug_file);
        symbol_path.push(&debug_id);
        symbol_path.push(&symbol_file);

        // TODO most of the time is spent waiting on I/O, maybe async would be more appropriate?
        // TODO decide min/max possible threads, possibly based on number of cores?
        handles.push(thread::spawn(move || {
            let mut body = String::new();
            let c = Client::new();
            let mut res = c.get(&this_url).send().unwrap();
            let _ = res.read_to_string(&mut body);

            // TODO write symbol file to disk, using file locking

            // FIXME fake values, for testing
            let symbol = symbolize(&symbol_path.as_path(), 0x1010);

            (symbol_file, symbol)
        }));
    }

    let mut result = SymbolResponse {
        symbolicatedStacks: vec!(),
        knownModules: vec!(),
    };

    for handle in handles {
        // TODO stash this file on disk
        let (symbol_file, symbol) = handle.join().unwrap();

        result.symbolicatedStacks.push(
            vec!(format!("{} (in {}))", symbol_file, symbol.unwrap()))
        );
        result.knownModules.push(true);
    }

    json::encode(&result).unwrap()
}

/**
  * Returns individual values from the configuration file.
  */
fn get_config(value_name: &str) -> String {
    // TODO move to actual file, static str for the moment
    let toml: &'static str = r#"
        [symbol_urls]
        public = "https://s3-us-west-2.amazonaws.com/org.mozilla.crash-stats.symbols-public/v1"
    "#;
    // TODO support multiple URLs
    let value: toml::Value = toml.parse().unwrap();

    value.lookup(value_name).unwrap().as_str().unwrap().to_string()
}

/**
  * Symbolicates based on incoming address
  */
fn symbolize(symbol_path: &Path, address: u64) -> Option<String> {
    debug!("symbol_path: {:?}", &symbol_path);
    let sym = SymbolFile::from_file(&symbol_path).unwrap();

    Some(sym.functions.lookup(address).unwrap().name.clone())
}
<commit_msg>turn stacks into a hashmap<commit_after>// Copyright 2015 Robert Helmer <rhelmer@rhelmer.org>. See the LICENSE
// file at the top-level directory of this distribution.

/**
  * Symbolapi - a microservice which accepts lists of symbol+addresses, and returns
  * a list of symbolicated functions.
  *
  * The goal is to take an HTTP JSON request such as:
  * {"stacks":[
  *   [
  *     [0,11723767],
  *     [1, 65802]
  *    ]
  *  ],
  *  "memoryMap":[
  *    ["xul.pdb","44E4EC8C2F41492B9369D6B9A059577C2"],
  *    ["wntdll.pdb","D74F79EB1F8D4A45ABCD2F476CCABACC2"]
  *  ],
  *  "version":4
  * }
  *
  * This would download the corresponding symbol files (e.g. from S3), and return the
  * function names for the corresponding addresses like so:
  * {"symbolicatedStacks": [
  *   [
  *     "XREMain::XRE_mainRun() (in xul.pdb)",
  *     "KiUserCallbackDispatcher (in wntdll.pdb)"]
  *   ],
  *   "knownModules": [true, true]
  * }
  */

extern crate breakpad_symbols;
extern crate hyper;
#[macro_use]
extern crate log;
extern crate log4rs;
extern crate rustc_serialize;
extern crate toml;

use std::collections::HashMap;
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
use std::thread;

use breakpad_symbols::SymbolFile;
use hyper::Client;
use hyper::server::{Server, Request, Response};
use hyper::status::StatusCode;
use rustc_serialize::json;

/**
  * Incoming JSON request format.
  */
// required JSON keys are non-snakecase
#[allow(non_snake_case)]
#[derive(RustcDecodable)]
pub struct SymbolRequest {
    memoryMap: Vec<(String,String)>,
    // index, offset
    stacks: Vec<Vec<(i8,u64)>>,
    version: u8,
}

/**
  * Outgoing JSON response format.
  */
// required JSON keys are non-snakecase
#[allow(non_snake_case)]
#[derive(RustcEncodable)]
pub struct SymbolResponse {
    symbolicatedStacks: Vec<Vec<String>>,
    knownModules: Vec<bool>,
}

fn main() {
    log4rs::init_file("config/log.toml", Default::default()).unwrap();

    let address = "0.0.0.0:8080";

    info!("Listening on {}", address);
    Server::http(address).unwrap().handle(server).unwrap();
}

/**
  * Receives single HTTP requests and demuxes to symbols file fetches from S3 bucket.
  */
fn server(mut req: Request, mut res: Response) {
    info!("incoming connection from {}", req.remote_addr);

    match req.method {
        hyper::Post => {
            let mut res = res.start().unwrap();

            let mut buffer = String::new();
            let _ = req.read_to_string(&mut buffer);
            debug!("raw POST: {:?}", &buffer);

            let decoded: SymbolRequest = json::decode(&buffer).unwrap();

            debug!("decoded memoryMap: {:?}", decoded.memoryMap);
            debug!("decoded stacks: {:?}", decoded.stacks);
            debug!("decoded version: {}", decoded.version);

            let symbol_url = get_config("symbol_urls.public");

            // stacks come in as an array, turn into hashmap
            let mut stack_map: HashMap<i8, u64> = HashMap::new();
            for stack in &decoded.stacks {
                let (index, offset) = stack[0];
                stack_map.insert(index, offset);
            }

            let symbol_response = client(symbol_url, decoded.memoryMap, stack_map.clone());
            let _ = res.write_all(symbol_response.as_bytes());

            res.end().unwrap();
        },
        _ => { *res.status_mut() = StatusCode::MethodNotAllowed },
    }

    debug!("finished serving request");
}

/**
  * Creates multiple client connections and aggregates result.
  */
fn client(url: String, memory_map: Vec<(String,String)>, stack_map: HashMap<i8, u64>) -> String {
    let mut handles = vec![];
    let mut counter: i8 = 0;
    for (debug_file, debug_id) in memory_map {
        let pdb = debug_file.find(".pdb").unwrap();
        let (symbol_name, _) = debug_file.split_at(pdb);
        let symbol_file = format!("{}.sym", symbol_name);
        let this_url = format!("{}/{}/{}/{}", url, debug_file, debug_id, symbol_file);

        // TODO get from config
        let mut symbol_path = PathBuf::new();
        symbol_path.push("testdata/symbols");
        symbol_path.push(&debug_file);
        symbol_path.push(&debug_id);
        symbol_path.push(&symbol_file);

        let stack_map_copy = stack_map.clone();

        // TODO most of the time is spent waiting on I/O, maybe async would be more appropriate?
        // TODO decide min/max possible threads, possibly based on number of cores?
        handles.push(thread::spawn(move || {
            let mut body = String::new();
            let c = Client::new();
            let mut res = c.get(&this_url).send().unwrap();
            let _ = res.read_to_string(&mut body);

            // TODO write symbol file to disk, using file locking

            let mut symbols = vec!();
            for stack in stack_map_copy.get(&counter) {
                symbols.push(symbolize(&symbol_path.as_path(), stack.clone()));
            }

            (symbol_file, symbols)
        }));

        counter += 1;
    }

    let mut result = SymbolResponse {
        symbolicatedStacks: vec!(),
        knownModules: vec!(),
    };

    for handle in handles {
        // TODO stash this file on disk
        let (symbol_file, symbols) = handle.join().unwrap();

        for symbol in symbols {
            result.symbolicatedStacks.push(
                vec!(format!("{} (in {}))", symbol_file, symbol.unwrap()))
            );
        }
        result.knownModules.push(true);
    }

    json::encode(&result).unwrap()
}

/**
  * Returns individual values from the configuration file.
  */
fn get_config(value_name: &str) -> String {
    // TODO move to actual file, static str for the moment
    let toml: &'static str = r#"
        [symbol_urls]
        public = "https://s3-us-west-2.amazonaws.com/org.mozilla.crash-stats.symbols-public/v1"
    "#;
    // TODO support multiple URLs
    let value: toml::Value = toml.parse().unwrap();

    value.lookup(value_name).unwrap().as_str().unwrap().to_string()
}

/**
  * Symbolicates based on incoming address
  */
fn symbolize(symbol_path: &Path, address: u64) -> Option<String> {
    debug!("symbol_path: {:?}", &symbol_path);
    let sym = SymbolFile::from_file(&symbol_path).unwrap();

    Some(sym.functions.lookup(address).unwrap().name.clone())
}
<|endoftext|>


<commit_before>extern crate piston_window;

use piston_window::*;

fn main() {
    let window: PistonWindow = WindowSettings::new("Rustris", [640, 480])
                               .exit_on_esc(true).build().unwrap();
    for e in window {
        e.draw_2d(|c, g| {
            clear([1.0; 4], g);
            rectangle([1.0, 0.0, 0.0, 1.0], // red
                      [0.0, 0.0, 100.0, 100.0],
                      c.transform, g);
        });
    }
}
<commit_msg>add the cargo version number to the title string<commit_after>extern crate piston_window;

use piston_window::*;

const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const WINDOW_WIDTH: u32 = 640;
const WINDOW_HEIGHT: u32 = 480;

fn main() {
    let window_title = format!("Rustris {}", VERSION);
    let window: PistonWindow = WindowSettings::new(window_title, [WINDOW_WIDTH, WINDOW_HEIGHT])
        .exit_on_esc(true).build().unwrap();
    for e in window {
        e.draw_2d(|c, g| {
            clear([1.0; 4], g);
            rectangle([1.0, 0.0, 0.0, 1.0], // red
                      [0.0, 0.0, 100.0, 100.0],
                      c.transform, g);
        });
    }
}
<|endoftext|>


<commit_before>extern crate identicon;
extern crate image;
extern crate openssl;

use std::io;
use std::io::{Read, Write, Result};

use image::ColorType;
use image::png::PNGEncoder;
use openssl::hash::{Hasher, MessageDigest};

use identicon::Identicon;

fn main() {
    let bytes = hash().unwrap();
    generate(&bytes[..]).unwrap();
}

fn generate(input: &[u8]) -> Result<()> {
    let identicon = Identicon::new(input);
    let image = identicon.image();
    let (width, height) = image.dimensions();
    let output = &mut io::stdout();
    let encoder = PNGEncoder::new(output);
    encoder.encode(image.as_ref(), width, height, ColorType::RGB(8))
}

fn hash() -> Result<Vec<u8>> {
    let mut hash = Hasher::new(MessageDigest::md5())?;
    let input = io::stdin();
    let mut reader = input.lock();
    pipe(&mut reader, &mut hash)?;
    Ok(hash.finish()?)
}

fn pipe(input: &mut Read, output: &mut Write) -> Result<usize> {
    let mut total = 0;
    let mut buf = [0; 1024];
    loop {
        match input.read(&mut buf)? {
            0 => break,
            n => {
                output.write(&buf[0..n])?;
                total += n;
            }
        }
    }
    Ok(total)
}
<commit_msg>Remove result unwrapping<commit_after>extern crate identicon;
extern crate image;
extern crate openssl;

use std::io;
use std::io::{Read, Write, Result};
use std::process::exit;

use image::ColorType;
use image::png::PNGEncoder;
use openssl::hash::{Hasher, MessageDigest};

use identicon::Identicon;

fn main() {
    match hash().and_then(|bytes| generate(&bytes)) {
        Ok(_) => (),
        Err(e) => {
            println!("{}", e);
            exit(1);
        }
    }
}

fn generate(input: &[u8]) -> Result<()> {
    let identicon = Identicon::new(input);
    let image = identicon.image();
    let (width, height) = image.dimensions();
    let output = &mut io::stdout();
    let encoder = PNGEncoder::new(output);
    encoder.encode(image.as_ref(), width, height, ColorType::RGB(8))
}

fn hash() -> Result<Vec<u8>> {
    let mut hash = Hasher::new(MessageDigest::md5())?;
    let input = io::stdin();
    let mut reader = input.lock();
    pipe(&mut reader, &mut hash)?;
    Ok(hash.finish()?)
}

fn pipe(input: &mut Read, output: &mut Write) -> Result<usize> {
    let mut total = 0;
    let mut buf = [0; 1024];
    loop {
        match input.read(&mut buf)? {
            0 => break,
            n => {
                output.write(&buf[0..n])?;
                total += n;
            }
        }
    }
    Ok(total)
}
<|endoftext|>


<commit_before>#![deny(warnings)]

extern crate clap;
extern crate fern;
extern crate rustc_version;
extern crate tempdir;

#[macro_use]
extern crate log;

use std::fs::{self, File};
use std::io::{Read, Write};
use std::path::Path;
use std::process::{Command, Stdio};

use clap::{App, AppSettings, Arg, SubCommand};

// TODO proper error reporting
macro_rules! try {
    ($e:expr) => {
        $e.unwrap_or_else(|_| panic!(stringify!($e)))
    }
}

struct Context<'a> {
    commit_hash: &'a str,
    host: &'a str,
    out_dir: &'a Path,
    release: bool,
    target: &'a str,
}

fn main() {
    let rustc_version::VersionMeta { ref host, ref commit_hash, .. } =
        rustc_version::version_meta();
    let commit_hash = commit_hash.as_ref().unwrap();

    let ref matches = App::new("cargo-sysroot")
                          .bin_name("cargo")
                          .settings(&[AppSettings::SubcommandRequired])
                          .subcommand(SubCommand::with_name("sysroot")
                                          .about("Builds a sysroot with cross compiled standard \
                                                  crates")
                                          .arg(Arg::with_name("triple")
                                                   .help("Target triple to compile for")
                                                   .long("target")
                                                   .required(true)
                                                   .takes_value(true))
                                          .arg(Arg::with_name("release")
                                                   .help("Build artifacts in release mode, \
                                                          with optimizations")
                                                   .long("release"))
                                          .arg(Arg::with_name("out_dir")
                                                   .help("Output directory")
                                                   .required(true))
                                          .author("Jorge Aparicio <japaricious@gmail.com>"))
                          .version(env!("CARGO_PKG_VERSION"))
                          .get_matches();

    if let Some(matches) = matches.subcommand_matches("sysroot") {
        if let (Some(target), Some(out_dir)) = (matches.value_of("triple"),
                                                matches.value_of("out_dir")) {
            if host == target {
                panic!("`cargo sysroot` for host has not been implement yet");
            }

            let ref ctx = Context {
                commit_hash: commit_hash,
                host: host,
                out_dir: Path::new(out_dir),
                release: matches.is_present("release"),
                target: target,
            };

            init_logger();
            fetch_source(ctx);
            symlink_host_crates(ctx);
            build_target_crates(ctx);
        }
    }
}

fn init_logger() {
    let config = fern::DispatchConfig {
        format: Box::new(|msg, level, _| format!("{}: {}", level, msg)),
        output: vec![fern::OutputConfig::stderr()],
        level: log::LogLevelFilter::Trace,
    };

    try!(fern::init_global_logger(config, log::LogLevelFilter::Trace));
}

fn fetch_source(ctx: &Context) {
    let hash = ctx.commit_hash;
    let ref src_dir = ctx.out_dir.join("src");
    let ref hash_file = src_dir.join(".commit-hash");

    if hash_file.exists() {
        let ref mut old_hash = String::with_capacity(hash.len());
        try!(try!(File::open(hash_file)).read_to_string(old_hash));

        if old_hash == hash {
            info!("source up to date");
            return;
        }
    }

    if src_dir.exists() {
        info!("purging the src directory");
        try!(fs::remove_dir_all(src_dir));
    }

    try!(fs::create_dir_all(src_dir));

    info!("fetching source tarball");
    let ref curl = try!(Command::new("curl")
                            .arg("-L")
                            .arg(format!("https://github.com/rust-lang/rust/tarball/{}", hash))
                            .output());

    assert!(curl.status.success());

    info!("unpacking source tarball");
    let short_hash = &hash[..7];
    let ref mut tar = try!(Command::new("tar")
                               .args(&["-xz", "--strip-components", "2", "-C"])
                               .arg(src_dir)
                               .arg(format!("rust-lang-rust-{}/src", short_hash))
                               .stdin(Stdio::piped())
                               .spawn());
    try!(tar.stdin.as_mut().unwrap().write_all(&curl.stdout));

    info!("creating .commit-hash file");
    try!(try!(File::create(hash_file)).write_all(hash.as_bytes()));
}

fn symlink_host_crates(ctx: &Context) {
    info!("symlinking host crates");

    let sys_root = try!(String::from_utf8(try!(Command::new("rustc")
                                                   .args(&["--print", "sysroot"])
                                                   .output())
                                              .stdout));
    let ref src = Path::new(sys_root.trim_right()).join(format!("lib/rustlib/{}", ctx.host));
    let ref rustlib_dir = ctx.out_dir.join("lib/rustlib");
    let ref dst = rustlib_dir.join(ctx.host);

    try!(fs::create_dir_all(rustlib_dir));

    if dst.exists() {
        try!(fs::remove_file(dst));
    }

    match () {
        #[cfg(unix)]
        () => {
            use std::os::unix::fs;

            try!(fs::symlink(src, dst))
        }
        #[cfg(windows)]
        () => {
            use std::os::windaws::fs;

            try!(fs::symlink_dir(src, dst))
        }
    }
}

// XXX we only build the core crate for now
// TODO build the rest of crates
fn build_target_crates(ctx: &Context) {
    const CARGO_TOML: &'static str = r#"[package]
authors = ["The Rust Project Developers"]
name = "core"
version = "0.0.0"

[lib]
name = "core"
path = "lib.rs""#;

    let ref src_dir = ctx.out_dir.join("src/libcore");

    try!(try!(File::create(src_dir.join("Cargo.toml"))).write_all(CARGO_TOML.as_bytes()));

    let ref temp_dir = try!(tempdir::TempDir::new("core"));
    let temp_dir = temp_dir.path();

    info!("building the core crate");
    assert!(try!(Command::new("cargo")
                     .args(&["build", "--target"])
                     .arg(ctx.target)
                     .arg(if ctx.release {
                         "--release"
                     } else {
                         // XXX dummy value
                         "--lib"
                     })
                     .current_dir(src_dir)
                     .env("CARGO_TARGET_DIR", temp_dir)
                     .status())
                .success());

    info!("copy the core crate to the sysroot");
    let ref libdir = ctx.out_dir.join(format!("lib/rustlib/{}/lib", ctx.target));
    try!(fs::create_dir_all(libdir));

    let ref src = temp_dir.join(format!("{}/{}/libcore.rlib",
                                        ctx.target,
                                        if ctx.release {
                                            "release"
                                        } else {
                                            "debug"
                                        }));
    let ref dst = libdir.join("libcore.rlib");
    try!(fs::copy(src, dst));
}
<commit_msg>fix: copy target specification file to source directory<commit_after>#![deny(warnings)]

extern crate clap;
extern crate fern;
extern crate rustc_version;
extern crate tempdir;

#[macro_use]
extern crate log;

use std::fs::{self, File};
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};

use clap::{App, AppSettings, Arg, SubCommand};

// TODO proper error reporting
macro_rules! try {
    ($e:expr) => {
        $e.unwrap_or_else(|_| panic!(stringify!($e)))
    }
}

struct Context<'a> {
    commit_hash: &'a str,
    host: &'a str,
    out_dir: &'a Path,
    release: bool,
    target: &'a str,
}

fn main() {
    let rustc_version::VersionMeta { ref host, ref commit_hash, .. } =
        rustc_version::version_meta();
    let commit_hash = commit_hash.as_ref().unwrap();

    let ref matches = App::new("cargo-sysroot")
                          .bin_name("cargo")
                          .settings(&[AppSettings::SubcommandRequired])
                          .subcommand(SubCommand::with_name("sysroot")
                                          .about("Builds a sysroot with cross compiled standard \
                                                  crates")
                                          .arg(Arg::with_name("triple")
                                                   .help("Target triple to compile for")
                                                   .long("target")
                                                   .required(true)
                                                   .takes_value(true))
                                          .arg(Arg::with_name("release")
                                                   .help("Build artifacts in release mode, \
                                                          with optimizations")
                                                   .long("release"))
                                          .arg(Arg::with_name("out_dir")
                                                   .help("Output directory")
                                                   .required(true))
                                          .author("Jorge Aparicio <japaricious@gmail.com>"))
                          .version(env!("CARGO_PKG_VERSION"))
                          .get_matches();

    if let Some(matches) = matches.subcommand_matches("sysroot") {
        if let (Some(target), Some(out_dir)) = (matches.value_of("triple"),
                                                matches.value_of("out_dir")) {
            if host == target {
                panic!("`cargo sysroot` for host has not been implement yet");
            }

            let ref ctx = Context {
                commit_hash: commit_hash,
                host: host,
                out_dir: Path::new(out_dir),
                release: matches.is_present("release"),
                target: target,
            };

            init_logger();
            fetch_source(ctx);
            symlink_host_crates(ctx);
            build_target_crates(ctx);
        }
    }
}

fn init_logger() {
    let config = fern::DispatchConfig {
        format: Box::new(|msg, level, _| format!("{}: {}", level, msg)),
        output: vec![fern::OutputConfig::stderr()],
        level: log::LogLevelFilter::Trace,
    };

    try!(fern::init_global_logger(config, log::LogLevelFilter::Trace));
}

fn fetch_source(ctx: &Context) {
    let hash = ctx.commit_hash;
    let ref src_dir = ctx.out_dir.join("src");
    let ref hash_file = src_dir.join(".commit-hash");

    if hash_file.exists() {
        let ref mut old_hash = String::with_capacity(hash.len());
        try!(try!(File::open(hash_file)).read_to_string(old_hash));

        if old_hash == hash {
            info!("source up to date");
            return;
        }
    }

    if src_dir.exists() {
        info!("purging the src directory");
        try!(fs::remove_dir_all(src_dir));
    }

    try!(fs::create_dir_all(src_dir));

    // FIXME don't rely on `curl` and `tar`, instead implement this functionality in Rust.
    // Check https://github.com/Diggsey/multirust-rs for a reference implementation of such
    // functionality
    info!("fetching source tarball");
    let ref curl = try!(Command::new("curl")
                            .arg("-L")
                            .arg(format!("https://github.com/rust-lang/rust/tarball/{}", hash))
                            .output());

    assert!(curl.status.success());

    info!("unpacking source tarball");
    let short_hash = &hash[..7];
    let ref mut tar = try!(Command::new("tar")
                               .args(&["-xz", "--strip-components", "2", "-C"])
                               .arg(src_dir)
                               .arg(format!("rust-lang-rust-{}/src", short_hash))
                               .stdin(Stdio::piped())
                               .spawn());
    try!(tar.stdin.as_mut().unwrap().write_all(&curl.stdout));

    info!("creating .commit-hash file");
    try!(try!(File::create(hash_file)).write_all(hash.as_bytes()));
}

fn symlink_host_crates(ctx: &Context) {
    info!("symlinking host crates");

    let sys_root = try!(String::from_utf8(try!(Command::new("rustc")
                                                   .args(&["--print", "sysroot"])
                                                   .output())
                                              .stdout));
    let ref src = Path::new(sys_root.trim_right()).join(format!("lib/rustlib/{}", ctx.host));
    let ref rustlib_dir = ctx.out_dir.join("lib/rustlib");
    let ref dst = rustlib_dir.join(ctx.host);

    try!(fs::create_dir_all(rustlib_dir));

    if dst.exists() {
        try!(fs::remove_file(dst));
    }

    match () {
        #[cfg(unix)]
        () => {
            use std::os::unix::fs;

            try!(fs::symlink(src, dst))
        }
        #[cfg(windows)]
        () => {
            use std::os::windaws::fs;

            try!(fs::symlink_dir(src, dst))
        }
    }
}

// XXX we only build the core crate for now
// TODO build the rest of crates
fn build_target_crates(ctx: &Context) {
    const CARGO_TOML: &'static str = r#"[package]
authors = ["The Rust Project Developers"]
name = "core"
version = "0.0.0"

[lib]
name = "core"
path = "lib.rs""#;

    let ref src_dir = ctx.out_dir.join("src/libcore");

    try!(try!(File::create(src_dir.join("Cargo.toml"))).write_all(CARGO_TOML.as_bytes()));

    let ref temp_dir = try!(tempdir::TempDir::new("core"));
    let temp_dir = temp_dir.path();

    let ref target_json = PathBuf::from(format!("{}.json", ctx.target));
    let mut spec_file = None;

    if target_json.exists() {
        let dst = src_dir.join(target_json);

        info!("copy target specification file");
        try!(fs::copy(target_json, &dst));
        spec_file = Some(dst);
    }

    info!("building the core crate");
    assert!(try!(Command::new("cargo")
                     .args(&["build", "--target"])
                     .arg(ctx.target)
                     .arg(if ctx.release {
                         "--release"
                     } else {
                         // XXX dummy value
                         "--lib"
                     })
                     .current_dir(src_dir)
                     .env("CARGO_TARGET_DIR", temp_dir)
                     .status())
                .success());

    if let Some(spec_file) = spec_file {
        info!("delete target specification file");
        try!(fs::remove_file(spec_file));
    }

    info!("copy the core crate to the sysroot");
    let ref libdir = ctx.out_dir.join(format!("lib/rustlib/{}/lib", ctx.target));
    try!(fs::create_dir_all(libdir));

    let ref src = temp_dir.join(format!("{}/{}/libcore.rlib",
                                        ctx.target,
                                        if ctx.release {
                                            "release"
                                        } else {
                                            "debug"
                                        }));
    let ref dst = libdir.join("libcore.rlib");
    try!(fs::copy(src, dst));
}
<|endoftext|>


<commit_before>use rustc_tools_util::*;

const CARGO_CLIPPY_HELP: &str = r#"Checks a package to catch common mistakes and improve your Rust code.

Usage:
    cargo clippy [options] [--] [<opts>...]

Common options:
    -h, --help               Print this message
    -V, --version            Print version info and exit

Other options are the same as `cargo check`.

To allow or deny a lint from the command line you can use `cargo clippy --`
with:

    -W --warn OPT       Set lint warnings
    -A --allow OPT      Set lint allowed
    -D --deny OPT       Set lint denied
    -F --forbid OPT     Set lint forbidden

You can use tool lints to allow or deny lints from your code, eg.:

    #[allow(clippy::needless_lifetimes)]
"#;

fn show_help() {
    println!("{}", CARGO_CLIPPY_HELP);
}

fn show_version() {
    let version_info = rustc_tools_util::get_version_info!();
    println!("{}", version_info);
}

pub fn main() {
    // Check for version and help flags even when invoked as 'cargo-clippy'
    if std::env::args().any(|a| a == "--help" || a == "-h") {
        show_help();
        return;
    }

    if std::env::args().any(|a| a == "--version" || a == "-V") {
        show_version();
        return;
    }

    if let Err(code) = process(std::env::args().skip(2)) {
        std::process::exit(code);
    }
}

fn process<I>(mut old_args: I) -> Result<(), i32>
where
    I: Iterator<Item = String>,
{
    let mut args = vec!["check".to_owned()];

    for arg in old_args.by_ref() {
        if arg == "--" {
            break;
        }
        args.push(arg);
    }

    let clippy_args: String = old_args.map(|arg| format!("{}__CLIPPY_HACKERY__", arg)).collect();

    let mut path = std::env::current_exe()
        .expect("current executable path invalid")
        .with_file_name("clippy-driver");
    if cfg!(windows) {
        path.set_extension("exe");
    }

    let target_dir = std::env::var_os("CLIPPY_DOGFOOD")
        .map(|_| {
            std::env::var_os("CARGO_MANIFEST_DIR").map_or_else(
                || std::ffi::OsString::from("clippy_dogfood"),
                |d| {
                    std::path::PathBuf::from(d)
                        .join("target")
                        .join("dogfood")
                        .into_os_string()
                },
            )
        })
        .map(|p| ("CARGO_TARGET_DIR", p));

    // Run the dogfood tests directly on nightly cargo. This is required due
    // to a bug in rustup.rs when running cargo on custom toolchains. See issue #3118.
    if std::env::var_os("CLIPPY_DOGFOOD").is_some() && cfg!(windows) {
        args.insert(0, "+nightly".to_string());
    }

    let exit_status = std::process::Command::new("cargo")
        .args(&args)
        .env("RUSTC_WRAPPER", path)
        .env("CLIPPY_ARGS", clippy_args)
        .envs(target_dir)
        .spawn()
        .expect("could not run cargo")
        .wait()
        .expect("failed to wait for cargo?");

    if exit_status.success() {
        Ok(())
    } else {
        Err(exit_status.code().unwrap_or(-1))
    }
}
<commit_msg>Don't run dogfood on windows<commit_after>use rustc_tools_util::*;

const CARGO_CLIPPY_HELP: &str = r#"Checks a package to catch common mistakes and improve your Rust code.

Usage:
    cargo clippy [options] [--] [<opts>...]

Common options:
    -h, --help               Print this message
    -V, --version            Print version info and exit

Other options are the same as `cargo check`.

To allow or deny a lint from the command line you can use `cargo clippy --`
with:

    -W --warn OPT       Set lint warnings
    -A --allow OPT      Set lint allowed
    -D --deny OPT       Set lint denied
    -F --forbid OPT     Set lint forbidden

You can use tool lints to allow or deny lints from your code, eg.:

    #[allow(clippy::needless_lifetimes)]
"#;

fn show_help() {
    println!("{}", CARGO_CLIPPY_HELP);
}

fn show_version() {
    let version_info = rustc_tools_util::get_version_info!();
    println!("{}", version_info);
}

pub fn main() {
    // Check for version and help flags even when invoked as 'cargo-clippy'
    if std::env::args().any(|a| a == "--help" || a == "-h") {
        show_help();
        return;
    }

    if std::env::args().any(|a| a == "--version" || a == "-V") {
        show_version();
        return;
    }

    if let Err(code) = process(std::env::args().skip(2)) {
        std::process::exit(code);
    }
}

fn process<I>(mut old_args: I) -> Result<(), i32>
where
    I: Iterator<Item = String>,
{
    let mut args = vec!["check".to_owned()];

    for arg in old_args.by_ref() {
        if arg == "--" {
            break;
        }
        args.push(arg);
    }

    let clippy_args: String = old_args.map(|arg| format!("{}__CLIPPY_HACKERY__", arg)).collect();

    let mut path = std::env::current_exe()
        .expect("current executable path invalid")
        .with_file_name("clippy-driver");
    if cfg!(windows) {
        path.set_extension("exe");
    }

    let target_dir = std::env::var_os("CLIPPY_DOGFOOD")
        .map(|_| {
            std::env::var_os("CARGO_MANIFEST_DIR").map_or_else(
                || std::ffi::OsString::from("clippy_dogfood"),
                |d| {
                    std::path::PathBuf::from(d)
                        .join("target")
                        .join("dogfood")
                        .into_os_string()
                },
            )
        })
        .map(|p| ("CARGO_TARGET_DIR", p));

    // Don't run the dogfood tests on beta
    if std::env::var_os("CLIPPY_DOGFOOD").is_some() && cfg!(windows) {
        return Ok(())
    }

    let exit_status = std::process::Command::new("cargo")
        .args(&args)
        .env("RUSTC_WRAPPER", path)
        .env("CLIPPY_ARGS", clippy_args)
        .envs(target_dir)
        .spawn()
        .expect("could not run cargo")
        .wait()
        .expect("failed to wait for cargo?");

    if exit_status.success() {
        Ok(())
    } else {
        Err(exit_status.code().unwrap_or(-1))
    }
}
<|endoftext|>


<commit_before>extern crate to;
#[macro_use]
extern crate error_chain;
#[macro_use]
extern crate prettytable;
#[macro_use]
extern crate slog;

use std::path::PathBuf;
use prettytable::Table;
use std::env;
use to::{cli, dir, logger};
use to::cli::Action;
use to::database::Database;
use to::errors::*;

fn main() {
    let matches = cli::app().get_matches();
    let (log, options) = setup(matches);

    // change the error output and logging based on the flags.
    if let Err(ref e) = run(&log, options) {
        use std::io::Write;
        let stderr = &mut ::std::io::stderr();
        let stderr_errmsg = "Error writing to stderr";

        writeln!(stderr, "error: {}", e).expect(stderr_errmsg);

        for e in e.iter().skip(1) {
            writeln!(stderr, "caused by: {}", e).expect(stderr_errmsg);
        }

        // The backtrace is not always generated. Try to run this example
        // with `RUST_BACKTRACE=1`.
        if let Some(backtrace) = e.backtrace() {
            writeln!(stderr, "backtrace: {:?}", backtrace).expect(stderr_errmsg);
        }

        ::std::process::exit(1);
    }
}

/// Reduces boilerplate, returns a working logger and parsed CLI options.
fn setup(matches: cli::ArgMatches) -> (logger::Logger, cli::Options) {
    let options = cli::Options::new(matches);
    let log = logger::root(&options);

    (log, options)
}

fn run(log: &slog::Logger, options: cli::Options) -> Result<()> {
    // --init # echo the shell script for the `to` function.
    if options.initialize {
        print!("{}", include_str!("to.sh"));
        return Ok(());
    }

    let config = match options.config {
        // NOTE(jxson): I am sure there is a better way to do this but I am a n00b.
        Some(ref value) => PathBuf::from(value),
        None => bail!(ErrorKind::ConfigError),
    };

    if !config.exists() {
        println!("does not exist {:?}", config);
        try!(dir::mkdirp(&config));
    }

    let store = try!(Database::open(config));
    info!(log, "database opened: {:?}", store.location);

    match options.action {
        Action::Info => info(&store, options),
        Action::Save => save(store, options),
        Action::Delete => delete(store, options),
        Action::List => list(&store),
        Action::Pathname => pathname(&store, options),
    }
}

fn info(store: &Database, options: cli::Options) -> Result<()> {
    let name = match options.name {
        Some(value) => value,
        None => bail!(ErrorKind::InfoFlagRequiresName),
    };

    let bookmark = try!(store.get(name));
    println!("bookmark: {:?}", bookmark);
    Ok(())
}

fn save(mut store: Database, options: cli::Options) -> Result<()> {
    let path = match options.path {
        Some(value) => try!(dir::resolve(value)),
        None => try!(env::current_dir()),
    };

    let basename = try!(dir::basename(&path));
    let name = match options.name {
        Some(value) => value,
        None => basename,
    };

    try!(store.put(name, path));

    Ok(())
}

fn delete(mut store: Database, options: cli::Options) -> Result<()> {
    let name = match options.name {
        Some(value) => value,
        None => bail!(ErrorKind::DeleteFlagRequiresName),
    };

    try!(store.delete(name));

    Ok(())
}

fn list(store: &Database) -> Result<()> {
    let mut table = Table::new();
    table.add_row(row![ b => "Name", "Path", "Count"]);

    for (name, bookmark) in store.list() {
        let path = bookmark.directory.to_string_lossy();
        table.add_row(row![name, path, bookmark.count]);
    }

    table.printstd();

    Ok(())
}

fn pathname(store: &Database, options: cli::Options) -> Result<()> {
    let name = match options.name {
        Some(value) => value,
        None => bail!(ErrorKind::ToRequiresName),
    };

    let bookmark = try!(store.get(name));
    let value = bookmark.directory.to_string_lossy();
    println!("{}", value);
    Ok(())
}

#[cfg(test)]
mod test {
    extern crate tempdir;

    use super::*;
    use self::tempdir::TempDir;

    #[test]
    fn setup_is_ok() {
        let tmp = TempDir::new("blah").unwrap();
        let config = tmp.path().to_str().unwrap();
        let matches = cli::app().get_matches_from(vec!["to", "--config", config]);
        let (_, options) = setup(matches);

        assert_eq!(options.verbose, false);
        assert_eq!(options.initialize, false);
        assert_eq!(options.name, None);
        assert_eq!(options.action, Action::Pathname);
    }

    #[test]
    fn run_with_init_flag() {
        let matches = cli::app().get_matches_from(vec!["to", "--init"]);
        let (log, options) = setup(matches);
        let result = run(&log, options);
        assert!(result.is_ok());
    }

    #[test]
    fn run_with_bad_config() {
        let matches = cli::app().get_matches_from(vec!["to"]);
        let (log, mut options) = setup(matches);
        options.config = None;
        let err = run(&log, options).err().unwrap();
        assert_eq!(format!("{}", err), format!("{}", ErrorKind::ConfigError {}));
    }
}
<commit_msg>Adds test to check for config creation<commit_after>extern crate to;
#[macro_use]
extern crate error_chain;
#[macro_use]
extern crate prettytable;
#[macro_use]
extern crate slog;

use std::path::PathBuf;
use prettytable::Table;
use std::env;
use to::{cli, dir, logger};
use to::cli::Action;
use to::database::Database;
use to::errors::*;

fn main() {
    let matches = cli::app().get_matches();
    let (log, options) = setup(matches);

    // change the error output and logging based on the flags.
    if let Err(ref e) = run(&log, options) {
        use std::io::Write;
        let stderr = &mut ::std::io::stderr();
        let stderr_errmsg = "Error writing to stderr";

        writeln!(stderr, "error: {}", e).expect(stderr_errmsg);

        for e in e.iter().skip(1) {
            writeln!(stderr, "caused by: {}", e).expect(stderr_errmsg);
        }

        // The backtrace is not always generated. Try to run this example
        // with `RUST_BACKTRACE=1`.
        if let Some(backtrace) = e.backtrace() {
            writeln!(stderr, "backtrace: {:?}", backtrace).expect(stderr_errmsg);
        }

        ::std::process::exit(1);
    }
}

/// Reduces boilerplate, returns a working logger and parsed CLI options.
fn setup(matches: cli::ArgMatches) -> (logger::Logger, cli::Options) {
    let options = cli::Options::new(matches);
    let log = logger::root(&options);

    (log, options)
}

fn run(log: &slog::Logger, options: cli::Options) -> Result<()> {
    // --init # echo the shell script for the `to` function.
    if options.initialize {
        print!("{}", include_str!("to.sh"));
        return Ok(());
    }

    let config = match options.config {
        // NOTE(jxson): I am sure there is a better way to do this but I am a n00b.
        Some(ref value) => PathBuf::from(value),
        None => bail!(ErrorKind::ConfigError),
    };

    if !config.exists() {
        try!(dir::mkdirp(&config));
    }

    let store = try!(Database::open(config));
    info!(log, "database opened: {:?}", store.location);

    match options.action {
        Action::Info => info(&store, options),
        Action::Save => save(store, options),
        Action::Delete => delete(store, options),
        Action::List => list(&store),
        Action::Pathname => pathname(&store, options),
    }
}

fn info(store: &Database, options: cli::Options) -> Result<()> {
    let name = match options.name {
        Some(value) => value,
        None => bail!(ErrorKind::InfoFlagRequiresName),
    };

    let bookmark = try!(store.get(name));
    println!("bookmark: {:?}", bookmark);
    Ok(())
}

fn save(mut store: Database, options: cli::Options) -> Result<()> {
    let path = match options.path {
        Some(value) => try!(dir::resolve(value)),
        None => try!(env::current_dir()),
    };

    let basename = try!(dir::basename(&path));
    let name = match options.name {
        Some(value) => value,
        None => basename,
    };

    try!(store.put(name, path));

    Ok(())
}

fn delete(mut store: Database, options: cli::Options) -> Result<()> {
    let name = match options.name {
        Some(value) => value,
        None => bail!(ErrorKind::DeleteFlagRequiresName),
    };

    try!(store.delete(name));

    Ok(())
}

fn list(store: &Database) -> Result<()> {
    let mut table = Table::new();
    table.add_row(row![ b => "Name", "Path", "Count"]);

    for (name, bookmark) in store.list() {
        let path = bookmark.directory.to_string_lossy();
        table.add_row(row![name, path, bookmark.count]);
    }

    table.printstd();

    Ok(())
}

fn pathname(store: &Database, options: cli::Options) -> Result<()> {
    let name = match options.name {
        Some(value) => value,
        None => bail!(ErrorKind::ToRequiresName),
    };

    let bookmark = try!(store.get(name));
    let value = bookmark.directory.to_string_lossy();
    println!("{}", value);
    Ok(())
}

#[cfg(test)]
mod test {
    extern crate tempdir;

    use super::*;
    use self::tempdir::TempDir;

    #[test]
    fn setup_is_ok() {
        let tmp = TempDir::new("blah").unwrap();
        let config = tmp.path().to_str().unwrap();
        let matches = cli::app().get_matches_from(vec!["to", "--config", config]);
        let (_, options) = setup(matches);

        assert_eq!(options.verbose, false);
        assert_eq!(options.initialize, false);
        assert_eq!(options.name, None);
        assert_eq!(options.action, Action::Pathname);
    }

    #[test]
    fn run_with_init_flag() {
        let matches = cli::app().get_matches_from(vec!["to", "--init"]);
        let (log, options) = setup(matches);
        let result = run(&log, options);
        assert!(result.is_ok());
    }

    #[test]
    fn run_with_bad_config() {
        let matches = cli::app().get_matches_from(vec!["to"]);
        let (log, mut options) = setup(matches);
        options.config = None;
        let err = run(&log, options).err().unwrap();
        assert_eq!(format!("{}", err), format!("{}", ErrorKind::ConfigError {}));
    }

    #[test]
    fn run_with_new_config() {
        let matches = cli::app().get_matches_from(vec!["to", "-s"]);
        let (log, mut options) = setup(matches);
        let config = TempDir::new("existing-dir")
            .map(|dir| dir.into_path().join("non-existing"))
            .unwrap();

        assert!(!config.exists());
        options.config = Some(PathBuf::from(&config));
        run(&log, options).unwrap();
        assert!(config.exists());
    }
}
<|endoftext|>


<commit_before>mod cpu;
mod utils;
mod memory;
mod video;
extern crate sdl2;
use sdl2::keyboard::Keycode;

const SCREEN_WIDTH: u32 = 320;
const SCREEN_HEIGHT: u32 = 200;

fn main()
{
    let sdl_context = sdl2::init().unwrap();
    let video_subsystem = sdl_context.video().unwrap();

    let window = video_subsystem.window("Rust64", SCREEN_WIDTH, SCREEN_HEIGHT)
        .resizable()
        .opengl()
        .build()
        .unwrap();

    let mut running = true;
    let mut renderer = window.renderer().build().unwrap();
    let mut event_pump = sdl_context.event_pump().unwrap();

    let mut cpu = cpu::CPU::new(&renderer);
    cpu.reset();

    while running
    {
        renderer.clear();
        //renderer.copy(&texture, None, Some(Rect::new_unwrap(0, 0, 256, 64)));
        //renderer.copy_ex(&texture, None, Some(Rect::new_unwrap(450, 100, 256, 256)), 30.0, None, (false, false));
        //renderer.present();

        for event in event_pump.poll_iter()
        {
            use sdl2::event::Event;

            match event
            {
                Event::KeyDown { keycode: Some(Keycode::A), .. } => {
                    let _ = (); //texture.update(None, &buff2, 768);
                },
                Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
                        running = false;
                },
                Event::Quit { .. } => {
                    running = false;
                },
                _ => ()
            }
        }

        cpu.update();
        cpu.render(&mut renderer);
        renderer.present();
    }
}
<commit_msg>Artificially "sync" frames so render is not performed continously for massive boot speed improvement. Eventually this has to be timed by VIC.<commit_after>mod cpu;
mod utils;
mod memory;
mod video;
extern crate sdl2;
use sdl2::keyboard::Keycode;
use std::num::Wrapping;

const SCREEN_WIDTH: u32 = 320;
const SCREEN_HEIGHT: u32 = 200;

fn main()
{
    let sdl_context = sdl2::init().unwrap();
    let video_subsystem = sdl_context.video().unwrap();

    let window = video_subsystem.window("Rust64", SCREEN_WIDTH, SCREEN_HEIGHT)
        .build()
        .unwrap();

    let mut running = true;
    let mut renderer = window.renderer().accelerated().present_vsync().build().unwrap();
    let mut event_pump = sdl_context.event_pump().unwrap();

    let mut cpu = cpu::CPU::new(&renderer);
    cpu.reset();

    let mut render_cnt: u16 = 0;

    while running
    {
        //renderer.copy(&texture, None, Some(Rect::new_unwrap(0, 0, 256, 64)));
        //renderer.copy_ex(&texture, None, Some(Rect::new_unwrap(450, 100, 256, 256)), 30.0, None, (false, false));
        //renderer.present();

        for event in event_pump.poll_iter()
        {
            use sdl2::event::Event;

            match event
            {
                Event::KeyDown { keycode: Some(Keycode::A), .. } => {
                    let _ = (); //texture.update(None, &buff2, 768);
                },
                Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
                        running = false;
                },
                Event::Quit { .. } => {
                    running = false;
                },
                _ => ()
            }
        }

        cpu.update();
        //cpu.render(&mut renderer);
        render_cnt = (Wrapping(render_cnt) + Wrapping(1)).0;

        if render_cnt == 0
        { renderer.clear(); cpu.render(&mut renderer); renderer.present(); }
    }
}
<|endoftext|>


<commit_before>fn main() {
    println!("Hello, world!")
}
<commit_msg>Add docopt boilerplate.<commit_after>#![feature(phase)]

extern crate docopt;
extern crate serialize;
#[phase(plugin)] extern crate docopt_macros;

docopt!(Args deriving Show, "
ury-faked, a dummy BAPS3 service daemon

Usage:
    ury-faked PROFILE [-H HOST] [-p PORT]
    ury-faked --help

Options:
    -H, --host=HOST  The listening host.  [default: 127.0.0.1]
    -p, --port=PORT  The listening port.  [default: 1350]
    -h, --help       Show this message.
", flag_port: u16)

fn main() {
    let args: Args = Args::docopt().decode()
                                   .unwrap_or_else(|e| e.exit());
    println!("{}", args);
}
<|endoftext|>


<commit_before>extern crate rustc_serialize;
extern crate docopt;
extern crate num;
extern crate flate2;
extern crate byteorder;
extern crate bincode;
extern crate rust_htslib as htslib;

mod tallyrun;
mod tallyread;
mod seqtable;
mod fasta;
mod filter;
mod counts;
mod bigwig;
mod scale;

use docopt::Docopt;
use std::process::exit;
use std::error::Error;
use std::path::Path;
use std::fs;
use std::fs::File;
use std::io::ErrorKind;
use seqtable::SeqTable;

/* Main usage/arguments */

const USAGE: &'static str = "
Cut-site frequencies

Usage:
  enzcut tallymer <fasta-file> <read-size> [--parts=<n>]
  enzcut seqtable <fasta-file> [options]
  enzcut dump <seqtbl-file> [<seqrange>]
  enzcut table <seqtbl-file> [<bam-file>...] [--qual=<q>] [--regions=<bedfile>] [--pdist=<min:max>] [--only-paired]
  enzcut scale <seqtbl-file> <bam-file>... [options]
  enzcut <fasta-file> <bam-file>... [options]
  enzcut (-h | --help)
  enzcut --version

Options:
  -h --help             Show this screen.
  --version             Show version.
  --cut-size=<n>        Cut-site size [default: 4].
  --tallymer=<file>     Unmappable positions file produced by tallymer (seq, pos).
  --plus-offset=<p>     Cut-site offset on plus strand [default: 2]. Eg, p=2 AA[A]A.
  --minus-offset=<m>    Cut-site offset on minus strand [default: 2]. Eg, m=2 A[A]AA.
  --read-size=<r>       Read length [default: 36].
  --parts=<n>           Split suffix tree generation into n parts [default: 4].
  --qual=<q>            Minimum read quality [default: 0].
  --regions=<bedfile>   Count only cut-sites inside the regions indicated in the BED file.
  --out=<outfile>       Output seqtable filename (defaults to fasta file basename with .tbl extension).
  --bed=<bedfile>       Output scaled BED filename (defaults to BAM file basename with '_scaled.bed' extension).
  --skip-bed            Skip creating the BED file output.
  --bw=<bigwigfile>     Output scaled BigWig filename (defaults to BAM file basename with .bw extension).
  --skip-bw             Skip creating the BigWig file output.
  --stranded            Output per strand counts when writting scaled values.
  --shift-counts        Shift minus strand counts.
  --no-scale            Skip actual scalling in 'scale' command.
  --pdist=<min:max>     Distance range for included paired reads.
  --only-paired         Only accept aligned reads that have a mapped pair.
";

#[derive(Debug, RustcDecodable)]
struct Args {
    arg_fasta_file: String,
    arg_read_size: u16,
    arg_seqtbl_file: String,
    arg_seqrange: Option<String>,
    arg_bam_file: Option<Vec<String>>,
    flag_cut_size: u8,
    flag_tallymer: Option<String>,
    flag_plus_offset: u8,
    flag_minus_offset: u8,
    flag_version: bool,
    flag_read_size: u16,
    flag_parts: u8,
    flag_qual: u8,
    flag_regions: Option<String>,
    flag_out: Option<String>,
    flag_stranded: bool,
    flag_bed: Option<String>,
    flag_skip_bed: bool,
    flag_bw: Option<String>,
    flag_skip_bw: bool,
    flag_shift_counts: bool,
    flag_no_scale: bool,
    flag_pdist: Option<String>,
    flag_only_paired: bool,
    cmd_tallymer: bool,
    cmd_seqtable: bool,
    cmd_dump: bool,
    cmd_table: bool,
    cmd_scale: bool,
}

fn parse_range(range: &str) -> (i32, i32) {
    let parts: Vec<&str> = range.split(':').collect();

    if parts.len() != 2 {
        println!("Invalid distance range: {}", range);
        exit(1);
    }
    let min_val = parts[0].parse::<i32>();
    let max_val = parts[1].parse::<i32>();

    if min_val.is_err() || max_val.is_err() {
        println!("Invalid distance range: {}", range);
        exit(1);
    }
    (min_val.unwrap(),max_val.unwrap())
}

fn stem_filename(stem_src: &str, suffix: &str, out_arg: Option<String>) -> String {
    match out_arg {
        Some(out) => out,
        None => {
            let mut basename = Path::new(stem_src).file_stem().unwrap().to_os_string();
            basename.push(suffix);
            basename.into_string().unwrap()
        }
    }
}

fn file_exists(filename: &str) -> bool {
	match fs::metadata(filename) {
		Ok(meta) => meta.is_file(),
		Err(err) => match err.kind() {
			ErrorKind::NotFound => false,
			_ => { println!("Failed to open file {}: {:}", filename, err); exit(-1) },
		},
	}
}

fn main() {
    // Parse command line arguments
    let args: Args = Docopt::new(USAGE)
                            .and_then(|d| d.decode())
                            .unwrap_or_else(|e| e.exit());

    if args.flag_version {
        println!("Cut-site frequencies, v{}.{}.{}",
            env!( "CARGO_PKG_VERSION_MAJOR" ),
            env!( "CARGO_PKG_VERSION_MINOR" ),
            env!( "CARGO_PKG_VERSION_PATCH" ) );
        return;
    }

    let dist_range = match args.flag_pdist {
        Some(range) => Some(parse_range(&range)),
        None => None
    };

    // Check for data output commands

    if args.cmd_dump {
        if let Some(seqrange) = args.arg_seqrange {
            seqtable::dump_seqtable_range(&args.arg_seqtbl_file, &seqrange);
        } else {
            seqtable::dump_seqtable(&args.arg_seqtbl_file);
        }

        return;
    }

    if args.cmd_table {
        let has_bam = args.arg_bam_file.is_some();
        let counts = counts::tabulate(&args.arg_seqtbl_file, args.arg_bam_file.as_ref(), args.flag_qual, args.flag_regions, dist_range, args.flag_only_paired);
        counts::print_counts(&counts, has_bam);
        return;
    }

    // Check for main sequence commands
    let mut run_tallymer = true;
    let mut run_seqtable = true;
    let mut run_scale = true;

    if args.cmd_tallymer {
        // just phase 1
        run_seqtable = false;
        run_scale = false;
    } else if args.cmd_seqtable {
        // phase 1 & 2
        run_scale = false;
    } else if args.cmd_scale {
        // just phase 3
        run_tallymer = false;
        run_seqtable = false;
    } else {
        // all three phases

        // catch cmd names being interpreted as fasta_file names
        if args.arg_fasta_file.eq("dump") || args.arg_fasta_file.eq("table") || args.arg_fasta_file.eq("tallymer") || args.arg_fasta_file.eq("seqtable") || args.arg_fasta_file.eq("scale") {
            println!("Invalid arguments to {} command.", args.arg_fasta_file);
            println!("{}", USAGE);
            exit(1);
        }
    }

    if args.flag_skip_bed && args.flag_skip_bw {
        println!("--skip-bed and --skip-bw cannot be used together");
        exit(1);
    }

    // Process main sequence phases

    // phase 1 - tallymer
    let tally_path = if run_tallymer {
        let path = tallyrun::tallymer_createfile(&args.arg_fasta_file, args.flag_read_size, args.flag_parts);
        println!("# tallymer produced/found {:}", path.to_str().unwrap());
        Some(path)
    } else { None };

    // phase 2 - seqtable
    let seqtable_file = if run_seqtable {
        let suffix = format!("_{}.{}.{}.{}.tbl", args.flag_read_size, args.flag_cut_size, args.flag_plus_offset, args.flag_minus_offset);
        let outfile = stem_filename(&args.arg_fasta_file, &suffix, args.flag_out);

        let seq_params = seqtable::SeqTableParams {
            cut_length: args.flag_cut_size,
            plus_offset: args.flag_plus_offset,
            minus_offset: args.flag_minus_offset,
            read_length: args.flag_read_size };

        if file_exists(&outfile) {
            let file = File::open(&outfile).ok().expect("read file");
            let table = match SeqTable::open(file) {
                Ok(value) => value,
                Err(e) => {
                    println!("Error: seqtable: {}", e.to_string());
                    exit(1);
                },
            };
            if table.equivalent(&outfile, &seq_params) {
                println!("# seqtable reusing existing {}", &outfile);
                outfile
            } else {
                println!("Error: seqtable: output file {} already exists but does not match requested parameters!", outfile);
                exit(1);
            }
        } else {
            fasta::process_fasta(&args.arg_fasta_file, &tally_path.unwrap(), seq_params, &outfile);
            println!("# seqtable produced {}", &outfile);
            outfile
        }
    } else {
        args.arg_seqtbl_file
    };

    // phase 3 - tabulate & scale
    if run_scale {
        for filename in args.arg_bam_file.as_ref().unwrap() {
            if !file_exists(filename) {
                println!("Error: BAM file {} does not exist!", filename);
                exit(1);
            }
        }

        let bamfile = args.arg_bam_file.as_ref().unwrap()[0].clone(); // use the first name for reference
        let counts = counts::tabulate(&seqtable_file, args.arg_bam_file.as_ref(), args.flag_qual, args.flag_regions, dist_range, args.flag_only_paired);
        let pileup = scale::scale(&seqtable_file, counts, args.arg_bam_file.as_ref().unwrap(), args.flag_qual, args.flag_shift_counts, args.flag_no_scale, &dist_range, args.flag_only_paired);

        if !args.flag_skip_bed {
            let outfile_bed = if args.flag_no_scale {
                    stem_filename(&bamfile, "_not_scaled.bed", args.flag_bed)
                } else {
                    stem_filename(&bamfile, "_scaled.bed", args.flag_bed)
                };

            if file_exists(&outfile_bed) {
                println!("Error: output BED file {} already exists!", outfile_bed);
                exit(1);
            }

            match pileup.write_bed(&outfile_bed, args.flag_stranded) {
                Ok(_) => println!("# scale produced {}", &outfile_bed),
                Err(err) => println!("Error producing BED file: {}", err.description()),
            }
        } else {
            println!("# scale skipping BED output");
        }

        if !args.flag_skip_bw {
            let outfile_bw = stem_filename(&bamfile, ".bw", args.flag_bw);

            match pileup.write_bw(&outfile_bw, args.flag_stranded) {
                Ok((f1, f2)) => {
                    if f2.is_some() {
                        println!("# scale produced {}", f1);
                        println!("# scale produced {}", f2.unwrap());
                    } else {
                        println!("# scale produced {}", f1);
                    }
                },
                Err(err) => println!("Error producing BigWig file: {}", err.description()),
            }
        } else {
            println!("# scale skipping BigWig output");
        }
    }
}
<commit_msg>Check if FASTA file exists.<commit_after>extern crate rustc_serialize;
extern crate docopt;
extern crate num;
extern crate flate2;
extern crate byteorder;
extern crate bincode;
extern crate rust_htslib as htslib;

mod tallyrun;
mod tallyread;
mod seqtable;
mod fasta;
mod filter;
mod counts;
mod bigwig;
mod scale;

use docopt::Docopt;
use std::process::exit;
use std::error::Error;
use std::path::Path;
use std::fs;
use std::fs::File;
use std::io::ErrorKind;
use seqtable::SeqTable;

/* Main usage/arguments */

const USAGE: &'static str = "
Cut-site frequencies

Usage:
  enzcut tallymer <fasta-file> <read-size> [--parts=<n>]
  enzcut seqtable <fasta-file> [options]
  enzcut dump <seqtbl-file> [<seqrange>]
  enzcut table <seqtbl-file> [<bam-file>...] [--qual=<q>] [--regions=<bedfile>] [--pdist=<min:max>] [--only-paired]
  enzcut scale <seqtbl-file> <bam-file>... [options]
  enzcut <fasta-file> <bam-file>... [options]
  enzcut (-h | --help)
  enzcut --version

Options:
  -h --help             Show this screen.
  --version             Show version.
  --cut-size=<n>        Cut-site size [default: 4].
  --tallymer=<file>     Unmappable positions file produced by tallymer (seq, pos).
  --plus-offset=<p>     Cut-site offset on plus strand [default: 2]. Eg, p=2 AA[A]A.
  --minus-offset=<m>    Cut-site offset on minus strand [default: 2]. Eg, m=2 A[A]AA.
  --read-size=<r>       Read length [default: 36].
  --parts=<n>           Split suffix tree generation into n parts [default: 4].
  --qual=<q>            Minimum read quality [default: 0].
  --regions=<bedfile>   Count only cut-sites inside the regions indicated in the BED file.
  --out=<outfile>       Output seqtable filename (defaults to fasta file basename with .tbl extension).
  --bed=<bedfile>       Output scaled BED filename (defaults to BAM file basename with '_scaled.bed' extension).
  --skip-bed            Skip creating the BED file output.
  --bw=<bigwigfile>     Output scaled BigWig filename (defaults to BAM file basename with .bw extension).
  --skip-bw             Skip creating the BigWig file output.
  --stranded            Output per strand counts when writting scaled values.
  --shift-counts        Shift minus strand counts.
  --no-scale            Skip actual scalling in 'scale' command.
  --pdist=<min:max>     Distance range for included paired reads.
  --only-paired         Only accept aligned reads that have a mapped pair.
";

#[derive(Debug, RustcDecodable)]
struct Args {
    arg_fasta_file: String,
    arg_read_size: u16,
    arg_seqtbl_file: String,
    arg_seqrange: Option<String>,
    arg_bam_file: Option<Vec<String>>,
    flag_cut_size: u8,
    flag_tallymer: Option<String>,
    flag_plus_offset: u8,
    flag_minus_offset: u8,
    flag_version: bool,
    flag_read_size: u16,
    flag_parts: u8,
    flag_qual: u8,
    flag_regions: Option<String>,
    flag_out: Option<String>,
    flag_stranded: bool,
    flag_bed: Option<String>,
    flag_skip_bed: bool,
    flag_bw: Option<String>,
    flag_skip_bw: bool,
    flag_shift_counts: bool,
    flag_no_scale: bool,
    flag_pdist: Option<String>,
    flag_only_paired: bool,
    cmd_tallymer: bool,
    cmd_seqtable: bool,
    cmd_dump: bool,
    cmd_table: bool,
    cmd_scale: bool,
}

fn parse_range(range: &str) -> (i32, i32) {
    let parts: Vec<&str> = range.split(':').collect();

    if parts.len() != 2 {
        println!("Invalid distance range: {}", range);
        exit(1);
    }
    let min_val = parts[0].parse::<i32>();
    let max_val = parts[1].parse::<i32>();

    if min_val.is_err() || max_val.is_err() {
        println!("Invalid distance range: {}", range);
        exit(1);
    }
    (min_val.unwrap(),max_val.unwrap())
}

fn stem_filename(stem_src: &str, suffix: &str, out_arg: Option<String>) -> String {
    match out_arg {
        Some(out) => out,
        None => {
            let mut basename = Path::new(stem_src).file_stem().unwrap().to_os_string();
            basename.push(suffix);
            basename.into_string().unwrap()
        }
    }
}

fn file_exists(filename: &str) -> bool {
	match fs::metadata(filename) {
		Ok(meta) => meta.is_file(),
		Err(err) => match err.kind() {
			ErrorKind::NotFound => false,
			_ => { println!("Failed to open file {}: {:}", filename, err); exit(-1) },
		},
	}
}

fn main() {
    // Parse command line arguments
    let args: Args = Docopt::new(USAGE)
                            .and_then(|d| d.decode())
                            .unwrap_or_else(|e| e.exit());

    if args.flag_version {
        println!("Cut-site frequencies, v{}.{}.{}",
            env!( "CARGO_PKG_VERSION_MAJOR" ),
            env!( "CARGO_PKG_VERSION_MINOR" ),
            env!( "CARGO_PKG_VERSION_PATCH" ) );
        return;
    }

    let dist_range = match args.flag_pdist {
        Some(range) => Some(parse_range(&range)),
        None => None
    };

    // Check for data output commands

    if args.cmd_dump {
        if let Some(seqrange) = args.arg_seqrange {
            seqtable::dump_seqtable_range(&args.arg_seqtbl_file, &seqrange);
        } else {
            seqtable::dump_seqtable(&args.arg_seqtbl_file);
        }

        return;
    }

    if args.cmd_table {
        let has_bam = args.arg_bam_file.is_some();
        let counts = counts::tabulate(&args.arg_seqtbl_file, args.arg_bam_file.as_ref(), args.flag_qual, args.flag_regions, dist_range, args.flag_only_paired);
        counts::print_counts(&counts, has_bam);
        return;
    }

    // Check for main sequence commands
    let mut run_tallymer = true;
    let mut run_seqtable = true;
    let mut run_scale = true;

    if args.cmd_tallymer {
        // just phase 1
        run_seqtable = false;
        run_scale = false;
    } else if args.cmd_seqtable {
        // phase 1 & 2
        run_scale = false;
    } else if args.cmd_scale {
        // just phase 3
        run_tallymer = false;
        run_seqtable = false;
    } else {
        // all three phases

        // catch cmd names being interpreted as fasta_file names
        if args.arg_fasta_file.eq("dump") || args.arg_fasta_file.eq("table") || args.arg_fasta_file.eq("tallymer") || args.arg_fasta_file.eq("seqtable") || args.arg_fasta_file.eq("scale") {
            println!("Invalid arguments to {} command.", args.arg_fasta_file);
            println!("{}", USAGE);
            exit(1);
        }
    }

    if args.flag_skip_bed && args.flag_skip_bw {
        println!("--skip-bed and --skip-bw cannot be used together");
        exit(1);
    }

    // Process main sequence phases

    // phase 1 - tallymer
    let tally_path = if run_tallymer {
        if !file_exists(&args.arg_fasta_file) {
            println!("Error: FASTA file {} does not exist!", args.arg_fasta_file);
            exit(1);
        }

        let path = tallyrun::tallymer_createfile(&args.arg_fasta_file, args.flag_read_size, args.flag_parts);
        println!("# tallymer produced/found {:}", path.to_str().unwrap());
        Some(path)
    } else { None };

    // phase 2 - seqtable
    let seqtable_file = if run_seqtable {
        let suffix = format!("_{}.{}.{}.{}.tbl", args.flag_read_size, args.flag_cut_size, args.flag_plus_offset, args.flag_minus_offset);
        let outfile = stem_filename(&args.arg_fasta_file, &suffix, args.flag_out);

        let seq_params = seqtable::SeqTableParams {
            cut_length: args.flag_cut_size,
            plus_offset: args.flag_plus_offset,
            minus_offset: args.flag_minus_offset,
            read_length: args.flag_read_size };

        if file_exists(&outfile) {
            let file = File::open(&outfile).ok().expect("read file");
            let table = match SeqTable::open(file) {
                Ok(value) => value,
                Err(e) => {
                    println!("Error: seqtable: {}", e.to_string());
                    exit(1);
                },
            };
            if table.equivalent(&outfile, &seq_params) {
                println!("# seqtable reusing existing {}", &outfile);
                outfile
            } else {
                println!("Error: seqtable: output file {} already exists but does not match requested parameters!", outfile);
                exit(1);
            }
        } else {
            fasta::process_fasta(&args.arg_fasta_file, &tally_path.unwrap(), seq_params, &outfile);
            println!("# seqtable produced {}", &outfile);
            outfile
        }
    } else {
        args.arg_seqtbl_file
    };

    // phase 3 - tabulate & scale
    if run_scale {
        for filename in args.arg_bam_file.as_ref().unwrap() {
            if !file_exists(filename) {
                println!("Error: BAM file {} does not exist!", filename);
                exit(1);
            }
        }

        let bamfile = args.arg_bam_file.as_ref().unwrap()[0].clone(); // use the first name for reference
        let counts = counts::tabulate(&seqtable_file, args.arg_bam_file.as_ref(), args.flag_qual, args.flag_regions, dist_range, args.flag_only_paired);
        let pileup = scale::scale(&seqtable_file, counts, args.arg_bam_file.as_ref().unwrap(), args.flag_qual, args.flag_shift_counts, args.flag_no_scale, &dist_range, args.flag_only_paired);

        if !args.flag_skip_bed {
            let outfile_bed = if args.flag_no_scale {
                    stem_filename(&bamfile, "_not_scaled.bed", args.flag_bed)
                } else {
                    stem_filename(&bamfile, "_scaled.bed", args.flag_bed)
                };

            if file_exists(&outfile_bed) {
                println!("Error: output BED file {} already exists!", outfile_bed);
                exit(1);
            }

            match pileup.write_bed(&outfile_bed, args.flag_stranded) {
                Ok(_) => println!("# scale produced {}", &outfile_bed),
                Err(err) => println!("Error producing BED file: {}", err.description()),
            }
        } else {
            println!("# scale skipping BED output");
        }

        if !args.flag_skip_bw {
            let outfile_bw = stem_filename(&bamfile, ".bw", args.flag_bw);

            match pileup.write_bw(&outfile_bw, args.flag_stranded) {
                Ok((f1, f2)) => {
                    if f2.is_some() {
                        println!("# scale produced {}", f1);
                        println!("# scale produced {}", f2.unwrap());
                    } else {
                        println!("# scale produced {}", f1);
                    }
                },
                Err(err) => println!("Error producing BigWig file: {}", err.description()),
            }
        } else {
            println!("# scale skipping BigWig output");
        }
    }
}
<|endoftext|>