<commit_before>// Copyright 2016 rust-fuzz developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

extern crate toml;
extern crate clap;
extern crate term;
#[macro_use]
extern crate error_chain;

use clap::{App, Arg, SubCommand, ArgMatches, AppSettings};
use std::{env, fs, path, process};
use std::io::Write;
use std::io::Read;

use std::os::unix::ffi::OsStrExt;

#[macro_use]
mod templates;
mod utils;

error_chain! {
    foreign_links {
        Toml(toml::de::Error);
        Io(::std::io::Error);
    }
}

fn main() {
    let app = App::new("cargo-fuzz")
        .version(option_env!("CARGO_PKG_VERSION").unwrap_or("0.0.0"))
        .about(option_env!("CARGO_PKG_DESCRIPTION").unwrap_or(""))
        .setting(AppSettings::SubcommandRequiredElseHelp)
        .setting(AppSettings::GlobalVersion)
        // cargo passes in the subcommand name to the invoked executable. Use a hidden, optional
        // positional argument to deal with it?
        .arg(Arg::with_name("dummy").possible_value("fuzz").required(false).hidden(true))
        .subcommand(SubCommand::with_name("init").about("Initialize the fuzz folder"))
        .subcommand(SubCommand::with_name("run").about("Run the fuzz target in fuzz/fuzzers")
            .setting(AppSettings::TrailingVarArg)
            .arg(Arg::with_name("TARGET").required(true)
                 .help("name of the fuzz target"))
            .arg(Arg::with_name("ARGS").multiple(true)
                 .help("additional libFuzzer arguments passed to the binary \
                       (including a path to corpus or artefact which must be last argument)"))
        )
        .subcommand(SubCommand::with_name("add").about("Add a new fuzz target")
                    .arg(Arg::with_name("TARGET").required(true)
                         .help("name of the fuzz target"))
        )
        .subcommand(SubCommand::with_name("list").about("List all fuzz targets"));
    let args = app.get_matches();

    ::std::process::exit(match args.subcommand() {
        ("init", _) => FuzzProject::init().map(|_| ()),
        ("add", matches) =>
            FuzzProject::new().and_then(|p| p.add_target(matches.expect("arguments present"))),
        ("list", _) => FuzzProject::new().and_then(|p| p.list_targets()),
        ("run", matches) =>
            FuzzProject::new().and_then(|p| p.exec_target(matches.expect("arguments present"))),
        (s, _) => panic!("unimplemented subcommand {}!", s),
    }.map(|_| 0).unwrap_or_else(|err| {
        utils::report_error(err);
        1
    }));
}

struct FuzzProject {
    /// Path to the root cargo project
    ///
    /// Not the project with fuzz targets, but the project being fuzzed
    root_project: path::PathBuf,
    targets: Vec<String>,
}

impl FuzzProject {
    fn new() -> Result<Self> {
        let mut project = FuzzProject {
            root_project: find_package()?,
            targets: Vec::new()
        };
        let manifest = project.manifest()?;
        if !is_fuzz_manifest(&manifest) {
            return Err(format!("manifest `{:?}` does not look a cargo-fuzz manifest. \
                                Add following lines to override:\n\
                                [package.metadata]\ncargo-fuzz = true",
                                project.manifest_path()).into());
        }
        project.targets = collect_targets(&manifest);
        Ok(project)
    }

    /// Create the fuzz project structure
    ///
    /// This will not clone libfuzzer-sys
    fn init() -> Result<Self> {
        let project = FuzzProject {
            root_project: find_package()?,
            targets: Vec::new(),
        };
        let fuzz_project = project.path();
        // TODO: check if the project is already initialized
        fs::create_dir(&fuzz_project)?;
        fs::create_dir(fuzz_project.join("fuzzers"))?;

        let mut cargo = fs::File::create(fuzz_project.join("Cargo.toml"))?;
        cargo.write_fmt(toml_template!(project.root_project_name()?))?;

        let mut ignore = fs::File::create(fuzz_project.join(".gitignore"))?;
        ignore.write_fmt(gitignore_template!())?;

        const TARGET: &'static str = "fuzzer_script_1";
        project.create_target_template(TARGET)
               .chain_err(|| format!("could not create template file for target {:?}", TARGET))?;
        Ok(project)
    }

    fn list_targets(&self) -> Result<()> {
        for bin in &self.targets {
            utils::print_message(bin, term::color::GREEN);
        }
        Ok(())
    }

    fn add_target<'a>(&self, args: &ArgMatches<'a>) -> Result<()> {
        let target: String = args.value_of_os("TARGET").expect("TARGET is required").to_os_string()
            .into_string().map_err(|_| "TARGET must be valid unicode")?;
        self.create_target_template(&target)
            .chain_err(|| format!("could not create template file for target {:?}", target))
    }

    /// Add a new fuzz target script with a given name
    fn create_target_template<'a>(&self, target: &str) -> Result<()> {
        let target_path = self.target_path(target);
        let mut script = fs::OpenOptions::new().write(true).create_new(true).open(&target_path)?;
        script.write_fmt(target_template!(self.root_project_name()?.replace("-", "_")))?;

        let mut cargo = fs::OpenOptions::new().append(true)
            .open(self.manifest_path())?;
        Ok(cargo.write_fmt(toml_bin_template!(target))?)
    }

    /// Fuzz a given fuzz target
    fn exec_target<'a>(&self, args: &ArgMatches<'a>) -> Result<()> {
        let target: String = args.value_of_os("TARGET").expect("TARGET is required").to_os_string()
            .into_string().map_err(|_| "TARGET must be valid unicode")?;
        let exec_args = args.values_of_os("ARGS")
                            .map(|v| v.collect::<Vec<_>>());
        let target_triple = "x86_64-unknown-linux-gnu";

        let mut flags = env::var("RUSTFLAGS").unwrap_or("".into());
        if !flags.is_empty() {
            flags.push(' ');
        }
        flags.push_str("-Cpasses=sancov -Cllvm-args=-sanitizer-coverage-level=3 -Zsanitizer=address -Cpanic=abort");
        let mut cmd = process::Command::new("cargo");

        fs::create_dir_all("corpus")?;
        fs::create_dir_all("artifacts")?;

        // Merge the asan options, so users can still provide their own options
        // to e.g. disable the leak sanitizer. Options are colon-separated.
        let mut asan_opts = env::var("ASAN_OPTIONS").unwrap_or("".into());
        if !asan_opts.is_empty() {
            asan_opts.push(':');
        }
        asan_opts.push_str("detect_odr_violation=0");

        cmd.env("RUSTFLAGS", flags)
           .arg("run")
           .arg("--manifest-path")
           .arg(self.manifest_path())
           .arg("--verbose")
           .arg("--bin")
           .arg(&target)
           .arg("--target")
           // won't pass rustflags to build scripts
           .arg(target_triple)
           .arg("--")
           .arg("-artifact_prefix=artifacts/")
           .env("ASAN_OPTIONS", &asan_opts);
        let exec_args = exec_args.as_ref().and_then(|args| args.split_last());
        exec_args.map(|(last, rest)| {
            for arg in rest {
                cmd.arg(arg);
            }
            if last.as_bytes().starts_with(b"-") {
                cmd.arg(last);
                cmd.arg("corpus"); // must be last arg
            } else {
                // Allow users to specify their own corpus directory or test case
                cmd.arg(last);
            }
        });
        exec_cmd(&mut cmd).chain_err(|| format!("could not execute command: {:?}", cmd))?;
        Ok(())
    }

    fn path(&self) -> path::PathBuf {
        self.root_project.join("fuzz")
    }

    fn manifest_path(&self) -> path::PathBuf {
        self.path().join("Cargo.toml")
    }

    fn target_path(&self, target: &str) -> path::PathBuf {
        let mut root = self.path();
        root.push("fuzzers");
        root.push(target);
        root.set_extension("rs");
        root
    }

    fn manifest(&self) -> Result<toml::Value> {
        let filename = self.manifest_path();
        let mut file = fs::File::open(&filename).chain_err(||
            format!("could not read the manifest file: {:?}", filename)
        )?;
        let mut data = Vec::new();
        file.read_to_end(&mut data)?;
        toml::from_slice(&data).chain_err(||
            format!("could not decode the manifest file at {:?}", filename)
        )
    }

    fn root_project_name(&self) -> Result<String> {
        let filename = self.root_project.join("Cargo.toml");
        let mut file = fs::File::open(&filename)?;
        let mut data = Vec::new();
        file.read_to_end(&mut data)?;
        let value: toml::Value = toml::from_slice(&data)?;
        let name = value.as_table().and_then(|v| v.get("package"))
                                   .and_then(toml::Value::as_table)
                                   .and_then(|v| v.get("name"))
                                   .and_then(toml::Value::as_str);
        if let Some(name) = name {
            Ok(String::from(name))
        } else {
            Err(format!("{:?} (package.name) is malformed", filename).into())
        }
    }
}

fn collect_targets(value: &toml::Value) -> Vec<String> {
    let bins = value.as_table().and_then(|v| v.get("bin"))
                               .and_then(toml::Value::as_array);
    if let Some(bins) = bins {
        bins.iter().map(|bin|
            bin.as_table().and_then(|v| v.get("name")).and_then(toml::Value::as_str)
        ).filter_map(|name| name.map(|v| String::from(v))).collect()
    } else {
        Vec::new()
    }
}

fn is_fuzz_manifest(value: &toml::Value) -> bool {
    let is_fuzz = value.as_table().and_then(|v| v.get("package"))
                                  .and_then(toml::Value::as_table)
                                  .and_then(|v| v.get("metadata"))
                                  .and_then(toml::Value::as_table)
                                  .and_then(|v| v.get("cargo-fuzz"))
                                  .and_then(toml::Value::as_bool);
    is_fuzz == Some(true)
}

/// Returns the path for the first found non-fuzz Cargo package
fn find_package() -> Result<path::PathBuf> {
    let mut dir = env::current_dir()?;
    let mut data = Vec::new();
    loop {
        let manifest_path = dir.join("Cargo.toml");
        match fs::File::open(&manifest_path) {
            Err(_) => {},
            Ok(mut f) => {
                f.read_to_end(&mut data)?;
                let value: toml::Value = toml::from_slice(&data)
                    .chain_err(||
                        format!("could not decode the manifest file at {:?}", manifest_path)
                    )?;
                if !is_fuzz_manifest(&value) {
                    // Not a cargo-fuzz project => must be a proper cargo project :)
                    return Ok(dir);
                }
            }
        }
        if !dir.pop() { break; }
    }
    Err("could not find a cargo project".into())
}

#[cfg(unix)]
fn exec_cmd(cmd: &mut process::Command) -> Result<process::ExitStatus> {
    use std::os::unix::process::CommandExt;
    Err(cmd.exec().into())
}

#[cfg(not(unix))]
fn exec_cmd(cmd: &mut process::Command) -> Result<process::ExitStatus> {
    cmd.status().map_err(|e| e.into())
}
<commit_msg>Per-target corpus and artefact directories<commit_after>// Copyright 2016 rust-fuzz developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

extern crate toml;
extern crate clap;
extern crate term;
#[macro_use]
extern crate error_chain;

use clap::{App, Arg, SubCommand, ArgMatches, AppSettings};
use std::{env, fs, path, ffi, process};
use std::io::Write;
use std::io::Read;

use std::os::unix::ffi::OsStrExt;

#[macro_use]
mod templates;
mod utils;

error_chain! {
    foreign_links {
        Toml(toml::de::Error);
        Io(::std::io::Error);
    }
}

fn main() {
    let app = App::new("cargo-fuzz")
        .version(option_env!("CARGO_PKG_VERSION").unwrap_or("0.0.0"))
        .about(option_env!("CARGO_PKG_DESCRIPTION").unwrap_or(""))
        .setting(AppSettings::SubcommandRequiredElseHelp)
        .setting(AppSettings::GlobalVersion)
        // cargo passes in the subcommand name to the invoked executable. Use a hidden, optional
        // positional argument to deal with it?
        .arg(Arg::with_name("dummy").possible_value("fuzz").required(false).hidden(true))
        .subcommand(SubCommand::with_name("init").about("Initialize the fuzz folder"))
        .subcommand(SubCommand::with_name("run").about("Run the fuzz target in fuzz/fuzzers")
            .setting(AppSettings::TrailingVarArg)
            .arg(Arg::with_name("TARGET").required(true)
                 .help("name of the fuzz target"))
            .arg(Arg::with_name("ARGS").multiple(true)
                 .help("additional libFuzzer arguments passed to the binary \
                       (including a path to corpus or artefact which must be last argument)"))
        )
        .subcommand(SubCommand::with_name("add").about("Add a new fuzz target")
                    .arg(Arg::with_name("TARGET").required(true)
                         .help("name of the fuzz target"))
        )
        .subcommand(SubCommand::with_name("list").about("List all fuzz targets"));
    let args = app.get_matches();

    ::std::process::exit(match args.subcommand() {
        ("init", _) => FuzzProject::init().map(|_| ()),
        ("add", matches) =>
            FuzzProject::new().and_then(|p| p.add_target(matches.expect("arguments present"))),
        ("list", _) => FuzzProject::new().and_then(|p| p.list_targets()),
        ("run", matches) =>
            FuzzProject::new().and_then(|p| p.exec_target(matches.expect("arguments present"))),
        (s, _) => panic!("unimplemented subcommand {}!", s),
    }.map(|_| 0).unwrap_or_else(|err| {
        utils::report_error(err);
        1
    }));
}

struct FuzzProject {
    /// Path to the root cargo project
    ///
    /// Not the project with fuzz targets, but the project being fuzzed
    root_project: path::PathBuf,
    targets: Vec<String>,
}

impl FuzzProject {
    fn new() -> Result<Self> {
        let mut project = FuzzProject {
            root_project: find_package()?,
            targets: Vec::new()
        };
        let manifest = project.manifest()?;
        if !is_fuzz_manifest(&manifest) {
            return Err(format!("manifest `{:?}` does not look a cargo-fuzz manifest. \
                                Add following lines to override:\n\
                                [package.metadata]\ncargo-fuzz = true",
                                project.manifest_path()).into());
        }
        project.targets = collect_targets(&manifest);
        Ok(project)
    }

    /// Create the fuzz project structure
    ///
    /// This will not clone libfuzzer-sys
    fn init() -> Result<Self> {
        let project = FuzzProject {
            root_project: find_package()?,
            targets: Vec::new(),
        };
        let fuzz_project = project.path();
        // TODO: check if the project is already initialized
        fs::create_dir(&fuzz_project)?;
        fs::create_dir(fuzz_project.join("fuzzers"))?;

        let mut cargo = fs::File::create(fuzz_project.join("Cargo.toml"))?;
        cargo.write_fmt(toml_template!(project.root_project_name()?))?;

        let mut ignore = fs::File::create(fuzz_project.join(".gitignore"))?;
        ignore.write_fmt(gitignore_template!())?;

        const TARGET: &'static str = "fuzzer_script_1";
        project.create_target_template(TARGET)
               .chain_err(|| format!("could not create template file for target {:?}", TARGET))?;
        Ok(project)
    }

    fn list_targets(&self) -> Result<()> {
        for bin in &self.targets {
            utils::print_message(bin, term::color::GREEN);
        }
        Ok(())
    }

    fn add_target<'a>(&self, args: &ArgMatches<'a>) -> Result<()> {
        let target: String = args.value_of_os("TARGET").expect("TARGET is required").to_os_string()
            .into_string().map_err(|_| "TARGET must be valid unicode")?;
        self.create_target_template(&target)
            .chain_err(|| format!("could not create template file for target {:?}", target))
    }

    /// Add a new fuzz target script with a given name
    fn create_target_template<'a>(&self, target: &str) -> Result<()> {
        let target_path = self.target_path(target);
        let mut script = fs::OpenOptions::new().write(true).create_new(true).open(&target_path)?;
        script.write_fmt(target_template!(self.root_project_name()?.replace("-", "_")))?;

        let mut cargo = fs::OpenOptions::new().append(true)
            .open(self.manifest_path())?;
        Ok(cargo.write_fmt(toml_bin_template!(target))?)
    }

    /// Fuzz a given fuzz target
    fn exec_target<'a>(&self, args: &ArgMatches<'a>) -> Result<()> {
        let target: String = args.value_of_os("TARGET").expect("TARGET is required").to_os_string()
            .into_string().map_err(|_| "TARGET must be valid unicode")?;
        let exec_args = args.values_of_os("ARGS")
                            .map(|v| v.collect::<Vec<_>>());
        let target_triple = "x86_64-unknown-linux-gnu";

        let mut flags = env::var("RUSTFLAGS").unwrap_or("".into());
        if !flags.is_empty() {
            flags.push(' ');
        }
        flags.push_str("-Cpasses=sancov -Cllvm-args=-sanitizer-coverage-level=3 -Zsanitizer=address -Cpanic=abort");
        let mut cmd = process::Command::new("cargo");

        let mut artefact_arg = ffi::OsString::from("-artifact_prefix=");
        artefact_arg.push(self.artifacts_for(&target)?);

        // Merge the asan options, so users can still provide their own options
        // to e.g. disable the leak sanitizer. Options are colon-separated.
        let mut asan_opts = env::var("ASAN_OPTIONS").unwrap_or("".into());
        if !asan_opts.is_empty() {
            asan_opts.push(':');
        }
        asan_opts.push_str("detect_odr_violation=0");

        cmd.env("RUSTFLAGS", flags)
           .arg("run")
           .arg("--manifest-path")
           .arg(self.manifest_path())
           .arg("--verbose")
           .arg("--bin")
           .arg(&target)
           .arg("--target")
           // won't pass rustflags to build scripts
           .arg(target_triple)
           .arg("--")
           .arg(artefact_arg)
           .env("ASAN_OPTIONS", &asan_opts);

        // Corpus or artefact file must be last argument passed. Check if the last argument being
        // passed through to libfuzzer looks anything like a not-argument (missing `-`). If so,
        // do not append our pregenerated corpus directory.
        if let Some((last, rest)) = exec_args.as_ref().and_then(|args| args.split_last()) {
            for arg in rest {
                cmd.arg(arg);
            }
            if last.as_bytes().starts_with(b"-") {
                cmd.arg(last);
                cmd.arg(self.corpus_for(&target)?);
            } else {
                cmd.arg(last);
            }
        } else {
            cmd.arg(self.corpus_for(&target)?);
        }

        exec_cmd(&mut cmd).chain_err(|| format!("could not execute command: {:?}", cmd))?;
        Ok(())
    }

    fn path(&self) -> path::PathBuf {
        self.root_project.join("fuzz")
    }

    fn manifest_path(&self) -> path::PathBuf {
        self.path().join("Cargo.toml")
    }

    fn corpus_for(&self, target: &str) -> Result<path::PathBuf> {
        let mut p = self.path();
        p.push("corpus");
        p.push(target);
        fs::create_dir_all(&p)
            .chain_err(|| format!("could not make a corpus directory at {:?}", p))?;
        Ok(p)
    }

    fn artifacts_for(&self, target: &str) -> Result<path::PathBuf> {
        let mut p = self.path();
        p.push("artifacts");
        p.push(target);
        p.push(""); // trailing slash, necessary for libfuzzer, because it does simple concat
        fs::create_dir_all(&p)
            .chain_err(|| format!("could not make a artifact directory at {:?}", p))?;
        Ok(p)
    }

    fn target_path(&self, target: &str) -> path::PathBuf {
        let mut root = self.path();
        root.push("fuzzers");
        root.push(target);
        root.set_extension("rs");
        root
    }

    fn manifest(&self) -> Result<toml::Value> {
        let filename = self.manifest_path();
        let mut file = fs::File::open(&filename).chain_err(||
            format!("could not read the manifest file: {:?}", filename)
        )?;
        let mut data = Vec::new();
        file.read_to_end(&mut data)?;
        toml::from_slice(&data).chain_err(||
            format!("could not decode the manifest file at {:?}", filename)
        )
    }

    fn root_project_name(&self) -> Result<String> {
        let filename = self.root_project.join("Cargo.toml");
        let mut file = fs::File::open(&filename)?;
        let mut data = Vec::new();
        file.read_to_end(&mut data)?;
        let value: toml::Value = toml::from_slice(&data)?;
        let name = value.as_table().and_then(|v| v.get("package"))
                                   .and_then(toml::Value::as_table)
                                   .and_then(|v| v.get("name"))
                                   .and_then(toml::Value::as_str);
        if let Some(name) = name {
            Ok(String::from(name))
        } else {
            Err(format!("{:?} (package.name) is malformed", filename).into())
        }
    }
}

fn collect_targets(value: &toml::Value) -> Vec<String> {
    let bins = value.as_table().and_then(|v| v.get("bin"))
                               .and_then(toml::Value::as_array);
    if let Some(bins) = bins {
        bins.iter().map(|bin|
            bin.as_table().and_then(|v| v.get("name")).and_then(toml::Value::as_str)
        ).filter_map(|name| name.map(|v| String::from(v))).collect()
    } else {
        Vec::new()
    }
}

fn is_fuzz_manifest(value: &toml::Value) -> bool {
    let is_fuzz = value.as_table().and_then(|v| v.get("package"))
                                  .and_then(toml::Value::as_table)
                                  .and_then(|v| v.get("metadata"))
                                  .and_then(toml::Value::as_table)
                                  .and_then(|v| v.get("cargo-fuzz"))
                                  .and_then(toml::Value::as_bool);
    is_fuzz == Some(true)
}

/// Returns the path for the first found non-fuzz Cargo package
fn find_package() -> Result<path::PathBuf> {
    let mut dir = env::current_dir()?;
    let mut data = Vec::new();
    loop {
        let manifest_path = dir.join("Cargo.toml");
        match fs::File::open(&manifest_path) {
            Err(_) => {},
            Ok(mut f) => {
                f.read_to_end(&mut data)?;
                let value: toml::Value = toml::from_slice(&data)
                    .chain_err(||
                        format!("could not decode the manifest file at {:?}", manifest_path)
                    )?;
                if !is_fuzz_manifest(&value) {
                    // Not a cargo-fuzz project => must be a proper cargo project :)
                    return Ok(dir);
                }
            }
        }
        if !dir.pop() { break; }
    }
    Err("could not find a cargo project".into())
}

#[cfg(unix)]
fn exec_cmd(cmd: &mut process::Command) -> Result<process::ExitStatus> {
    use std::os::unix::process::CommandExt;
    Err(cmd.exec().into())
}

#[cfg(not(unix))]
fn exec_cmd(cmd: &mut process::Command) -> Result<process::ExitStatus> {
    cmd.status().map_err(|e| e.into())
}
<|endoftext|>


<commit_before>//! Documentation for Rust Bio Tools
use clap::{load_yaml, value_t};
use log::LevelFilter;

use clap::App;
use itertools::Itertools;
use rayon::prelude::*;
use std::collections::HashMap;
use std::error::Error;
use std::fs;
use std::path::Path;
use std::str::FromStr;

pub mod bam;
pub mod bcf;
pub mod common;
pub mod fastq;
pub mod sequences_stats;

fn main() -> Result<(), Box<dyn Error>> {
    let yaml = load_yaml!("cli.yaml");
    let matches = App::from_yaml(yaml)
        .version(env!("CARGO_PKG_VERSION"))
        .get_matches();

    fern::Dispatch::new()
        .format(|out, message, _| out.finish(format_args!("{}", message)))
        .level(if matches.is_present("verbose") {
            LevelFilter::Debug
        } else {
            LevelFilter::Info
        })
        .chain(std::io::stderr())
        .apply()
        .unwrap();

    match matches.subcommand() {
        ("fastq-split", Some(matches)) => {
            fastq::split::split(&matches.values_of("chunks").unwrap().collect_vec())
        }
        ("fastq-filter", Some(matches)) => fastq::filter::filter(&matches.value_of("ids").unwrap()),
        ("bam-depth", Some(matches)) => bam::depth::depth(
            &matches.value_of("bam-path").unwrap(),
            value_t!(matches, "max-read-length", u32).unwrap_or(1000),
            value_t!(matches, "include-flags", u16).unwrap_or(0),
            value_t!(matches, "exclude-flags", u16).unwrap_or(4 | 256 | 512 | 1024),
            value_t!(matches, "min-mapq", u8).unwrap_or(0),
        ),
        ("vcf-to-txt", Some(matches)) => bcf::to_txt::to_txt(
            &matches
                .values_of("info")
                .map(|values| values.collect_vec())
                .unwrap_or_default(),
            &matches
                .values_of("format")
                .map(|values| values.collect_vec())
                .unwrap_or_default(),
            matches.is_present("genotypes"),
        ),
        ("vcf-match", Some(matches)) => bcf::match_variants::match_variants(
            matches.value_of("vcf").unwrap(),
            value_t!(matches, "max-dist", u32).unwrap_or(20),
            value_t!(matches, "max-len-diff", u32).unwrap_or(10),
        ),
        ("vcf-baf", Some(_)) => bcf::baf::calculate_baf(),
        ("vcf-fix-iupac-alleles", Some(_)) => bcf::fix_iupac_alleles::fix_iupac_alleles(),
        ("vcf-annotate-dgidb", Some(matches)) => bcf::annotate_dgidb::annotate_dgidb(
            &matches.value_of("vcf").unwrap(),
            matches.value_of("api-path").unwrap().to_string(),
            &matches.value_of("field").unwrap(),
            match matches.is_present("datasources") {
                true => Some(matches.values_of("datasources").unwrap().collect()),
                false => None,
            },
            value_t!(matches, "genes-per-request", usize).unwrap(),
        ),
        ("vcf-report", Some(matches)) => {
            let mut sample_calls = HashMap::new();
            let mut bam_paths = HashMap::new();
            let output_path = matches.value_of("output-path").unwrap();
            let threads = usize::from_str(matches.value_of("threads").unwrap()).unwrap();
            if !Path::new(output_path).exists() {
                fs::create_dir(Path::new(output_path)).unwrap_or_else(|_| {
                    panic!(
                        "Couldn't create output directory at {}. Please make sure the path exists.",
                        output_path
                    )
                });
            }
            let max_cells = u32::from_str(matches.value_of("max-cells").unwrap()).unwrap();
            let max_read_depth =
                u32::from_str(matches.value_of("max-read-depth").unwrap()).unwrap();
            let custom_js = matches.value_of("custom-js-template");
            let js_files = matches.values_of("custom-js-files");
            let js_files_vec = js_files
                .clone()
                .map_or_else(Vec::new, |values| values.collect());
            let js_file_names = if let Some(files) = js_files.clone() {
                files
                    .map(|f| {
                        f.split('/')
                            .collect_vec()
                            .pop()
                            .unwrap_or_else(|| {
                                panic!("Unable to extract file name from path: {:?}", f)
                            })
                            .to_owned()
                    })
                    .collect()
            } else {
                vec![]
            };
            let tsv_data = matches.value_of("tsv");
            bcf::report::embed_js(output_path, custom_js, js_files_vec)?;
            bcf::report::embed_css(output_path)?;
            bcf::report::embed_html(output_path)?;
            let fasta_path = matches.value_of("fasta").unwrap();
            let detail_path = output_path.to_owned() + "/details/";
            fs::create_dir(Path::new(&detail_path))?;
            let vcfs = matches.values_of("vcfs").unwrap();
            let bams = matches.values_of("bams").unwrap();
            let infos = matches.values_of("info");
            let formats = matches.values_of("format");
            for vcf in vcfs {
                let v: Vec<_> = vcf.split('=').collect();
                match sample_calls.insert(v[0].to_owned(), v[1].to_owned()) {
                    None => {}
                    _ => panic!("Found duplicate sample name {}. Please make sure the provided sample names are unique.", v[0].to_owned())
                }
            }
            for bam in bams {
                let b: Vec<_> = bam.split('=').collect();
                let c: Vec<_> = b[0].split(':').collect();
                let rec = bam_paths.entry(c[0].to_owned()).or_insert_with(Vec::new);
                rec.push((c[1].to_owned(), b[1].to_owned()))
            }
            let pool = rayon::ThreadPoolBuilder::new()
                .num_threads(threads)
                .build()?;
            pool.install(|| {
                sample_calls.par_iter().for_each(|(sample, sample_call)| {
                    bcf::report::table_report::table_report(
                        sample_call,
                        fasta_path,
                        bam_paths
                            .get(sample)
                            .unwrap_or_else(|| panic!("No bam provided for sample {}", sample)),
                        output_path,
                        sample,
                        infos.clone(),
                        formats.clone(),
                        max_read_depth,
                        js_file_names.clone(),
                    )
                    .unwrap_or_else(|_| {
                        panic!("Failed building table report for sample {}", sample)
                    });
                })
            });

            bcf::report::oncoprint::oncoprint(&sample_calls, output_path, max_cells, tsv_data)
        }
        ("vcf-split", Some(matches)) => {
            bcf::split::split(
                matches.value_of("input").unwrap(),
                &matches.values_of("output").unwrap().collect::<Vec<_>>(),
            )?;
            Ok(())
        }
        ("collapse-reads-to-fragments", Some(matches)) => match matches.subcommand() {
            ("fastq", Some(matches)) => {
                fastq::collapse_reads_to_fragments::call_consensus_reads_from_paths(
                    matches.value_of("fq1").unwrap(),
                    matches.value_of("fq2").unwrap(),
                    matches.value_of("consensus-fq1").unwrap(),
                    matches.value_of("consensus-fq2").unwrap(),
                    matches.value_of("consensus-fq3"),
                    value_t!(matches, "umi-len", usize).unwrap(),
                    value_t!(matches, "max-seq-dist", usize).unwrap(),
                    value_t!(matches, "max-umi-dist", usize).unwrap(),
                    matches.is_present("umi-on-reverse"),
                    matches.is_present("verbose-read-names"),
                    if matches.is_present("insert-size") {
                        Some(value_t!(matches, "insert-size", usize).unwrap())
                    } else {
                        None
                    },
                    if matches.is_present("std-dev") {
                        Some(value_t!(matches, "std-dev", usize).unwrap())
                    } else {
                        None
                    },
                )
            }
            ("bam", Some(matches)) => {
                bam::collapse_reads_to_fragments::call_consensus_reads_from_paths(
                    matches.value_of("bam").unwrap(),
                    matches.value_of("consensus-fq1").unwrap(),
                    matches.value_of("consensus-fq2").unwrap(),
                    matches.value_of("consensus-fq-se").unwrap(),
                    matches.value_of("skipped-bam").unwrap(),
                    matches.is_present("verbose-read-names"),
                )
            }
            _ => unreachable!(),
        },
        ("sequence-stats", Some(matches)) => sequences_stats::stats(matches.is_present("fastq")),
        // This cannot be reached, since the matches step of
        // clap assures that a valid subcommand is provided
        _ => unreachable!(),
    }
}
<commit_msg>Use global thread pool<commit_after>//! Documentation for Rust Bio Tools
use clap::{load_yaml, value_t};
use log::LevelFilter;

use clap::App;
use itertools::Itertools;
use rayon::prelude::*;
use std::collections::HashMap;
use std::error::Error;
use std::fs;
use std::path::Path;
use std::str::FromStr;

pub mod bam;
pub mod bcf;
pub mod common;
pub mod fastq;
pub mod sequences_stats;

fn main() -> Result<(), Box<dyn Error>> {
    let yaml = load_yaml!("cli.yaml");
    let matches = App::from_yaml(yaml)
        .version(env!("CARGO_PKG_VERSION"))
        .get_matches();

    fern::Dispatch::new()
        .format(|out, message, _| out.finish(format_args!("{}", message)))
        .level(if matches.is_present("verbose") {
            LevelFilter::Debug
        } else {
            LevelFilter::Info
        })
        .chain(std::io::stderr())
        .apply()
        .unwrap();

    match matches.subcommand() {
        ("fastq-split", Some(matches)) => {
            fastq::split::split(&matches.values_of("chunks").unwrap().collect_vec())
        }
        ("fastq-filter", Some(matches)) => fastq::filter::filter(&matches.value_of("ids").unwrap()),
        ("bam-depth", Some(matches)) => bam::depth::depth(
            &matches.value_of("bam-path").unwrap(),
            value_t!(matches, "max-read-length", u32).unwrap_or(1000),
            value_t!(matches, "include-flags", u16).unwrap_or(0),
            value_t!(matches, "exclude-flags", u16).unwrap_or(4 | 256 | 512 | 1024),
            value_t!(matches, "min-mapq", u8).unwrap_or(0),
        ),
        ("vcf-to-txt", Some(matches)) => bcf::to_txt::to_txt(
            &matches
                .values_of("info")
                .map(|values| values.collect_vec())
                .unwrap_or_default(),
            &matches
                .values_of("format")
                .map(|values| values.collect_vec())
                .unwrap_or_default(),
            matches.is_present("genotypes"),
        ),
        ("vcf-match", Some(matches)) => bcf::match_variants::match_variants(
            matches.value_of("vcf").unwrap(),
            value_t!(matches, "max-dist", u32).unwrap_or(20),
            value_t!(matches, "max-len-diff", u32).unwrap_or(10),
        ),
        ("vcf-baf", Some(_)) => bcf::baf::calculate_baf(),
        ("vcf-fix-iupac-alleles", Some(_)) => bcf::fix_iupac_alleles::fix_iupac_alleles(),
        ("vcf-annotate-dgidb", Some(matches)) => bcf::annotate_dgidb::annotate_dgidb(
            &matches.value_of("vcf").unwrap(),
            matches.value_of("api-path").unwrap().to_string(),
            &matches.value_of("field").unwrap(),
            match matches.is_present("datasources") {
                true => Some(matches.values_of("datasources").unwrap().collect()),
                false => None,
            },
            value_t!(matches, "genes-per-request", usize).unwrap(),
        ),
        ("vcf-report", Some(matches)) => {
            let mut sample_calls = HashMap::new();
            let mut bam_paths = HashMap::new();
            let output_path = matches.value_of("output-path").unwrap();
            let threads = usize::from_str(matches.value_of("threads").unwrap()).unwrap();
            if !Path::new(output_path).exists() {
                fs::create_dir(Path::new(output_path)).unwrap_or_else(|_| {
                    panic!(
                        "Couldn't create output directory at {}. Please make sure the path exists.",
                        output_path
                    )
                });
            }
            let max_cells = u32::from_str(matches.value_of("max-cells").unwrap()).unwrap();
            let max_read_depth =
                u32::from_str(matches.value_of("max-read-depth").unwrap()).unwrap();
            let custom_js = matches.value_of("custom-js-template");
            let js_files = matches.values_of("custom-js-files");
            let js_files_vec = js_files
                .clone()
                .map_or_else(Vec::new, |values| values.collect());
            let js_file_names = if let Some(files) = js_files.clone() {
                files
                    .map(|f| {
                        f.split('/')
                            .collect_vec()
                            .pop()
                            .unwrap_or_else(|| {
                                panic!("Unable to extract file name from path: {:?}", f)
                            })
                            .to_owned()
                    })
                    .collect()
            } else {
                vec![]
            };
            let tsv_data = matches.value_of("tsv");
            bcf::report::embed_js(output_path, custom_js, js_files_vec)?;
            bcf::report::embed_css(output_path)?;
            bcf::report::embed_html(output_path)?;
            let fasta_path = matches.value_of("fasta").unwrap();
            let detail_path = output_path.to_owned() + "/details/";
            fs::create_dir(Path::new(&detail_path))?;
            let vcfs = matches.values_of("vcfs").unwrap();
            let bams = matches.values_of("bams").unwrap();
            let infos = matches.values_of("info");
            let formats = matches.values_of("format");
            for vcf in vcfs {
                let v: Vec<_> = vcf.split('=').collect();
                match sample_calls.insert(v[0].to_owned(), v[1].to_owned()) {
                    None => {}
                    _ => panic!("Found duplicate sample name {}. Please make sure the provided sample names are unique.", v[0].to_owned())
                }
            }
            for bam in bams {
                let b: Vec<_> = bam.split('=').collect();
                let c: Vec<_> = b[0].split(':').collect();
                let rec = bam_paths.entry(c[0].to_owned()).or_insert_with(Vec::new);
                rec.push((c[1].to_owned(), b[1].to_owned()))
            }

            rayon::ThreadPoolBuilder::new()
                .num_threads(threads)
                .build_global()?;

            sample_calls.par_iter().for_each(|(sample, sample_call)| {
                bcf::report::table_report::table_report(
                    sample_call,
                    fasta_path,
                    bam_paths
                        .get(sample)
                        .unwrap_or_else(|| panic!("No bam provided for sample {}", sample)),
                    output_path,
                    sample,
                    infos.clone(),
                    formats.clone(),
                    max_read_depth,
                    js_file_names.clone(),
                )
                .unwrap_or_else(|_| panic!("Failed building table report for sample {}", sample));
            });

            bcf::report::oncoprint::oncoprint(&sample_calls, output_path, max_cells, tsv_data)
        }
        ("vcf-split", Some(matches)) => {
            bcf::split::split(
                matches.value_of("input").unwrap(),
                &matches.values_of("output").unwrap().collect::<Vec<_>>(),
            )?;
            Ok(())
        }
        ("collapse-reads-to-fragments", Some(matches)) => match matches.subcommand() {
            ("fastq", Some(matches)) => {
                fastq::collapse_reads_to_fragments::call_consensus_reads_from_paths(
                    matches.value_of("fq1").unwrap(),
                    matches.value_of("fq2").unwrap(),
                    matches.value_of("consensus-fq1").unwrap(),
                    matches.value_of("consensus-fq2").unwrap(),
                    matches.value_of("consensus-fq3"),
                    value_t!(matches, "umi-len", usize).unwrap(),
                    value_t!(matches, "max-seq-dist", usize).unwrap(),
                    value_t!(matches, "max-umi-dist", usize).unwrap(),
                    matches.is_present("umi-on-reverse"),
                    matches.is_present("verbose-read-names"),
                    if matches.is_present("insert-size") {
                        Some(value_t!(matches, "insert-size", usize).unwrap())
                    } else {
                        None
                    },
                    if matches.is_present("std-dev") {
                        Some(value_t!(matches, "std-dev", usize).unwrap())
                    } else {
                        None
                    },
                )
            }
            ("bam", Some(matches)) => {
                bam::collapse_reads_to_fragments::call_consensus_reads_from_paths(
                    matches.value_of("bam").unwrap(),
                    matches.value_of("consensus-fq1").unwrap(),
                    matches.value_of("consensus-fq2").unwrap(),
                    matches.value_of("consensus-fq-se").unwrap(),
                    matches.value_of("skipped-bam").unwrap(),
                    matches.is_present("verbose-read-names"),
                )
            }
            _ => unreachable!(),
        },
        ("sequence-stats", Some(matches)) => sequences_stats::stats(matches.is_present("fastq")),
        // This cannot be reached, since the matches step of
        // clap assures that a valid subcommand is provided
        _ => unreachable!(),
    }
}
<|endoftext|>


<commit_before>extern crate combine;

use std::io::prelude::*;

use combine::{between, many1, digit, letter, spaces, string, try, choice, parser, Parser, ParserExt, ParseError};
use combine::char as combine_char;
use combine::primitives::{Stream, State, ParseResult};

#[derive(Debug, PartialEq)]
enum LispVal {
    Atom(String),
    Bool(bool),
    Number(i32),
    Character(char),
    String(String)
}

fn parse_symbol<I>(input: State<I>) -> ParseResult<String, I>
where I: Stream<Item=char>
{
    let lex_char = |c| combine_char(c).skip(spaces());

    let symbols = choice([lex_char('!'), lex_char('$'), lex_char('%'), lex_char('$'), lex_char('&'),
                    lex_char('|'), lex_char('*'), lex_char('+'), lex_char('-'), lex_char(':'),
                    lex_char('<'), lex_char('='), lex_char('>'), lex_char('?'), lex_char('@'),
                    lex_char('^'), lex_char('_'), lex_char('~')]);

    symbols.map(|c: char| {
            let mut s = String::new();
            s.push(c);
            return s;
        })
        .parse_state(input)
}

fn parse_bool<I>(input: State<I>) -> ParseResult<bool, I>
where I: Stream<Item=char>
{
    let bewl = try(string("#t").map(|_| return true)).or(
            string("#f").map(|_| return false)
        )
       .parse_state(input);

    bewl
}

fn parse_atom<I>(input: State<I>) -> ParseResult<String, I>
where I: Stream<Item=char>
{
    let atom = (parser(parse_symbol::<I>))
        .parse_state(input);

    atom
}

fn parse_string<I>(input: State<I>) -> ParseResult<String, I>
where I: Stream<Item=char>
{
    let string = between(combine_char('"'), combine_char('"'), many1(letter()))
        .parse_state(input);
    string
}

fn parse_char<I>(input: State<I>) -> ParseResult<char, I>
where I: Stream<Item=char>
{
    between(combine_char('\''), combine_char('\''), letter())
        .parse_state(input)
}

fn parse_number<I>(input: State<I>) -> ParseResult<i32, I>
where I: Stream<Item=char>
{
    let number = many1(digit())
        .map(|string: String| string.parse::<i32>().unwrap())
        .parse_state(input);
    number
}

fn rusty_lisp<I>(input: State<I>) -> ParseResult<LispVal, I>
where I: Stream<Item=char>
{
    let lispval = parser(parse_atom::<I>)
                   .map(LispVal::Atom)
        .or(parser(parse_bool::<I>)
            .map(LispVal::Bool))
        .or(parser(parse_number::<I>)
            .map(LispVal::Number))
        .or(parser(parse_char::<I>)
            .map(LispVal::Character))
        .or(parser(parse_string::<I>)
            .map(LispVal::String))
        .parse_state(input);

    lispval
}

fn parse(program: String) {
    let program = program.as_ref();

    let result: Result<(LispVal, &str), ParseError<&str>> = parser(rusty_lisp).parse(program);
    match result {
        Ok((value, _remaining_input)) => println!("{:?}", value),
        Err(err) => println!("{}", err)
    }
}

fn main() {
    println!("rusty-lisp v0.1.0");
    println!("Press 'Ctrl+C' to exit");
    println!("");

    let mut stdin = std::io::stdin();
    let mut input = String::new();

    stdin.read_to_string(&mut input).unwrap();
    parse(input);

    println!("Thanks for using!");
}

#[cfg(test)]
mod tests {
    use combine::{parser, Parser, ParseError};
    use super::{parse_symbol, parse_bool, parse_char, parse_string, parse_number};

    #[test]
    fn test_parse_symbol() {
        let input = "!";
        let result: Result<(String, &str), ParseError<&str>> = parser(parse_symbol).parse(input);
        assert_eq!(result, Ok(("!".to_string(), "")))
    }

    #[test]
    fn test_parse_bool() {
        let input = "#t";
        let result: Result<(bool, &str), ParseError<&str>> = parser(parse_bool).parse(input);
        assert_eq!(result, Ok((true, "")));

        let input = "#f";
        let result: Result<(bool, &str), ParseError<&str>> = parser(parse_bool).parse(input);
        assert_eq!(result, Ok((false, "")))
    }

    #[test]
    fn test_parse_string() {
        let input = "\"Foo\"";
        let result: Result<(String, &str), ParseError<&str>> = parser(parse_string).parse(input);
        assert_eq!(result, Ok(("Foo".to_string(), "")))
    }

    #[test]
    fn test_parse_char() {
        let input = "'c'";
        let result: Result<(char, &str), ParseError<&str>> = parser(parse_char).parse(input);
        assert_eq!(result, Ok(('c', "")))
    }

    #[test]
    fn test_parse_number() {
        let input = "42";
        let result: Result<(i32, &str), ParseError<&str>> = parser(parse_number).parse(input);
        assert_eq!(result, Ok((42, "")))
    }

}
<commit_msg>test atom parsing<commit_after>extern crate combine;

use std::io::prelude::*;

use combine::{between, many1, digit, letter, spaces, string, try, choice, parser, Parser, ParserExt, ParseError};
use combine::char as combine_char;
use combine::primitives::{Stream, State, ParseResult};

#[derive(Debug, PartialEq)]
enum LispVal {
    Atom(String),
    Bool(bool),
    Number(i32),
    Character(char),
    String(String)
}

fn parse_symbol<I>(input: State<I>) -> ParseResult<String, I>
where I: Stream<Item=char>
{
    let lex_char = |c| combine_char(c).skip(spaces());

    let symbols = choice([lex_char('!'), lex_char('$'), lex_char('%'), lex_char('$'), lex_char('&'),
                    lex_char('|'), lex_char('*'), lex_char('+'), lex_char('-'), lex_char(':'),
                    lex_char('<'), lex_char('='), lex_char('>'), lex_char('?'), lex_char('@'),
                    lex_char('^'), lex_char('_'), lex_char('~')]);

    symbols.map(|c: char| {
            let mut s = String::new();
            s.push(c);
            return s;
        })
        .parse_state(input)
}

fn parse_bool<I>(input: State<I>) -> ParseResult<bool, I>
where I: Stream<Item=char>
{
    let bewl = try(string("#t").map(|_| return true)).or(
            string("#f").map(|_| return false)
        )
       .parse_state(input);

    bewl
}

fn parse_atom<I>(input: State<I>) -> ParseResult<String, I>
where I: Stream<Item=char>
{
    let atom = (parser(parse_symbol::<I>))
        .parse_state(input);

    atom
}

fn parse_string<I>(input: State<I>) -> ParseResult<String, I>
where I: Stream<Item=char>
{
    let string = between(combine_char('"'), combine_char('"'), many1(letter()))
        .parse_state(input);
    string
}

fn parse_char<I>(input: State<I>) -> ParseResult<char, I>
where I: Stream<Item=char>
{
    between(combine_char('\''), combine_char('\''), letter())
        .parse_state(input)
}

fn parse_number<I>(input: State<I>) -> ParseResult<i32, I>
where I: Stream<Item=char>
{
    let number = many1(digit())
        .map(|string: String| string.parse::<i32>().unwrap())
        .parse_state(input);
    number
}

fn rusty_lisp<I>(input: State<I>) -> ParseResult<LispVal, I>
where I: Stream<Item=char>
{
    let lispval = parser(parse_atom::<I>)
                   .map(LispVal::Atom)
        .or(parser(parse_bool::<I>)
            .map(LispVal::Bool))
        .or(parser(parse_number::<I>)
            .map(LispVal::Number))
        .or(parser(parse_char::<I>)
            .map(LispVal::Character))
        .or(parser(parse_string::<I>)
            .map(LispVal::String))
        .parse_state(input);

    lispval
}

fn parse(program: String) {
    let program = program.as_ref();

    let result: Result<(LispVal, &str), ParseError<&str>> = parser(rusty_lisp).parse(program);
    match result {
        Ok((value, _remaining_input)) => println!("{:?}", value),
        Err(err) => println!("{}", err)
    }
}

fn main() {
    println!("rusty-lisp v0.1.0");
    println!("Press 'Ctrl+C' to exit");
    println!("");

    let mut stdin = std::io::stdin();
    let mut input = String::new();

    stdin.read_to_string(&mut input).unwrap();
    parse(input);

    println!("Thanks for using!");
}

#[cfg(test)]
mod tests {
    use combine::{parser, Parser, ParseError};
    use super::{parse_symbol, parse_bool, parse_char, parse_string, parse_number, parse_atom};

    #[test]
    fn test_parse_symbol() {
        let input = "!";
        let result: Result<(String, &str), ParseError<&str>> = parser(parse_symbol).parse(input);
        assert_eq!(result, Ok(("!".to_string(), "")))
    }

    #[test]
    fn test_parse_bool() {
        let input = "#t";
        let result: Result<(bool, &str), ParseError<&str>> = parser(parse_bool).parse(input);
        assert_eq!(result, Ok((true, "")));

        let input = "#f";
        let result: Result<(bool, &str), ParseError<&str>> = parser(parse_bool).parse(input);
        assert_eq!(result, Ok((false, "")))
    }

    #[test]
    fn test_parse_string() {
        let input = "\"Foo\"";
        let result: Result<(String, &str), ParseError<&str>> = parser(parse_string).parse(input);
        assert_eq!(result, Ok(("Foo".to_string(), "")))
    }

    #[test]
    fn test_parse_char() {
        let input = "'c'";
        let result: Result<(char, &str), ParseError<&str>> = parser(parse_char).parse(input);
        assert_eq!(result, Ok(('c', "")))
    }

    #[test]
    fn test_parse_number() {
        let input = "42";
        let result: Result<(i32, &str), ParseError<&str>> = parser(parse_number).parse(input);
        assert_eq!(result, Ok((42, "")))
    }

    #[test]
    fn test_parse_atom() {
        let input = "@";
        let result: Result<(String, &str), ParseError<&str>> = parser(parse_atom).parse(input);
        assert_eq!(result, Ok(("@".to_string(), "")))
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate quick_error;
extern crate serde_json;
extern crate colored;

extern crate rustfix;

use std::fs::File;
use std::io::{Read, Write};
use colored::Colorize;

const USER_OPTIONS: &'static str = "What do you want to do? \
    [r]eplace | [s]kip | save and [q]uit | [a]bort (without saving)";

fn main() {
    let program = try_main();
    match program {
        Ok(_) => std::process::exit(0),
        Err(ProgramError::UserAbort) => {
            writeln!(std::io::stdout(), "{}", ProgramError::UserAbort).unwrap();
            std::process::exit(0);
        }
        Err(error) => {
            writeln!(std::io::stderr(), "An error occured: {:#?}", error).unwrap();
            std::process::exit(1);
        }
    }
}

macro_rules! flush {
    () => (try!(std::io::stdout().flush());)
}

fn try_main() -> Result<(), ProgramError> {
    let file_name = try!(std::env::args().skip(1).next().ok_or(ProgramError::NoFile));
    let file = try!(read_file_to_string(&file_name));

    let mut accepted_suggestions: Vec<rustfix::Suggestion> = vec![];

    'diagnostics: for line in file.lines().filter(not_empty) {
        let deserialized: rustfix::diagnostics::Diagnostic = try!(serde_json::from_str(&line));
        'suggestions: for suggestion in &rustfix::collect_suggestions(&deserialized, None) {
            println!("\n\n{info}: {message}\n{arrow} \
                      {file}:{range}\n{suggestion}\n\n{text}\n\n{with}\n\n{replacement}\n",
                     info = "Info".green().bold(),
                     message = split_at_lint_name(&suggestion.message),
                     arrow = "  -->".blue().bold(),
                     suggestion = "Suggestion - Replace:".yellow().bold(),
                     file = suggestion.file_name,
                     range = suggestion.line_range,
                     text = indent(4, &reset_indent(&suggestion.text)),
                     with = "with:".yellow().bold(),
                     replacement = indent(4, &suggestion.replacement));

            'userinput: loop {
                print!("{arrow} {user_options}\n\
                    {prompt} ",
                       arrow = "==>".green().bold(),
                       prompt = "  >".green().bold(),
                       user_options = USER_OPTIONS.green());

                flush!();
                let mut input = String::new();
                try!(std::io::stdin().read_line(&mut input));

                match input.trim() {
                    "s" => {
                        println!("Skipped.");
                        continue 'suggestions;
                    }
                    "r" => {
                        accepted_suggestions.push((*suggestion).clone());
                        println!("Suggestion accepted. I'll remember that and apply it later.");
                        continue 'suggestions;
                    }
                    "q" => {
                        println!("Thanks for playing!");
                        break 'diagnostics;
                    }
                    "a" => {
                        return Err(ProgramError::UserAbort);
                    }
                    _ => {
                        println!("{error}: I didn't quite get that. {user_options}",
                                 error = "Error".red().bold(),
                                 user_options = USER_OPTIONS);
                        continue 'userinput;
                    }
                }
            }
        }

    }

    if !accepted_suggestions.is_empty() {
        println!("Good work. Let me just apply these {} changes!",
                 accepted_suggestions.len());

        for suggestion in accepted_suggestions.iter().rev() {
            try!(apply_suggestion(suggestion));

            print!(".");
            flush!();
        }

        println!("\nDone.");
    }

    println!("See you around!");

    Ok(())
}

quick_error! {
    /// All possible errors in programm lifecycle
    #[derive(Debug)]
    pub enum ProgramError {
        UserAbort {
            description("Let's get outta here!")
        }
        /// Missing File
        NoFile {
            description("No input file given")
        }
        /// Error while dealing with file or stdin/stdout
        Io(err: std::io::Error) {
            from()
            cause(err)
            description(err.description())
        }
        /// Error with deserialization
        Serde(err: serde_json::Error) {
            from()
            cause(err)
            description(err.description())
        }
    }
}

// Helpers
// -------

fn read_file_to_string(file_name: &str) -> Result<String, std::io::Error> {
    let mut file = try!(File::open(file_name));
    let mut buffer = String::new();
    try!(file.read_to_string(&mut buffer));
    Ok(buffer)
}

fn not_empty(s: &&str) -> bool {
    !s.trim().is_empty()
}

fn split_at_lint_name(s: &str) -> String {
    s.split(", #[")
        .collect::<Vec<_>>()
        .join("\n      #[") // Length of whitespace == length of "Info: "
}

fn reset_indent(s: &str) -> String {
    let leading_whitespace = s.lines()
        .nth(0)
        .unwrap_or("")
        .chars()
        .take_while(|&c| char::is_whitespace(c))
        .count();

    s.lines()
        .map(|line| String::from(&line[leading_whitespace..]))
        .collect::<Vec<_>>()
        .join("\n")
}

fn indent(size: u32, s: &str) -> String {
    let whitespace: String = std::iter::repeat(' ').take(size as usize).collect();

    s.lines()
        .map(|l| format!("{}{}", whitespace, l))
        .collect::<Vec<_>>()
        .join("\n")
}

/// Apply suggestion to a file
///
/// Please beware of ugly hacks below! Originally, I wanted to replace byte ranges, but sadly the
/// ranges rustc's JSON output gives me do not correspond to the parts of the file they are meant
/// to correspond to. So, for now, let's just replace lines!
///
/// This function is as stupid as possible. Make sure you call for the replacemnts in one file in
/// reverse order to not mess up the lines for replacements further down the road.
fn apply_suggestion(suggestion: &rustfix::Suggestion) -> Result<(), ProgramError> {
    use std::cmp::max;
    use std::iter::repeat;

    let file_content = try!(read_file_to_string(&suggestion.file_name));
    let mut new_content = String::new();

    // Add the lines before the section we want to replace
    new_content.push_str(&file_content.lines()
        .take(max(suggestion.line_range.start.line - 1, 0) as usize)
        .collect::<Vec<_>>()
        .join("\n"));
    new_content.push_str("\n");

    // Indentation
    new_content.push_str(&repeat(" ")
        .take(suggestion.line_range.start.column - 1 as usize)
        .collect::<String>());

    // TODO(killercup): Replace sections of lines only
    new_content.push_str(suggestion.replacement.trim());

    // Add the lines after the section we want to replace
    new_content.push_str("\n");
    new_content.push_str(&file_content.lines()
        .skip(suggestion.line_range.end.line as usize)
        .collect::<Vec<_>>()
        .join("\n"));
    new_content.push_str("\n");

    let mut file = try!(File::create(&suggestion.file_name));
    let new_content = new_content.as_bytes();

    try!(file.set_len(new_content.len() as u64));
    try!(file.write_all(&new_content));

    Ok(())
}
<commit_msg>Remember Trailing Semicolons<commit_after>#[macro_use]
extern crate quick_error;
extern crate serde_json;
extern crate colored;

extern crate rustfix;

use std::fs::File;
use std::io::{Read, Write};
use colored::Colorize;

const USER_OPTIONS: &'static str = "What do you want to do? \
    [r]eplace | [s]kip | save and [q]uit | [a]bort (without saving)";

fn main() {
    let program = try_main();
    match program {
        Ok(_) => std::process::exit(0),
        Err(ProgramError::UserAbort) => {
            writeln!(std::io::stdout(), "{}", ProgramError::UserAbort).unwrap();
            std::process::exit(0);
        }
        Err(error) => {
            writeln!(std::io::stderr(), "An error occured: {:#?}", error).unwrap();
            std::process::exit(1);
        }
    }
}

macro_rules! flush {
    () => (try!(std::io::stdout().flush());)
}

fn try_main() -> Result<(), ProgramError> {
    let file_name = try!(std::env::args().skip(1).next().ok_or(ProgramError::NoFile));
    let file = try!(read_file_to_string(&file_name));

    let mut accepted_suggestions: Vec<rustfix::Suggestion> = vec![];

    'diagnostics: for line in file.lines().filter(not_empty) {
        let deserialized: rustfix::diagnostics::Diagnostic = try!(serde_json::from_str(&line));
        'suggestions: for suggestion in &rustfix::collect_suggestions(&deserialized, None) {
            println!("\n\n{info}: {message}\n{arrow} \
                      {file}:{range}\n{suggestion}\n\n{text}\n\n{with}\n\n{replacement}\n",
                     info = "Info".green().bold(),
                     message = split_at_lint_name(&suggestion.message),
                     arrow = "  -->".blue().bold(),
                     suggestion = "Suggestion - Replace:".yellow().bold(),
                     file = suggestion.file_name,
                     range = suggestion.line_range,
                     text = indent(4, &reset_indent(&suggestion.text)),
                     with = "with:".yellow().bold(),
                     replacement = indent(4, &suggestion.replacement));

            'userinput: loop {
                print!("{arrow} {user_options}\n\
                    {prompt} ",
                       arrow = "==>".green().bold(),
                       prompt = "  >".green().bold(),
                       user_options = USER_OPTIONS.green());

                flush!();
                let mut input = String::new();
                try!(std::io::stdin().read_line(&mut input));

                match input.trim() {
                    "s" => {
                        println!("Skipped.");
                        continue 'suggestions;
                    }
                    "r" => {
                        accepted_suggestions.push((*suggestion).clone());
                        println!("Suggestion accepted. I'll remember that and apply it later.");
                        continue 'suggestions;
                    }
                    "q" => {
                        println!("Thanks for playing!");
                        break 'diagnostics;
                    }
                    "a" => {
                        return Err(ProgramError::UserAbort);
                    }
                    _ => {
                        println!("{error}: I didn't quite get that. {user_options}",
                                 error = "Error".red().bold(),
                                 user_options = USER_OPTIONS);
                        continue 'userinput;
                    }
                }
            }
        }

    }

    if !accepted_suggestions.is_empty() {
        println!("Good work. Let me just apply these {} changes!",
                 accepted_suggestions.len());

        for suggestion in accepted_suggestions.iter().rev() {
            try!(apply_suggestion(suggestion));

            print!(".");
            flush!();
        }

        println!("\nDone.");
    }

    println!("See you around!");

    Ok(())
}

quick_error! {
    /// All possible errors in programm lifecycle
    #[derive(Debug)]
    pub enum ProgramError {
        UserAbort {
            description("Let's get outta here!")
        }
        /// Missing File
        NoFile {
            description("No input file given")
        }
        /// Error while dealing with file or stdin/stdout
        Io(err: std::io::Error) {
            from()
            cause(err)
            description(err.description())
        }
        /// Error with deserialization
        Serde(err: serde_json::Error) {
            from()
            cause(err)
            description(err.description())
        }
    }
}

// Helpers
// -------

fn read_file_to_string(file_name: &str) -> Result<String, std::io::Error> {
    let mut file = try!(File::open(file_name));
    let mut buffer = String::new();
    try!(file.read_to_string(&mut buffer));
    Ok(buffer)
}

fn not_empty(s: &&str) -> bool {
    !s.trim().is_empty()
}

fn split_at_lint_name(s: &str) -> String {
    s.split(", #[")
        .collect::<Vec<_>>()
        .join("\n      #[") // Length of whitespace == length of "Info: "
}

fn reset_indent(s: &str) -> String {
    let leading_whitespace = s.lines()
        .nth(0)
        .unwrap_or("")
        .chars()
        .take_while(|&c| char::is_whitespace(c))
        .count();

    s.lines()
        .map(|line| String::from(&line[leading_whitespace..]))
        .collect::<Vec<_>>()
        .join("\n")
}

fn indent(size: u32, s: &str) -> String {
    let whitespace: String = std::iter::repeat(' ').take(size as usize).collect();

    s.lines()
        .map(|l| format!("{}{}", whitespace, l))
        .collect::<Vec<_>>()
        .join("\n")
}

/// Apply suggestion to a file
///
/// Please beware of ugly hacks below! Originally, I wanted to replace byte ranges, but sadly the
/// ranges rustc's JSON output gives me do not correspond to the parts of the file they are meant
/// to correspond to. So, for now, let's just replace lines!
///
/// This function is as stupid as possible. Make sure you call for the replacemnts in one file in
/// reverse order to not mess up the lines for replacements further down the road.
fn apply_suggestion(suggestion: &rustfix::Suggestion) -> Result<(), ProgramError> {
    use std::cmp::max;
    use std::iter::repeat;

    let file_content = try!(read_file_to_string(&suggestion.file_name));
    let mut new_content = String::new();

    // Add the lines before the section we want to replace
    new_content.push_str(&file_content.lines()
        .take(max(suggestion.line_range.start.line - 1, 0) as usize)
        .collect::<Vec<_>>()
        .join("\n"));

    // Some suggestions seem to currently omit the trailing semicolon
    let remember_a_semicolon = new_content.ends_with(';');

    // Indentation
    new_content.push_str("\n");
    new_content.push_str(&repeat(" ")
        .take(suggestion.line_range.start.column - 1 as usize)
        .collect::<String>());

    // TODO(killercup): Replace sections of lines only
    new_content.push_str(suggestion.replacement.trim());

    if remember_a_semicolon {
        new_content.push_str(";");
    }

    // Add the lines after the section we want to replace
    new_content.push_str("\n");
    new_content.push_str(&file_content.lines()
        .skip(suggestion.line_range.end.line as usize)
        .collect::<Vec<_>>()
        .join("\n"));
    new_content.push_str("\n");

    let mut file = try!(File::create(&suggestion.file_name));
    let new_content = new_content.as_bytes();

    try!(file.set_len(new_content.len() as u64));
    try!(file.write_all(&new_content));

    Ok(())
}
<|endoftext|>


<commit_before>extern crate arrayvec;
extern crate rand;
extern crate smallvec;
extern crate unicode_segmentation;

mod rope;
mod iter;
mod small_string;
mod small_string_utils;

use rope::Rope;
use rand::Rng;

fn main() {
    let mut rng = rand::thread_rng();

    let mut tree = Rope::new();

    for _ in 0..16 {
        use rope::Count;
        let len = tree.char_count().max(1);
        tree.insert(rng.gen::<Count>() % len, "Hello ");
        tree.insert(rng.gen::<Count>() % len, "world! ");
        tree.insert(rng.gen::<Count>() % len, "How are ");
        tree.insert(rng.gen::<Count>() % len, "you ");
        tree.insert(rng.gen::<Count>() % len, "doing?\n");
        tree.insert(rng.gen::<Count>() % len, "Let's ");
        tree.insert(rng.gen::<Count>() % len, "keep ");
        tree.insert(rng.gen::<Count>() % len, "inserting ");
        tree.insert(rng.gen::<Count>() % len, "more ");
        tree.insert(rng.gen::<Count>() % len, "items. ");
        tree.insert(rng.gen::<Count>() % len, "こんいちは、");
        tree.insert(rng.gen::<Count>() % len, "みんなさん！");
    }

    // println!("{:#?}", tree);
    println!("{}", tree.to_string());
}

#[cfg(test)]
mod tests {
    use rope::Rope;

    #[test]
    fn insert_01() {
        let mut r = Rope::new();
        r.insert(0, "Hello world!");
        r.insert(3, "zopter");

        assert_eq!("Helzopterlo world!", &r.to_string());
    }

    #[test]
    fn insert_02() {
        let mut r = Rope::new();
        r.insert(0, "Hello world!");
        r.insert(0, "zopter");

        assert_eq!("zopterHello world!", &r.to_string());
    }

    #[test]
    fn insert_03() {
        let mut r = Rope::new();
        r.insert(0, "Hello world!");
        r.insert(12, "zopter");

        assert_eq!("Hello world!zopter", &r.to_string());
    }

    #[test]
    fn insert_04() {
        let mut r = Rope::new();
        r.insert(0, "He");
        r.insert(2, "l");
        r.insert(3, "l");
        r.insert(4, "o w");
        r.insert(7, "o");
        r.insert(8, "rl");
        r.insert(10, "d!");
        r.insert(3, "zopter");

        assert_eq!("Helzopterlo world!", &r.to_string());
    }
}
<commit_msg>Added random vs deterministic main() switch for easier testing.<commit_after>extern crate arrayvec;
extern crate rand;
extern crate smallvec;
extern crate unicode_segmentation;

mod rope;
mod iter;
mod small_string;
mod small_string_utils;

use rope::Rope;
use rand::Rng;

const DO_RAND: bool = false;

fn main() {
    let mut rng = rand::thread_rng();

    let mut tree = Rope::new();

    for _ in 0..16 {
        use rope::Count;
        if DO_RAND {
            let len = tree.char_count().max(1);
            tree.insert(rng.gen::<Count>() % len, "Hello ");
            tree.insert(rng.gen::<Count>() % len, "world! ");
            tree.insert(rng.gen::<Count>() % len, "How are ");
            tree.insert(rng.gen::<Count>() % len, "you ");
            tree.insert(rng.gen::<Count>() % len, "doing?\r\n");
            tree.insert(rng.gen::<Count>() % len, "Let's ");
            tree.insert(rng.gen::<Count>() % len, "keep ");
            tree.insert(rng.gen::<Count>() % len, "inserting ");
            tree.insert(rng.gen::<Count>() % len, "more ");
            tree.insert(rng.gen::<Count>() % len, "items.\r\n");
            tree.insert(rng.gen::<Count>() % len, "こんいちは、");
            tree.insert(rng.gen::<Count>() % len, "みんなさん！");
        } else {
            let len = tree.char_count().max(1);
            tree.insert(1298809 % len, "Hello world! How are you doing?\r\n");
            let len = tree.char_count().max(1);
            tree.insert(1298809 % len, "Let's keep inserting more items.\r\n");
            let len = tree.char_count().max(1);
            tree.insert(1298809 % len, "こんいちは、みんなさん！");
        }
    }

    // println!("{:#?}", tree);
    println!("{}", tree.to_string());
}

#[cfg(test)]
mod tests {
    use rope::Rope;

    #[test]
    fn insert_01() {
        let mut r = Rope::new();
        r.insert(0, "Hello world!");
        r.insert(3, "zopter");

        assert_eq!("Helzopterlo world!", &r.to_string());
    }

    #[test]
    fn insert_02() {
        let mut r = Rope::new();
        r.insert(0, "Hello world!");
        r.insert(0, "zopter");

        assert_eq!("zopterHello world!", &r.to_string());
    }

    #[test]
    fn insert_03() {
        let mut r = Rope::new();
        r.insert(0, "Hello world!");
        r.insert(12, "zopter");

        assert_eq!("Hello world!zopter", &r.to_string());
    }

    #[test]
    fn insert_04() {
        let mut r = Rope::new();
        r.insert(0, "He");
        r.insert(2, "l");
        r.insert(3, "l");
        r.insert(4, "o w");
        r.insert(7, "o");
        r.insert(8, "rl");
        r.insert(10, "d!");
        r.insert(3, "zopter");

        assert_eq!("Helzopterlo world!", &r.to_string());
    }
}
<|endoftext|>


<commit_before>//
// gash.rs
//
// Starting code for PS2
// Running on Rust 0.9
//
// University of Virginia - cs4414 Spring 2014
// Weilin Xu, David Evans
// Version 0.4
//

extern crate getopts;

use getopts::{optopt, getopts};
use std::io::BufferedReader;
use std::io::Command;
use std::io::stdin;
use std::{io, os};
use std::str;

struct Shell<'a> {
    cmd_prompt: &'a str,
}

impl <'a>Shell<'a> {
    fn new(prompt_str: &'a str) -> Shell<'a> {
        Shell { cmd_prompt: prompt_str }
    }

    fn run(&self) {
        let mut stdin = BufferedReader::new(stdin());

        loop {
            io::stdio::print(self.cmd_prompt.as_slice());
            io::stdio::flush();

            let line = stdin.read_line().unwrap();
            let cmd_line = line.trim();
            let program = cmd_line.splitn(1, ' ').nth(0).expect("no program");

            match program {
                ""      =>  { continue; }
                "exit"  =>  { return; }
                _       =>  { self.run_cmdline(cmd_line); }
            }
        }
    }

    fn run_cmdline(&self, cmd_line: &str) {
        let argv: Vec<&str> = cmd_line.split(' ').filter_map(|x| {
            if x == "" {
                None
            } else {
                Some(x)
            }
        }).collect();

        match argv.head() {
            Some(&program) => self.run_cmd(program, argv.tail()),
            None => (),
        };
    }

    fn run_cmd(&self, program: &str, argv: &[&str]) {
        if self.cmd_exists(program) {
            io::stdio::print(str::from_utf8(Command::new(program).args(argv).output().unwrap().output.as_slice()).unwrap());
        } else {
            println!("{}: command not found", program);
        }
    }

    fn cmd_exists(&self, cmd_path: &str) -> bool {
        Command::new("which").arg(cmd_path).status().unwrap().success()
    }
}

fn get_cmdline_from_args() -> Option<String> {
    /* Begin processing program arguments and initiate the parameters. */
    let args = os::args();

    let opts = &[
        getopts::optopt("c", "", "", "")
    ];

    getopts::getopts(args.tail(), opts).unwrap().opt_str("c")
}

fn main() {
    let opt_cmd_line = get_cmdline_from_args();

    match opt_cmd_line {
        Some(cmd_line) => Shell::new("").run_cmdline(cmd_line.as_slice()),
        None           => Shell::new("gash > ").run(),
    }
}
<commit_msg>update deprecated call<commit_after>//
// gash.rs
//
// Starting code for PS2
// Running on Rust 0.9
//
// University of Virginia - cs4414 Spring 2014
// Weilin Xu, David Evans
// Version 0.4
//

extern crate getopts;

use getopts::{optopt, getopts};
use std::io::BufferedReader;
use std::io::Command;
use std::io::stdin;
use std::{io, os};
use std::str;

struct Shell<'a> {
    cmd_prompt: &'a str,
}

impl <'a>Shell<'a> {
    fn new(prompt_str: &'a str) -> Shell<'a> {
        Shell { cmd_prompt: prompt_str }
    }

    fn run(&self) {
        let mut stdin = BufferedReader::new(stdin());

        loop {
            io::stdio::print(self.cmd_prompt.as_slice());
            io::stdio::flush();

            let line = stdin.read_line().unwrap();
            let cmd_line = line.trim();
            let program = cmd_line.splitn(1, ' ').nth(0).expect("no program");

            match program {
                ""      =>  { continue; }
                "exit"  =>  { return; }
                _       =>  { self.run_cmdline(cmd_line); }
            }
        }
    }

    fn run_cmdline(&self, cmd_line: &str) {
        let argv: Vec<&str> = cmd_line.split(' ').filter_map(|x| {
            if x == "" {
                None
            } else {
                Some(x)
            }
        }).collect();

        match argv.first() {
            Some(&program) => self.run_cmd(program, argv.tail()),
            None => (),
        };
    }

    fn run_cmd(&self, program: &str, argv: &[&str]) {
        if self.cmd_exists(program) {
            io::stdio::print(str::from_utf8(Command::new(program).args(argv).output().unwrap().output.as_slice()).unwrap());
        } else {
            println!("{}: command not found", program);
        }
    }

    fn cmd_exists(&self, cmd_path: &str) -> bool {
        Command::new("which").arg(cmd_path).status().unwrap().success()
    }
}

fn get_cmdline_from_args() -> Option<String> {
    /* Begin processing program arguments and initiate the parameters. */
    let args = os::args();

    let opts = &[
        getopts::optopt("c", "", "", "")
    ];

    getopts::getopts(args.tail(), opts).unwrap().opt_str("c")
}

fn main() {
    let opt_cmd_line = get_cmdline_from_args();

    match opt_cmd_line {
        Some(cmd_line) => Shell::new("").run_cmdline(cmd_line.as_slice()),
        None           => Shell::new("gash > ").run(),
    }
}
<|endoftext|>


<commit_before>// error-pattern:yummy
#![feature(box_syntax)]
#![feature(rustc_private)]

extern crate rustc_driver;
extern crate getopts;
extern crate rustc;
extern crate syntax;
extern crate rustc_plugin;
extern crate clippy_lints;
extern crate rustc_serialize;

use rustc_driver::{driver, CompilerCalls, RustcDefaultCalls, Compilation};
use rustc::session::{config, Session};
use rustc::session::config::{Input, ErrorOutputType};
use syntax::diagnostics;
use std::path::PathBuf;
use std::process::Command;

mod cargo;

struct ClippyCompilerCalls(RustcDefaultCalls);

impl std::default::Default for ClippyCompilerCalls {
    fn default() -> Self {
        Self::new()
    }
}

impl ClippyCompilerCalls {
    fn new() -> Self {
        ClippyCompilerCalls(RustcDefaultCalls)
    }
}

impl<'a> CompilerCalls<'a> for ClippyCompilerCalls {
    fn early_callback(&mut self,
                      matches: &getopts::Matches,
                      sopts: &config::Options,
                      descriptions: &diagnostics::registry::Registry,
                      output: ErrorOutputType)
                      -> Compilation {
        self.0.early_callback(matches, sopts, descriptions, output)
    }
    fn no_input(&mut self,
                matches: &getopts::Matches,
                sopts: &config::Options,
                odir: &Option<PathBuf>,
                ofile: &Option<PathBuf>,
                descriptions: &diagnostics::registry::Registry)
                -> Option<(Input, Option<PathBuf>)> {
        self.0.no_input(matches, sopts, odir, ofile, descriptions)
    }
    fn late_callback(&mut self,
                     matches: &getopts::Matches,
                     sess: &Session,
                     input: &Input,
                     odir: &Option<PathBuf>,
                     ofile: &Option<PathBuf>)
                     -> Compilation {
        self.0.late_callback(matches, sess, input, odir, ofile)
    }
    fn build_controller(&mut self, sess: &Session, matches: &getopts::Matches) -> driver::CompileController<'a> {
        let mut control = self.0.build_controller(sess, matches);

        let old = std::mem::replace(&mut control.after_parse.callback, box |_| {});
        control.after_parse.callback = Box::new(move |state| {
            {
                let mut registry = rustc_plugin::registry::Registry::new(state.session, state.krate.as_ref().expect("at this compilation stage the krate must be parsed"));
                registry.args_hidden = Some(Vec::new());
                clippy_lints::register_plugins(&mut registry);

                let rustc_plugin::registry::Registry { early_lint_passes, late_lint_passes, lint_groups, llvm_passes, attributes, mir_passes, .. } = registry;
                let sess = &state.session;
                let mut ls = sess.lint_store.borrow_mut();
                for pass in early_lint_passes {
                    ls.register_early_pass(Some(sess), true, pass);
                }
                for pass in late_lint_passes {
                    ls.register_late_pass(Some(sess), true, pass);
                }

                for (name, to) in lint_groups {
                    ls.register_group(Some(sess), true, name, to);
                }

                sess.plugin_llvm_passes.borrow_mut().extend(llvm_passes);
                sess.mir_passes.borrow_mut().extend(mir_passes);
                sess.plugin_attributes.borrow_mut().extend(attributes);
            }
            old(state);
        });

        control
    }
}

use std::path::Path;

pub fn main() {
    use std::env;

    if env::var("CLIPPY_DOGFOOD").map(|_| true).unwrap_or(false) {
        panic!("yummy");
    }

    let dep_path = env::current_dir().expect("current dir is not readable").join("target").join("debug").join("deps");

    let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME"));
    let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN"));
    let sys_root = match (home, toolchain) {
        (Some(home), Some(toolchain)) => format!("{}/toolchains/{}", home, toolchain),
        _ => option_env!("SYSROOT").map(|s| s.to_owned())
                                   .or(Command::new("rustc").arg("--print")
                                                            .arg("sysroot")
                                                            .output().ok()
                                                            .and_then(|out| String::from_utf8(out.stdout).ok())
                                                            .map(|s| s.trim().to_owned())
                                                            )
                .expect("need to specify SYSROOT env var during clippy compilation, or use rustup or multirust"),
    };

    if let Some("clippy") = std::env::args().nth(1).as_ref().map(AsRef::as_ref) {
        let output = std::process::Command::new("cargo").args(&["metadata", "--no-deps"]).output().expect("could not run `cargo metadata`");
        let stdout = std::str::from_utf8(&output.stdout).expect("`cargo metadata` output is not utf8");
        let mut metadata: cargo::Metadata = rustc_serialize::json::decode(stdout).expect("`cargo metadata` output is not valid json");
        assert_eq!(metadata.version, 1);
        for target in metadata.packages.remove(0).targets {
            let args = std::env::args().skip(2);
            assert_eq!(target.kind.len(), 1);
            match target.kind[0] {
                cargo::Kind::dylib => process(std::iter::once("--lib".to_owned()).chain(args), &dep_path, &sys_root),
                cargo::Kind::bin => process(vec!["--bin".to_owned(), target.name].into_iter().chain(args), &dep_path, &sys_root),
                // don't process tests
                _ => {},
            }
        }
    } else {
        let args: Vec<String> = if env::args().any(|s| s == "--sysroot") {
            env::args().collect()
        } else {
            env::args().chain(Some("--sysroot".to_owned())).chain(Some(sys_root)).collect()
        };
        let (result, _) = rustc_driver::run_compiler(&args, &mut ClippyCompilerCalls::new());

        if let Err(err_count) = result {
            if err_count > 0 {
                std::process::exit(1);
            }
        }
    }
}

fn process<P, I>(old_args: I, dep_path: P, sysroot: &str)
    where P: AsRef<Path>, I: Iterator<Item=String> {

    let mut args = vec!["rustc".to_owned()];

    let mut found_dashes = false;
    for arg in old_args {
        found_dashes |= arg == "--";
        args.push(arg);
    }
    if !found_dashes {
        args.push("--".to_owned());
    }
    args.push("-L".to_owned());
    args.push(dep_path.as_ref().to_string_lossy().into_owned());
    args.push(String::from("--sysroot"));
    args.push(sysroot.to_owned());
    args.push("-Zno-trans".to_owned());

    let path = std::env::current_exe().expect("current executable path invalid");
    let exit_status = std::process::Command::new("cargo")
        .args(&args)
        .env("RUSTC", path)
        .spawn().expect("could not run cargo")
        .wait().expect("failed to wait for cargo?");

    if let Some(code) = exit_status.code() {
        std::process::exit(code);
    }
}
<commit_msg>dogfood<commit_after>// error-pattern:yummy
#![feature(box_syntax)]
#![feature(rustc_private)]
#![feature(slice_patterns)]

extern crate rustc_driver;
extern crate getopts;
extern crate rustc;
extern crate syntax;
extern crate rustc_plugin;
extern crate clippy_lints;
extern crate rustc_serialize;

use rustc_driver::{driver, CompilerCalls, RustcDefaultCalls, Compilation};
use rustc::session::{config, Session};
use rustc::session::config::{Input, ErrorOutputType};
use syntax::diagnostics;
use std::path::PathBuf;
use std::process::Command;

mod cargo;

struct ClippyCompilerCalls(RustcDefaultCalls);

impl std::default::Default for ClippyCompilerCalls {
    fn default() -> Self {
        Self::new()
    }
}

impl ClippyCompilerCalls {
    fn new() -> Self {
        ClippyCompilerCalls(RustcDefaultCalls)
    }
}

impl<'a> CompilerCalls<'a> for ClippyCompilerCalls {
    fn early_callback(&mut self,
                      matches: &getopts::Matches,
                      sopts: &config::Options,
                      descriptions: &diagnostics::registry::Registry,
                      output: ErrorOutputType)
                      -> Compilation {
        self.0.early_callback(matches, sopts, descriptions, output)
    }
    fn no_input(&mut self,
                matches: &getopts::Matches,
                sopts: &config::Options,
                odir: &Option<PathBuf>,
                ofile: &Option<PathBuf>,
                descriptions: &diagnostics::registry::Registry)
                -> Option<(Input, Option<PathBuf>)> {
        self.0.no_input(matches, sopts, odir, ofile, descriptions)
    }
    fn late_callback(&mut self,
                     matches: &getopts::Matches,
                     sess: &Session,
                     input: &Input,
                     odir: &Option<PathBuf>,
                     ofile: &Option<PathBuf>)
                     -> Compilation {
        self.0.late_callback(matches, sess, input, odir, ofile)
    }
    fn build_controller(&mut self, sess: &Session, matches: &getopts::Matches) -> driver::CompileController<'a> {
        let mut control = self.0.build_controller(sess, matches);

        let old = std::mem::replace(&mut control.after_parse.callback, box |_| {});
        control.after_parse.callback = Box::new(move |state| {
            {
                let mut registry = rustc_plugin::registry::Registry::new(state.session, state.krate.as_ref().expect("at this compilation stage the krate must be parsed"));
                registry.args_hidden = Some(Vec::new());
                clippy_lints::register_plugins(&mut registry);

                let rustc_plugin::registry::Registry { early_lint_passes, late_lint_passes, lint_groups, llvm_passes, attributes, mir_passes, .. } = registry;
                let sess = &state.session;
                let mut ls = sess.lint_store.borrow_mut();
                for pass in early_lint_passes {
                    ls.register_early_pass(Some(sess), true, pass);
                }
                for pass in late_lint_passes {
                    ls.register_late_pass(Some(sess), true, pass);
                }

                for (name, to) in lint_groups {
                    ls.register_group(Some(sess), true, name, to);
                }

                sess.plugin_llvm_passes.borrow_mut().extend(llvm_passes);
                sess.mir_passes.borrow_mut().extend(mir_passes);
                sess.plugin_attributes.borrow_mut().extend(attributes);
            }
            old(state);
        });

        control
    }
}

use std::path::Path;

pub fn main() {
    use std::env;

    if env::var("CLIPPY_DOGFOOD").map(|_| true).unwrap_or(false) {
        panic!("yummy");
    }

    let dep_path = env::current_dir().expect("current dir is not readable").join("target").join("debug").join("deps");

    let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME"));
    let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN"));
    let sys_root = match (home, toolchain) {
        (Some(home), Some(toolchain)) => format!("{}/toolchains/{}", home, toolchain),
        _ => option_env!("SYSROOT").map(|s| s.to_owned())
                                   .or(Command::new("rustc").arg("--print")
                                                            .arg("sysroot")
                                                            .output().ok()
                                                            .and_then(|out| String::from_utf8(out.stdout).ok())
                                                            .map(|s| s.trim().to_owned())
                                                            )
                .expect("need to specify SYSROOT env var during clippy compilation, or use rustup or multirust"),
    };

    if let Some("clippy") = std::env::args().nth(1).as_ref().map(AsRef::as_ref) {
        let output = std::process::Command::new("cargo").args(&["metadata", "--no-deps"]).output().expect("could not run `cargo metadata`");
        let stdout = std::str::from_utf8(&output.stdout).expect("`cargo metadata` output is not utf8");
        let mut metadata: cargo::Metadata = rustc_serialize::json::decode(stdout).expect("`cargo metadata` output is not valid json");
        assert_eq!(metadata.version, 1);
        for target in metadata.packages.remove(0).targets {
            let args = std::env::args().skip(2);
            assert_eq!(target.kind.len(), 1);
            match &target.kind[..] {
                [cargo::Kind::lib] |
                [cargo::Kind::dylib] => process(std::iter::once("--lib".to_owned()).chain(args), &dep_path, &sys_root),
                [cargo::Kind::bin] => process(vec!["--bin".to_owned(), target.name].into_iter().chain(args), &dep_path, &sys_root),
                // don't process tests and other stuff
                _ => {},
            }
        }
    } else {
        let args: Vec<String> = if env::args().any(|s| s == "--sysroot") {
            env::args().collect()
        } else {
            env::args().chain(Some("--sysroot".to_owned())).chain(Some(sys_root)).collect()
        };
        let (result, _) = rustc_driver::run_compiler(&args, &mut ClippyCompilerCalls::new());

        if let Err(err_count) = result {
            if err_count > 0 {
                std::process::exit(1);
            }
        }
    }
}

fn process<P, I>(old_args: I, dep_path: P, sysroot: &str)
    where P: AsRef<Path>, I: Iterator<Item=String> {

    let mut args = vec!["rustc".to_owned()];

    let mut found_dashes = false;
    for arg in old_args {
        found_dashes |= arg == "--";
        args.push(arg);
    }
    if !found_dashes {
        args.push("--".to_owned());
    }
    args.push("-L".to_owned());
    args.push(dep_path.as_ref().to_string_lossy().into_owned());
    args.push(String::from("--sysroot"));
    args.push(sysroot.to_owned());
    args.push("-Zno-trans".to_owned());

    let path = std::env::current_exe().expect("current executable path invalid");
    let exit_status = std::process::Command::new("cargo")
        .args(&args)
        .env("RUSTC", path)
        .spawn().expect("could not run cargo")
        .wait().expect("failed to wait for cargo?");

    if let Some(code) = exit_status.code() {
        std::process::exit(code);
    }
}
<|endoftext|>