<commit_before>extern crate ansi_term;
#[macro_use]
extern crate clap;
#[macro_use]
extern crate error_chain;
extern crate flexi_logger;
extern crate futures;
#[macro_use]
extern crate log;
extern crate mhost;
extern crate tokio_core;
extern crate trust_dns;

use mhost::{Query, Response, multiple_lookup};
use mhost::lookup::{self, Result as LookupResult};
use mhost::get;
use mhost::output::{self, OutputModule};

use ansi_term::Colour;
use clap::{App, Arg, ArgMatches, Shell};
use flexi_logger::{Logger, LogRecord};
use futures::Future;
use log::LogLevel;
use std::net::IpAddr;
use std::str::FromStr;
use std::time::Duration;
use tokio_core::reactor::Core;
use trust_dns::rr::RecordType;

fn run() -> Result<()> {
    let args = build_cli().get_matches();

    init_looger(args.occurrences_of("verbosity"))
        .start()
        .unwrap_or_else(|e| { panic!("Logger initialization failed with {}", e) });

    if args.is_present("completions") {
        return generate_completion(&args);
    }

    let (query, server_limit) = parse_args(&args)?;
    let responses = run_lookup(&args, query, server_limit);
    output(&responses, args.values_of_lossy("output modules").unwrap())?;

    Ok(())
}

fn build_cli() -> App<'static, 'static> {
    let name = env!("CARGO_PKG_NAME");
    let version = env!("CARGO_PKG_VERSION");
    let about = env!("CARGO_PKG_DESCRIPTION");

    App::new(name)
        .version(version)
        .about(about)
        .arg(
            Arg::with_name("domain name")
                .index(1)
                .required(true)
                .conflicts_with("completions")
                .help("domain name or IP address to look up")
        )
        .arg(
            Arg::with_name("timeout")
                .long("timeout")
                .default_value("5")
                .help("Sets timeout for server responses in sec")
                .takes_value(true)
        )
        .arg(
            Arg::with_name("record types")
                .long("type")
                .short("t")
                .takes_value(true)
                .multiple(true)
                .require_delimiter(true)
                .possible_values(
                    &[
                        "a",
                        "aaaa",
                        "any",
                        "cname",
                        "dnskey",
                        "mx",
                        "ns",
                        "opt",
                        "ptr",
                        "soa",
                        "srv",
                        "txt",
                    ],
                )
                .help("Selects resource record type [default: a, aaaa, mx]")
        )
        .arg(
            Arg::with_name("dont use local dns servers")
                .short("L")
                .help("Ignore local DNS servers from /etc/resolv.conf")
        )
        .arg(
            Arg::with_name("dont use local search domain")
                .short("S")
                .help("Ignore local search domains from /etc/resolv.conf")
        )
        .arg(Arg::with_name("predefined server").short("p").help(
            "Uses predefined DNS servers set"
        ))
        .arg(
            Arg::with_name("DNS servers")
                .long("server")
                .short("s")
                .takes_value(true)
                .multiple(true)
                .require_delimiter(true)
                .number_of_values(1)
                .help(
                    "Sets DNS servers to use"
                )
        )
        .arg(
            Arg::with_name("ungefiltert ids")
                .short("u")
                .takes_value(true)
                .multiple(true)
                .require_delimiter(true)
                .help("Retrieves DNS servers from https://www.ungefiltert-surfen.de/ for country ids, e.g., 'de'")
        )
        .arg(
            Arg::with_name("limit")
                .long("limit")
                .short("l")
                .default_value("100")
                .help("Limits the amount of servers to query")
                .takes_value(true)
        )
        .arg(
            Arg::with_name("output modules")
                .long("module")
                .short("m")
                .takes_value(true)
                .multiple(true)
                .require_delimiter(true)
                .default_value("summary")
                .possible_values(
                    &[
                        "summary",
                        "details",
                    ],
                )
                .help("Selects output module")
        )
        .arg(Arg::with_name("verbosity")
            .short("v")
            .multiple(true)
            .help("Sets level of verbosity")
        )
        .arg(
            Arg::with_name("completions")
                .long("completions")
                .takes_value(true)
                .hidden(true)
                .possible_values(&["bash", "fish", "zsh"])
                .help("The shell to generate the script for")
        )
}

fn init_looger(verbosity_level: u64) -> Logger {
    fn colour(l: LogLevel) -> Colour {
        match l {
            LogLevel::Error => Colour::Red,
            LogLevel::Warn => Colour::Yellow,
            LogLevel::Info => Colour::Blue,
            _ => Colour::White,
        }
    }

    fn log_format_info(r: &LogRecord) -> String {
        format!("{}", colour(r.level()).paint(
            format!("{}: {}",
                    r.location().module_path(),
                    &r.args()
            )
        ))
    }

    fn log_format_debug(r: &LogRecord) -> String {
        format!("{}", colour(r.level()).paint(
            format!("{}:{}:{}: {}",
                    r.location().module_path(),
                    r.location().file(),
                    r.location().line(),
                    &r.args()
            )
        ))
    }

    let (log_spec, log_format): (String, fn(&LogRecord) -> String) = match std::env::var("RUST_LOG") {
        Ok(spec) => (spec, log_format_debug),
        Err(_) if verbosity_level == 1 => ("mhost=info".to_string(), log_format_info),
        Err(_) if verbosity_level == 2 => ("mhost=debug".to_string(), log_format_debug),
        Err(_) if verbosity_level > 2 => ("mhost=trace".to_string(), log_format_debug),
        Err(_) => ("mhost=warn".to_string(), log_format_info),
    };

    Logger::with_str(&log_spec).format(log_format)
}

fn generate_completion(args: &ArgMatches) -> Result<()> {
    let bin_name = env!("CARGO_PKG_NAME");
    let shell = args.value_of("completions").ok_or(
        ErrorKind::CliArgsParsingError,
    )?;
    build_cli().gen_completions_to(
        bin_name,
        shell.parse::<Shell>().unwrap(),
        &mut std::io::stdout(),
    );
    Ok(())
}

fn parse_args(args: &ArgMatches) -> Result<(Query, usize)> {
    let timeout = value_t!(args, "timeout", u64)
        .map(Duration::from_secs)
        .unwrap();

    let domain_name = if args.is_present("dont use local search domain") {
        args.value_of("domain name").unwrap().to_string()
    } else {
        let cfg = get::resolv_conf()?;
        let domain_name_from_args = args.value_of("domain name").unwrap();
        if domain_name_from_args.ends_with(".") {
            domain_name_from_args.to_string()
        } else if cfg.search.len() > 0 && domain_name_from_args.matches(".").count() >= cfg.ndots as usize {
            format!("{}.", domain_name_from_args)
        } else {
            format!("{}.{}.", domain_name_from_args, cfg.search.first().unwrap())
        }
    };
    debug!("domain_name: '{}'", domain_name);

    let query = match IpAddr::from_str(&domain_name) {
        Ok(ip) => Query::from(ip, vec![RecordType::PTR]),
        Err(_) => {
            let record_types = get::record_types(args.values_of_lossy("record types"))
                .chain_err(|| ErrorKind::CliArgsParsingError)?;
            Query::new(&domain_name, record_types)
        }
    }.set_timeout(timeout);
    trace!("{:?}", query);

    let server_limit = value_t!(args, "limit", usize).unwrap();

    Ok((query, server_limit))
}

// TODO: make this a Result
fn run_lookup(args: &ArgMatches, query: Query, server_limit: usize) -> Vec<LookupResult<Response>> {
    let mut io_loop = Core::new().unwrap();
    let handle = io_loop.handle();
    let lookup = get::dns_servers(
        &handle,
        args.values_of_lossy("DNS servers"),
        args.is_present("predefined server"),
        args.is_present("dont use local dns servers"),
        args.values_of_lossy("ungefiltert ids"),
    )
        .map_err(|e| e.into())
        .and_then(|servers| {
            debug!("DNS Server set: {:?}", servers);
            let dns_endpoints = servers
                .into_iter()
                .map(|s| (s, 53))
                .take(server_limit)
                .collect();
            multiple_lookup(&handle, query, dns_endpoints).map_err(
                |_| {
                    Error::from_kind(ErrorKind::LookupFailed)
                },
            )
        });
    let result = io_loop.run(lookup);

    result.unwrap()
}

fn output(responses: &[LookupResult<Response>], outputs: Vec<String>) -> Result<()> {
    let mut writer = std::io::stdout();
    for output in outputs {
        match output.as_ref() {
            "details" => output::DetailsOutput::new(responses).output(&mut writer)?,
            _ => output::SummaryOutput::new(responses).output(&mut writer)?,
        }
    }

    Ok(())
}

error_chain! {
    errors {
        CliArgsParsingError {
            description("failed to parse CLI arguments")
            display("failed to parse CLI arguments")
        }

        LookupFailed {
            description("failed to run lookup")
            display("failed to run lookup")
        }
    }

    links {
        Get(get::Error, get::ErrorKind);
        Lookup(lookup::Error, lookup::ErrorKind);
        Output(output::Error, output::ErrorKind);
    }
}

quick_main!(run);
<commit_msg>Hotfix for Clap issue #1056<commit_after>extern crate ansi_term;
#[macro_use]
extern crate clap;
#[macro_use]
extern crate error_chain;
extern crate flexi_logger;
extern crate futures;
#[macro_use]
extern crate log;
extern crate mhost;
extern crate tokio_core;
extern crate trust_dns;

use mhost::{Query, Response, multiple_lookup};
use mhost::lookup::{self, Result as LookupResult};
use mhost::get;
use mhost::output::{self, OutputModule};

use ansi_term::Colour;
use clap::{App, Arg, ArgMatches, Shell};
use flexi_logger::{Logger, LogRecord};
use futures::Future;
use log::LogLevel;
use std::net::IpAddr;
use std::str::FromStr;
use std::time::Duration;
use tokio_core::reactor::Core;
use trust_dns::rr::RecordType;

fn run() -> Result<()> {
    let args = build_cli().get_matches();

    init_looger(args.occurrences_of("verbosity"))
        .start()
        .unwrap_or_else(|e| { panic!("Logger initialization failed with {}", e) });

    if args.is_present("completions") {
        return generate_completion(&args);
    }

    let (query, server_limit) = parse_args(&args)?;
    let responses = run_lookup(&args, query, server_limit);
    // TODO: https://github.com/kbknapp/clap-rs/issues/1056
    let output_modules = match args.values_of_lossy("output modules") {
        Some(v) => v,
        None => vec!["summar".to_string()],
    };
    output(&responses, output_modules)?;

    Ok(())
}

fn build_cli() -> App<'static, 'static> {
    let name = env!("CARGO_PKG_NAME");
    let version = env!("CARGO_PKG_VERSION");
    let about = env!("CARGO_PKG_DESCRIPTION");

    App::new(name)
        .version(version)
        .about(about)
        .arg(
            Arg::with_name("domain name")
                .index(1)
                .required(true)
                .conflicts_with("completions")
                .help("domain name or IP address to look up")
        )
        .arg(
            Arg::with_name("timeout")
                .long("timeout")
                .default_value("5")
                .help("Sets timeout for server responses in sec")
                .takes_value(true)
        )
        .arg(
            Arg::with_name("record types")
                .long("type")
                .short("t")
                .takes_value(true)
                .multiple(true)
                .require_delimiter(true)
                .possible_values(
                    &[
                        "a",
                        "aaaa",
                        "any",
                        "cname",
                        "dnskey",
                        "mx",
                        "ns",
                        "opt",
                        "ptr",
                        "soa",
                        "srv",
                        "txt",
                    ],
                )
                .help("Selects resource record type [default: a, aaaa, mx]")
        )
        .arg(
            Arg::with_name("dont use local dns servers")
                .short("L")
                .help("Ignore local DNS servers from /etc/resolv.conf")
        )
        .arg(
            Arg::with_name("dont use local search domain")
                .short("S")
                .help("Ignore local search domains from /etc/resolv.conf")
        )
        .arg(Arg::with_name("predefined server").short("p").help(
            "Uses predefined DNS servers set"
        ))
        .arg(
            Arg::with_name("DNS servers")
                .long("server")
                .short("s")
                .takes_value(true)
                .multiple(true)
                .require_delimiter(true)
                .number_of_values(1)
                .help(
                    "Sets DNS servers to use"
                )
        )
        .arg(
            Arg::with_name("ungefiltert ids")
                .short("u")
                .takes_value(true)
                .multiple(true)
                .require_delimiter(true)
                .help("Retrieves DNS servers from https://www.ungefiltert-surfen.de/ for country ids, e.g., 'de'")
        )
        .arg(
            Arg::with_name("limit")
                .long("limit")
                .short("l")
                .default_value("100")
                .help("Limits the amount of servers to query")
                .takes_value(true)
        )
        .arg(
            Arg::with_name("output modules")
                .long("module")
                .short("m")
                .takes_value(true)
                .multiple(true)
                .require_delimiter(true)
                // TODO: https://github.com/kbknapp/clap-rs/issues/1056: .default_value("summary")
                .possible_values(
                    &[
                        "summary",
                        "details",
                    ],
                )
                .help("Selects output module")
        )
        .arg(Arg::with_name("verbosity")
            .short("v")
            .multiple(true)
            .help("Sets level of verbosity")
        )
        .arg(
            Arg::with_name("completions")
                .long("completions")
                .takes_value(true)
                .hidden(true)
                .possible_values(&["bash", "fish", "zsh"])
                .help("The shell to generate the script for")
        )
}

fn init_looger(verbosity_level: u64) -> Logger {
    fn colour(l: LogLevel) -> Colour {
        match l {
            LogLevel::Error => Colour::Red,
            LogLevel::Warn => Colour::Yellow,
            LogLevel::Info => Colour::Blue,
            _ => Colour::White,
        }
    }

    fn log_format_info(r: &LogRecord) -> String {
        format!("{}", colour(r.level()).paint(
            format!("{}: {}",
                    r.location().module_path(),
                    &r.args()
            )
        ))
    }

    fn log_format_debug(r: &LogRecord) -> String {
        format!("{}", colour(r.level()).paint(
            format!("{}:{}:{}: {}",
                    r.location().module_path(),
                    r.location().file(),
                    r.location().line(),
                    &r.args()
            )
        ))
    }

    let (log_spec, log_format): (String, fn(&LogRecord) -> String) = match std::env::var("RUST_LOG") {
        Ok(spec) => (spec, log_format_debug),
        Err(_) if verbosity_level == 1 => ("mhost=info".to_string(), log_format_info),
        Err(_) if verbosity_level == 2 => ("mhost=debug".to_string(), log_format_debug),
        Err(_) if verbosity_level > 2 => ("mhost=trace".to_string(), log_format_debug),
        Err(_) => ("mhost=warn".to_string(), log_format_info),
    };

    Logger::with_str(&log_spec).format(log_format)
}

fn generate_completion(args: &ArgMatches) -> Result<()> {
    let bin_name = env!("CARGO_PKG_NAME");
    let shell = args.value_of("completions").ok_or(
        ErrorKind::CliArgsParsingError,
    )?;
    build_cli().gen_completions_to(
        bin_name,
        shell.parse::<Shell>().unwrap(),
        &mut std::io::stdout(),
    );
    Ok(())
}

fn parse_args(args: &ArgMatches) -> Result<(Query, usize)> {
    let timeout = value_t!(args, "timeout", u64)
        .map(Duration::from_secs)
        .unwrap();

    let domain_name = if args.is_present("dont use local search domain") {
        args.value_of("domain name").unwrap().to_string()
    } else {
        let cfg = get::resolv_conf()?;
        let domain_name_from_args = args.value_of("domain name").unwrap();
        if domain_name_from_args.ends_with(".") {
            domain_name_from_args.to_string()
        } else if cfg.search.len() > 0 && domain_name_from_args.matches(".").count() >= cfg.ndots as usize {
            format!("{}.", domain_name_from_args)
        } else {
            format!("{}.{}.", domain_name_from_args, cfg.search.first().unwrap())
        }
    };
    debug!("domain_name: '{}'", domain_name);

    let query = match IpAddr::from_str(&domain_name) {
        Ok(ip) => Query::from(ip, vec![RecordType::PTR]),
        Err(_) => {
            let record_types = get::record_types(args.values_of_lossy("record types"))
                .chain_err(|| ErrorKind::CliArgsParsingError)?;
            Query::new(&domain_name, record_types)
        }
    }.set_timeout(timeout);
    trace!("{:?}", query);

    let server_limit = value_t!(args, "limit", usize).unwrap();

    Ok((query, server_limit))
}

// TODO: make this a Result
fn run_lookup(args: &ArgMatches, query: Query, server_limit: usize) -> Vec<LookupResult<Response>> {
    let mut io_loop = Core::new().unwrap();
    let handle = io_loop.handle();
    let lookup = get::dns_servers(
        &handle,
        args.values_of_lossy("DNS servers"),
        args.is_present("predefined server"),
        args.is_present("dont use local dns servers"),
        args.values_of_lossy("ungefiltert ids"),
    )
        .map_err(|e| e.into())
        .and_then(|servers| {
            debug!("DNS Server set: {:?}", servers);
            let dns_endpoints = servers
                .into_iter()
                .map(|s| (s, 53))
                .take(server_limit)
                .collect();
            multiple_lookup(&handle, query, dns_endpoints).map_err(
                |_| {
                    Error::from_kind(ErrorKind::LookupFailed)
                },
            )
        });
    let result = io_loop.run(lookup);

    result.unwrap()
}

fn output(responses: &[LookupResult<Response>], outputs: Vec<String>) -> Result<()> {
    let mut writer = std::io::stdout();
    for output in outputs {
        match output.as_ref() {
            "details" => output::DetailsOutput::new(responses).output(&mut writer)?,
            _ => output::SummaryOutput::new(responses).output(&mut writer)?,
        }
    }

    Ok(())
}

error_chain! {
    errors {
        CliArgsParsingError {
            description("failed to parse CLI arguments")
            display("failed to parse CLI arguments")
        }

        LookupFailed {
            description("failed to run lookup")
            display("failed to run lookup")
        }
    }

    links {
        Get(get::Error, get::ErrorKind);
        Lookup(lookup::Error, lookup::ErrorKind);
        Output(output::Error, output::ErrorKind);
    }
}

quick_main!(run);
<|endoftext|>


<commit_before>extern crate rand;

use std::io::{BufRead, BufReader, BufWriter, Write};
use std::fs::File;
use std::slice;
use std::mem;

use std::cmp::{min, max};

use rand::Rng;

fn main() {
    println!("usage: parse <source> <target> [strings] [sort|dedup|reorder|undirect|randomize|symmetrize]^*");
    println!("takes sorted input whose lines look like:");
    println!("<src> <dst>");
    println!("will overwrite <target>.offsets and <target>.targets");
    let source = std::env::args().nth(1).unwrap();
    let target = std::env::args().nth(2).unwrap();

    let (mut graph, skip) = if std::env::args().nth(3) == Some("strings".to_owned()) {
        (read_strings(&source), 4)
    }
    else {
        (read_edges(&source), 3)
    };

    for instruction in std::env::args().skip(skip) {
        match instruction.as_str() {
            "sort" => graph.sort(),                     // sorts the edges by (src, dst).
            "dedup" => graph.dedup(),                   // deduplicates edges.
            "deloop" => graph.retain(|&(x,y)| x != y),  // removes self-loops.
            "reorder" => reorder(&mut graph),           // renumbers nodes by undirected degree.
            "undirect" => undirect(&mut graph),         // directs edges from small to large.
            "randomize" => randomize(&mut graph),       // renumbers nodes randomly.
            "symmetrize" => symmetrize(&mut graph),     // adds reverse edges.
            unknown => {
                panic!(format!("Unrecognized command: {}", unknown));
            },
        }
    }

    graph.sort();
    graph.dedup();
    digest_graph_vector(&_extract_fragment(graph.iter().map(|x| *x)), &target);
}

fn symmetrize(graph: &mut Vec<(u32, u32)>) {
    let count = graph.len();
    for index in 0 .. count {
        let (x,y) = graph[index];
        graph.push((y,x));
    }
}

/// Reads lines of text into pairs of integers.
fn read_edges(filename: &str) -> Vec<(u32, u32)> {
    let mut graph = Vec::new();
    let file = BufReader::new(File::open(filename).unwrap());
    for readline in file.lines() {
        let line = readline.ok().expect("read error");
        if !line.starts_with('#') {
            let mut elts = line[..].split_whitespace();
            let src: u32 = elts.next().expect("line missing src field").parse().expect("malformed src");
            let dst: u32 = elts.next().expect("line missing dst field").parse().expect("malformed dst");
            graph.push((src, dst));
        }
    }
    println!("read {} lines", graph.len());
    graph
}

/// Reads lines of text into pairs of integers.
fn read_strings(filename: &str) -> Vec<(u32, u32)> {
    let mut graph = Vec::new();
    let mut intern = ::std::collections::HashMap::new();
    let file = BufReader::new(File::open(filename).unwrap());
    for readline in file.lines() {
        let line = readline.ok().expect("read error");
        if !line.starts_with('#') {
            let mut elts = line[..].split_whitespace();
            let src = elts.next().expect("line missing src field");
            if !intern.contains_key(src) { let len = intern.len() as u32; intern.insert(src.to_owned(), len); }
            let dst = elts.next().expect("line missing dst field");
            if !intern.contains_key(dst) { let len = intern.len() as u32; intern.insert(dst.to_owned(), len); }
            graph.push((intern[src], intern[dst]));
        }
    }
    println!("read {} lines", graph.len());
    graph
}

/// Re-orders graph identifiers by undirected degree.
fn reorder(graph: &mut Vec<(u32, u32)>) {

    let mut degrees = Vec::new();
    for &(src, dst) in graph.iter() {
        while degrees.len() <= src as usize { degrees.push(0); }
        while degrees.len() <= dst as usize { degrees.push(0); }
        degrees[src as usize] += 1;
        degrees[dst as usize] += 1;
    }

    let mut to_sort =
        degrees
            .drain(..)
            .enumerate()
            .filter(|&(_,deg)| deg > 0)
            .map(|(pos,deg)| (deg,pos as u32))
            .collect::<Vec<_>>();

    to_sort.sort_unstable();
    let mut rename = Vec::new();
    for (idx, (_deg, pos)) in to_sort.into_iter().enumerate() {
        while rename.len() <= pos as usize { rename.push(0); }
        rename[pos as usize] = idx as u32;
    }

    for index in 0 .. graph.len() {
        let new_src = rename[graph[index].0 as usize];
        let new_dst = rename[graph[index].1 as usize];
        graph[index] = (min(new_src, new_dst), max(new_src, new_dst));
    }
}

/// Re-orients edges to point from small to large.
fn undirect(graph: &mut Vec<(u32, u32)>) {
    for src_dst in graph.iter_mut() {
        if src_dst.0 > src_dst.1 {
            ::std::mem::swap(&mut src_dst.0, &mut src_dst.1);
        }
    }
}

fn randomize(graph: &mut Vec<(u32, u32)>) {

    let mut rng = ::rand::thread_rng();

    let mut random = Vec::new();
    for index in 0 .. graph.len() {
        while random.len() <= graph[index].0 as usize { random.push(rng.gen::<usize>()); }
        while random.len() <= graph[index].1 as usize { random.push(rng.gen::<usize>()); }
    }

    let mut to_sort =
        random
            .drain(..)
            .enumerate()
            .map(|(pos,deg)| (deg,pos as u32))
            .collect::<Vec<_>>();

    to_sort.sort_unstable();
    let mut rename = Vec::new();
    for (idx, (_deg, pos)) in to_sort.into_iter().enumerate() {
        while rename.len() <= pos as usize { rename.push(0); }
        rename[pos as usize] = idx as u32;
    }

    for index in 0 .. graph.len() {
        graph[index].0 = rename[graph[index].0 as usize];
        graph[index].1 = rename[graph[index].1 as usize];
    }
}

fn _extract_fragment<I: Iterator<Item=(u32, u32)>>(graph: I) -> (Vec<u64>, Vec<u32>) {
    let mut nodes = Vec::new();
    let mut edges = Vec::new();

    for (src, dst) in graph {
        while src + 1 >= nodes.len() as u32 { nodes.push(0); }
        while dst + 1 >= nodes.len() as u32 { nodes.push(0); }

        nodes[src as usize + 1] += 1;
        edges.push(dst);
    }

    for index in 1..nodes.len() {
        nodes[index] += nodes[index - 1];
    }

    return (nodes, edges);
}

fn digest_graph_vector(graph: &(Vec<u64>, Vec<u32>), output_prefix: &str) {
    let mut edge_writer = BufWriter::new(File::create(format!("{}.targets", output_prefix)).unwrap());
    let mut node_writer = BufWriter::new(File::create(format!("{}.offsets", output_prefix)).unwrap());
    node_writer.write_all(unsafe { _typed_as_byte_slice(&graph.0[..]) }).unwrap();

    let mut slice = unsafe { _typed_as_byte_slice(&graph.1[..]) };
    while slice.len() > 0 {
        let to_write = if slice.len() < 1000000 { slice.len() } else { 1000000 };
        edge_writer.write_all(&slice[..to_write]).unwrap();
        slice = &slice[to_write..];
    }
}

unsafe fn _typed_as_byte_slice<T>(slice: &[T]) -> &[u8] {
    slice::from_raw_parts(slice.as_ptr() as *const u8, slice.len() * mem::size_of::<T>())
}
<commit_msg>Add optional separator argument<commit_after>extern crate rand;

use std::io::{BufRead, BufReader, BufWriter, Write};
use std::fs::File;
use std::slice;
use std::mem;

use std::cmp::{min, max};

use rand::Rng;

fn main() {
    println!("usage: parse <source> <target> [separator <char>] [strings] [sort|dedup|deloop|reorder|undirect|randomize|symmetrize]^*");
    println!("takes sorted input whose lines look like:");
    println!("<src> <dst>");
    println!("will overwrite <target>.offsets and <target>.targets");
    let source = std::env::args().nth(1).unwrap();
    let target = std::env::args().nth(2).unwrap();

    let (separator, nth) = if std::env::args().nth(3) == Some("separator".to_owned()) {
        (std::env::args().nth(4).map(|strn| strn.chars().next().unwrap()), 5)
    } else {
        (None, 3)
    };

    println!("Processing input file");
    let (mut graph, skip) = if std::env::args().nth(nth) == Some("strings".to_owned()) {
        (read_strings(&source, separator), nth + 1)
    } else {
        (read_edges(&source, separator), nth)
    };

    for instruction in std::env::args().skip(skip) {
        match instruction.as_str() {
            "sort" => { graph.sort_unstable(); }        // sorts the edges by (src, dst).
            "dedup" => {                                // deduplicates edges.
                let len = graph.len();
                graph.dedup();
                println!("Removed {} duplicate edges", len - graph.len());
            }
            "deloop" => {                               // removes self-loops.
                let len = graph.len();
                graph.retain(|&(x, y)| x != y);
                let count = len - graph.len();
                println!("Removed {} loops", count);
            }
            "reorder" => reorder(&mut graph),           // renumbers nodes by undirected degree.
            "undirect" => undirect(&mut graph),         // directs edges from small to large.
            "randomize" => randomize(&mut graph),       // renumbers nodes randomly.
            "symmetrize" => symmetrize(&mut graph),     // adds reverse edges.
            unknown => {
                panic!(format!("Unrecognized command: {}", unknown));
            },
        }
    }

    println!("Preparing output");
    graph.sort_unstable();
    let output = &_extract_fragment(graph.into_iter());
    println!("Writing {} vertices and {} edges", output.0.len() - 1, output.1.len());
    digest_graph_vector(output, &target);
}

fn symmetrize(graph: &mut Vec<(u32, u32)>) {
    let count = graph.len();
    for index in 0 .. count {
        let (x,y) = graph[index];
        graph.push((y,x));
    }
}

/// Reads lines of text into pairs of integers.
fn read_edges(filename: &str, separator: Option<char>) -> Vec<(u32, u32)> {
    let mut graph = Vec::new();
    let file = BufReader::new(File::open(filename).unwrap());
    for line in file.lines().filter_map(Result::ok) {
        if !['#', '%']
            .iter()
            .any(|&comment_char| line.starts_with(comment_char))
        {
            let elts = if let Some(sep) = separator { line[..].split(sep).collect::<Vec<_>>() } else { line[..].split_whitespace().collect::<Vec<_>>() };
            let src: u32 = elts.get(0).expect("line missing src field").parse().expect("malformed src");
            let dst: u32 = elts.get(1).expect("line missing dst field").parse().expect("malformed dst");
            graph.push((src, dst));
        }
    }
    println!("Read {} lines", graph.len());
    graph
}

/// Reads lines of text into pairs of integers.
fn read_strings(filename: &str, separator: Option<char>) -> Vec<(u32, u32)> {
    let mut graph = Vec::new();
    let mut intern = ::std::collections::HashMap::new();
    let file = BufReader::new(File::open(filename).unwrap());
    for line in file.lines().filter_map(Result::ok) {
        if !['#', '%']
            .iter()
            .any(|&comment_char| line.starts_with(comment_char))
        {
            let elts = if let Some(sep) = separator { line[..].split(sep).collect::<Vec<_>>() } else { line[..].split_whitespace().collect::<Vec<_>>() };
            let src = *elts.get(0).expect("line missing src field");
            if !intern.contains_key(src) { let len = intern.len() as u32; intern.insert(src.to_string(), len); }
            let dst = *elts.get(1).expect("line missing dst field");
            if !intern.contains_key(dst) { let len = intern.len() as u32; intern.insert(dst.to_string(), len); }
            graph.push((intern[src], intern[dst]));
        }
    }
    println!("Read {} lines", graph.len());
    graph
}

/// Re-orders graph identifiers by undirected degree.
fn reorder(graph: &mut Vec<(u32, u32)>) {

    let mut degrees = Vec::new();
    for &(src, dst) in graph.iter() {
        while degrees.len() <= src as usize { degrees.push(0); }
        while degrees.len() <= dst as usize { degrees.push(0); }
        degrees[src as usize] += 1;
        degrees[dst as usize] += 1;
    }

    let mut to_sort =
        degrees
            .drain(..)
            .enumerate()
            .filter(|&(_,deg)| deg > 0)
            .map(|(pos,deg)| (deg,pos as u32))
            .collect::<Vec<_>>();

    to_sort.sort_unstable();
    let mut rename = Vec::new();
    for (idx, (_deg, pos)) in to_sort.into_iter().enumerate() {
        while rename.len() <= pos as usize { rename.push(0); }
        rename[pos as usize] = idx as u32;
    }

    for index in 0 .. graph.len() {
        let new_src = rename[graph[index].0 as usize];
        let new_dst = rename[graph[index].1 as usize];
        graph[index] = (min(new_src, new_dst), max(new_src, new_dst));
    }
}

/// Re-orients edges to point from small to large.
fn undirect(graph: &mut Vec<(u32, u32)>) {
    for src_dst in graph.iter_mut() {
        if src_dst.0 > src_dst.1 {
            ::std::mem::swap(&mut src_dst.0, &mut src_dst.1);
        }
    }
}

fn randomize(graph: &mut Vec<(u32, u32)>) {

    let mut rng = ::rand::thread_rng();

    let mut random = Vec::new();
    for index in 0 .. graph.len() {
        while random.len() <= graph[index].0 as usize { random.push(rng.gen::<usize>()); }
        while random.len() <= graph[index].1 as usize { random.push(rng.gen::<usize>()); }
    }

    let mut to_sort =
        random
            .drain(..)
            .enumerate()
            .map(|(pos,deg)| (deg,pos as u32))
            .collect::<Vec<_>>();

    to_sort.sort_unstable();
    let mut rename = Vec::new();
    for (idx, (_deg, pos)) in to_sort.into_iter().enumerate() {
        while rename.len() <= pos as usize { rename.push(0); }
        rename[pos as usize] = idx as u32;
    }

    for index in 0 .. graph.len() {
        graph[index].0 = rename[graph[index].0 as usize];
        graph[index].1 = rename[graph[index].1 as usize];
    }
}

fn _extract_fragment<I: Iterator<Item=(u32, u32)>>(graph: I) -> (Vec<u64>, Vec<u32>) {
    let mut nodes = Vec::new();
    let mut edges = Vec::new();

    for (src, dst) in graph {
        while src + 1 >= nodes.len() as u32 { nodes.push(0); }
        while dst + 1 >= nodes.len() as u32 { nodes.push(0); }

        nodes[src as usize + 1] += 1;
        edges.push(dst);
    }

    for index in 1..nodes.len() {
        nodes[index] += nodes[index - 1];
    }

    return (nodes, edges);
}

fn digest_graph_vector(graph: &(Vec<u64>, Vec<u32>), output_prefix: &str) {
    let mut edge_writer = BufWriter::new(File::create(format!("{}.targets", output_prefix)).unwrap());
    let mut node_writer = BufWriter::new(File::create(format!("{}.offsets", output_prefix)).unwrap());
    node_writer.write_all(unsafe { _typed_as_byte_slice(&graph.0[..]) }).unwrap();

    let mut slice = unsafe { _typed_as_byte_slice(&graph.1[..]) };
    while slice.len() > 0 {
        let to_write = if slice.len() < 1000000 { slice.len() } else { 1000000 };
        edge_writer.write_all(&slice[..to_write]).unwrap();
        slice = &slice[to_write..];
    }
}

unsafe fn _typed_as_byte_slice<T>(slice: &[T]) -> &[u8] {
    slice::from_raw_parts(slice.as_ptr() as *const u8, slice.len() * mem::size_of::<T>())
}
<|endoftext|>


<commit_before>// This file is part of the uutils coreutils package.
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

use clap::App;
use std::collections::hash_map::HashMap;
use std::ffi::OsString;
use std::fs::File;
use std::io::{self, Write};

include!(concat!(env!("OUT_DIR"), "/uutils_map.rs"));

fn main() -> io::Result<()> {
    let utils = util_map::<Box<dyn Iterator<Item = OsString>>>();
    match std::fs::create_dir("docs/src/utils/") {
        Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => Ok(()),
        x => x,
    }?;

    let mut summary = File::create("docs/src/SUMMARY.md")?;

    let _ = write!(
        summary,
        "# Summary\n\
        \n\
        [Introduction](index.md)\n\
        * [Installation](installation.md)\n\
        * [Contributing](contributing.md)\n\
        \n\
        # Reference\n\
        * [Multi-call binary](multicall.md)\n",
    );

    let mut utils = utils.iter().collect::<Vec<_>>();
    utils.sort();
    for (&name, (_, app)) in utils {
        if name == "[" {
            continue;
        }
        let p = format!("docs/src/utils/{}.md", name);
        if let Ok(f) = File::create(&p) {
            write_markdown(f, &mut app(), name)?;
            println!("Wrote to '{}'", p);
        } else {
            println!("Error writing to {}", p);
        }
        write!(summary, "* [{0}](utils/{0}.md)\n", name)?
    }
    Ok(())
}

fn write_markdown(mut w: impl Write, app: &mut App, name: &str) -> io::Result<()> {
    write!(w, "# {}\n\n", name)?;
    write_version(&mut w, app)?;
    write_usage(&mut w, app, name)?;
    write_description(&mut w, app)?;
    write_options(&mut w, app)
}

fn write_version(w: &mut impl Write, app: &App) -> io::Result<()> {
    writeln!(
        w,
        "
version: {}
",
        app.render_version().split_once(' ').unwrap().1
    )
}

fn write_usage(w: &mut impl Write, app: &mut App, name: &str) -> io::Result<()> {
    writeln!(w, "\n```")?;
    let mut usage: String = app.render_usage().lines().nth(1).unwrap().trim().into();
    usage = usage.replace(app.get_name(), name);
    writeln!(w, "{}", usage)?;
    writeln!(w, "```")
}

fn write_description(w: &mut impl Write, app: &App) -> io::Result<()> {
    if let Some(about) = app.get_long_about().or_else(|| app.get_about()) {
        writeln!(w, "{}", about)
    } else {
        Ok(())
    }
}

fn write_options(w: &mut impl Write, app: &App) -> io::Result<()> {
    writeln!(w, "
Options
")?;
    write!(w, "
")?;
    for arg in app.get_arguments() {
        write!(w, "
")?;
        let mut first = true;
        for l in arg.get_long_and_visible_aliases().unwrap_or_default() {
            if !first {
                write!(w, ", ")?;
            } else {
                first = false;
            }
            write!(w, "")?;
            write!(w, "--{}", l)?;
            if let Some(names) = arg.get_value_names() {
                write!(
                    w,
                    "={}",
                    names
                        .iter()
                        .map(|x| format!("<{}>", x))
                        .collect::<Vec<_>>()
                        .join(" ")
                )?;
            }
            write!(w, "")?;
        }
        for s in arg.get_short_and_visible_aliases().unwrap_or_default() {
            if !first {
                write!(w, ", ")?;
            } else {
                first = false;
            }
            write!(w, "")?;
            write!(w, "-{}", s)?;
            if let Some(names) = arg.get_value_names() {
                write!(
                    w,
                    " {}",
                    names
                        .iter()
                        .map(|x| format!("<{}>", x))
                        .collect::<Vec<_>>()
                        .join(" ")
                )?;
            }
            write!(w, "")?;
        }
        writeln!(w, "
")?;
        writeln!(w, "
\n\n{}\n\n
", arg.get_help().unwrap_or_default())?;
    }
    writeln!(w, "
")
}
<commit_msg>uudoc: fix clippy lint<commit_after>// This file is part of the uutils coreutils package.
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

use clap::App;
use std::collections::hash_map::HashMap;
use std::ffi::OsString;
use std::fs::File;
use std::io::{self, Write};

include!(concat!(env!("OUT_DIR"), "/uutils_map.rs"));

fn main() -> io::Result<()> {
    let utils = util_map::<Box<dyn Iterator<Item = OsString>>>();
    match std::fs::create_dir("docs/src/utils/") {
        Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => Ok(()),
        x => x,
    }?;

    let mut summary = File::create("docs/src/SUMMARY.md")?;

    let _ = write!(
        summary,
        "# Summary\n\
        \n\
        [Introduction](index.md)\n\
        * [Installation](installation.md)\n\
        * [Contributing](contributing.md)\n\
        \n\
        # Reference\n\
        * [Multi-call binary](multicall.md)\n",
    );

    let mut utils = utils.iter().collect::<Vec<_>>();
    utils.sort();
    for (&name, (_, app)) in utils {
        if name == "[" {
            continue;
        }
        let p = format!("docs/src/utils/{}.md", name);
        if let Ok(f) = File::create(&p) {
            write_markdown(f, &mut app(), name)?;
            println!("Wrote to '{}'", p);
        } else {
            println!("Error writing to {}", p);
        }
        writeln!(summary, "* [{0}](utils/{0}.md)", name)?
    }
    Ok(())
}

fn write_markdown(mut w: impl Write, app: &mut App, name: &str) -> io::Result<()> {
    write!(w, "# {}\n\n", name)?;
    write_version(&mut w, app)?;
    write_usage(&mut w, app, name)?;
    write_description(&mut w, app)?;
    write_options(&mut w, app)
}

fn write_version(w: &mut impl Write, app: &App) -> io::Result<()> {
    writeln!(
        w,
        "
version: {}
",
        app.render_version().split_once(' ').unwrap().1
    )
}

fn write_usage(w: &mut impl Write, app: &mut App, name: &str) -> io::Result<()> {
    writeln!(w, "\n```")?;
    let mut usage: String = app.render_usage().lines().nth(1).unwrap().trim().into();
    usage = usage.replace(app.get_name(), name);
    writeln!(w, "{}", usage)?;
    writeln!(w, "```")
}

fn write_description(w: &mut impl Write, app: &App) -> io::Result<()> {
    if let Some(about) = app.get_long_about().or_else(|| app.get_about()) {
        writeln!(w, "{}", about)
    } else {
        Ok(())
    }
}

fn write_options(w: &mut impl Write, app: &App) -> io::Result<()> {
    writeln!(w, "
Options
")?;
    write!(w, "
")?;
    for arg in app.get_arguments() {
        write!(w, "
")?;
        let mut first = true;
        for l in arg.get_long_and_visible_aliases().unwrap_or_default() {
            if !first {
                write!(w, ", ")?;
            } else {
                first = false;
            }
            write!(w, "")?;
            write!(w, "--{}", l)?;
            if let Some(names) = arg.get_value_names() {
                write!(
                    w,
                    "={}",
                    names
                        .iter()
                        .map(|x| format!("<{}>", x))
                        .collect::<Vec<_>>()
                        .join(" ")
                )?;
            }
            write!(w, "")?;
        }
        for s in arg.get_short_and_visible_aliases().unwrap_or_default() {
            if !first {
                write!(w, ", ")?;
            } else {
                first = false;
            }
            write!(w, "")?;
            write!(w, "-{}", s)?;
            if let Some(names) = arg.get_value_names() {
                write!(
                    w,
                    " {}",
                    names
                        .iter()
                        .map(|x| format!("<{}>", x))
                        .collect::<Vec<_>>()
                        .join(" ")
                )?;
            }
            write!(w, "")?;
        }
        writeln!(w, "
")?;
        writeln!(w, "
\n\n{}\n\n
", arg.get_help().unwrap_or_default())?;
    }
    writeln!(w, "
")
}
<|endoftext|>


<commit_before>use sdl2::EventPump;
use sdl2::event::Event;
use sdl2::keyboard::Keycode;

pub struct Keyboard<'a> {
    key_statuses: [bool; 16],
    key_press_pending: bool,
    event_pump: &'a mut EventPump,
}

const KEY_0: Keycode = Keycode::Comma;
const KEY_1: Keycode = Keycode::Num7;
const KEY_2: Keycode = Keycode::Num8;
const KEY_3: Keycode = Keycode::Num9;
const KEY_4: Keycode = Keycode::H;
const KEY_5: Keycode = Keycode::G;
const KEY_6: Keycode = Keycode::F;
const KEY_7: Keycode = Keycode::N;
const KEY_8: Keycode = Keycode::R;
const KEY_9: Keycode = Keycode::T;
const KEY_A: Keycode = Keycode::M;
const KEY_B: Keycode = Keycode::Period;
const KEY_C: Keycode = Keycode::Num0;
const KEY_D: Keycode = Keycode::Q;
const KEY_E: Keycode = Keycode::D;
const KEY_F: Keycode = Keycode::J;

impl<'a> Keyboard<'a> {
    pub fn new(event_pump: &'a mut EventPump) -> Keyboard<'a> {
        Keyboard {
            key_statuses: [false; 16],
            key_press_pending: false,
            event_pump,
        }
    }

    pub fn is_pressed(&mut self, key: u8) -> bool {
        self.key_press_pending = false;
        self.key_statuses[key as usize]
    }

    pub fn any_key_pressed(&mut self) -> Option<u8> {
        let mut ret: Option<u8> = None;
        if self.key_press_pending {
            for i in 0u8..16u8 {
                if self.key_statuses[i as usize] {
                    ret = Some(i);
                    break;
                }
            }
        }
        self.key_press_pending = false;
        ret
    }

    pub fn check_events(&mut self) {
        while let Some(event) = self.event_pump.poll_event() {
            match event {
                Event::Quit { .. } |
                Event::KeyDown { keycode: Some(Keycode::Escape), .. } => ::std::process::exit(0),
                Event::KeyDown { keycode: Some(key), .. } => self.update_key_status(key, true),
                Event::KeyUp { keycode: Some(key), .. } => self.update_key_status(key, false),
                _ => {}
            }
        }
    }

    fn update_key_status(&mut self, keycode: Keycode, down: bool) {
        match keycode {
            KEY_0 => { self.key_statuses[0x0] = down; self.key_press_pending = true; },
            KEY_1 => { self.key_statuses[0x1] = down; self.key_press_pending = true; },
            KEY_2 => { self.key_statuses[0x2] = down; self.key_press_pending = true; },
            KEY_3 => { self.key_statuses[0x3] = down; self.key_press_pending = true; },
            KEY_4 => { self.key_statuses[0x4] = down; self.key_press_pending = true; },
            KEY_5 => { self.key_statuses[0x5] = down; self.key_press_pending = true; },
            KEY_6 => { self.key_statuses[0x6] = down; self.key_press_pending = true; },
            KEY_7 => { self.key_statuses[0x7] = down; self.key_press_pending = true; },
            KEY_8 => { self.key_statuses[0x8] = down; self.key_press_pending = true; },
            KEY_9 => { self.key_statuses[0x9] = down; self.key_press_pending = true; },
            KEY_A => { self.key_statuses[0xA] = down; self.key_press_pending = true; },
            KEY_B => { self.key_statuses[0xB] = down; self.key_press_pending = true; },
            KEY_C => { self.key_statuses[0xC] = down; self.key_press_pending = true; },
            KEY_D => { self.key_statuses[0xD] = down; self.key_press_pending = true; },
            KEY_E => { self.key_statuses[0xE] = down; self.key_press_pending = true; },
            KEY_F => { self.key_statuses[0xF] = down; self.key_press_pending = true; },
            _ => ()
        }
    }
}
<commit_msg>Refactor keyboard module<commit_after>use sdl2::EventPump;
use sdl2::event::Event;
use sdl2::keyboard::Keycode;

const KEY_0: Keycode = Keycode::Comma;
const KEY_1: Keycode = Keycode::Num7;
const KEY_2: Keycode = Keycode::Num8;
const KEY_3: Keycode = Keycode::Num9;
const KEY_4: Keycode = Keycode::H;
const KEY_5: Keycode = Keycode::G;
const KEY_6: Keycode = Keycode::F;
const KEY_7: Keycode = Keycode::N;
const KEY_8: Keycode = Keycode::R;
const KEY_9: Keycode = Keycode::T;
const KEY_A: Keycode = Keycode::M;
const KEY_B: Keycode = Keycode::Period;
const KEY_C: Keycode = Keycode::Num0;
const KEY_D: Keycode = Keycode::Q;
const KEY_E: Keycode = Keycode::D;
const KEY_F: Keycode = Keycode::J;

pub struct Keyboard<'a> {
    key_statuses: [bool; 16],
    key_press_pending: bool,
    event_pump: &'a mut EventPump,
}

impl<'a> Keyboard<'a> {
    pub fn new(event_pump: &'a mut EventPump) -> Keyboard<'a> {
        Keyboard {
            key_statuses: [false; 16],
            key_press_pending: false,
            event_pump,
        }
    }

    pub fn is_pressed(&mut self, key: u8) -> bool {
        self.key_press_pending = false;
        self.key_statuses[key as usize]
    }

    pub fn any_key_pressed(&mut self) -> Option<u8> {
        let mut ret: Option<u8> = None;
        if self.key_press_pending {
            let first_key = self.key_statuses.iter().position(|&x| x);
            ret = match first_key {
                Some(x) => Some(x as u8),
                None => None,
            };
        }
        self.key_press_pending = false;
        ret
    }

    pub fn check_events(&mut self) {
        while let Some(event) = self.event_pump.poll_event() {
            match event {
                Event::Quit { .. } |
                Event::KeyDown { keycode: Some(Keycode::Escape), .. } => ::std::process::exit(0),
                Event::KeyDown { keycode: Some(key), .. } => self.update_key_status(key, true),
                Event::KeyUp { keycode: Some(key), .. } => self.update_key_status(key, false),
                _ => {}
            }
        }
    }

    fn update_key_status(&mut self, keycode: Keycode, down: bool) {
        let key = match keycode {
            KEY_0 => 0x0,
            KEY_1 => 0x1,
            KEY_2 => 0x2,
            KEY_3 => 0x3,
            KEY_4 => 0x4,
            KEY_5 => 0x5,
            KEY_6 => 0x6,
            KEY_7 => 0x7,
            KEY_8 => 0x8,
            KEY_9 => 0x9,
            KEY_A => 0xA,
            KEY_B => 0xB,
            KEY_C => 0xC,
            KEY_D => 0xD,
            KEY_E => 0xE,
            KEY_F => 0xF,
            _ => return,
        };
        self.key_statuses[key] = down;
        self.key_press_pending = true;
    }
}
<|endoftext|>


<commit_before>import std._uint;
import std._int;
import std._vec;

// FIXME: With recursive object types, we could implement binary methods like
//        union, intersection, and difference. At that point, we could write
//        an optimizing version of this module that produces a different obj
//        for the case where nbits <= 32.

// FIXME: Almost all the functions in this module should be state fns, but the
//        effect system isn't currently working correctly.

state type t = rec(vec[mutable uint] storage, uint nbits);

// FIXME: this should be a constant once they work
fn uint_bits() -> uint {
    ret 32u + ((1u << 32u) >> 27u);
}

fn create(uint nbits, bool init) -> t {
    auto elt;
    if (init) {
        elt = ~0u;
    } else {
        elt = 0u;
    }

    auto storage = _vec.init_elt[mutable uint](elt, nbits / uint_bits() + 1u);
    ret rec(storage = storage, nbits = nbits);
}

fn process(&fn(uint, uint) -> uint op, &t v0, &t v1) -> bool {
    auto len = _vec.len[mutable uint](v1.storage);

    check (_vec.len[mutable uint](v0.storage) == len);
    check (v0.nbits == v1.nbits);

    auto changed = false;

    for each (uint i in _uint.range(0u, len)) {
        auto w0 = v0.storage.(i);
        auto w1 = v1.storage.(i);

        auto w = op(w0, w1);
        if (w0 != w) {
            changed = true;
            v0.storage.(i) = w;
        }
    }

    ret changed;
}

fn lor(uint w0, uint w1) -> uint {
    ret w0 | w1;
}

fn union(&t v0, &t v1) -> bool {
    auto sub = lor;
    ret process(sub, v0, v1);
}

fn land(uint w0, uint w1) -> uint {
    ret w0 & w1;
}

fn intersect(&t v0, &t v1) -> bool {
    auto sub = land;
    ret process(sub, v0, v1);
}

fn right(uint w0, uint w1) -> uint {
    ret w1;
}

fn copy(&t v0, t v1) -> bool {
    auto sub = right;
    ret process(sub, v0, v1);
}

fn get(&t v, uint i) -> bool {
    check (i < v.nbits);

    auto bits = uint_bits();

    auto w = i / bits;
    auto b = i % bits;
    auto x = 1u & (v.storage.(w) >> b);
    ret x == 1u;
}

fn equal(&t v0, &t v1) -> bool {
    // FIXME: when we can break or return from inside an iterator loop,
    //        we can eliminate this painful while-loop
    auto len = _vec.len[mutable uint](v1.storage);
    auto i = 0u;
    while (i < len) {
        if (v0.storage.(i) != v1.storage.(i)) {
            ret false;
        }
        i = i + 1u;
    }
    ret true;
}

fn clear(&t v) {
    for each (uint i in _uint.range(0u, _vec.len[mutable uint](v.storage))) {
        v.storage.(i) = 0u;
    }
}

fn invert(&t v) {
    for each (uint i in _uint.range(0u, _vec.len[mutable uint](v.storage))) {
        v.storage.(i) = ~v.storage.(i);
    }
}

/* v0 = v0 - v1 */
fn difference(&t v0, &t v1) -> bool {
    invert(v1);
    auto b = intersect(v0, v1);
    invert(v1);
    ret b;
}

fn set(&t v, uint i, bool x) {
    check (i < v.nbits);

    auto bits = uint_bits();

    auto w = i / bits;
    auto b = i % bits;
    auto w0 = v.storage.(w);
    auto flag = 1u << b;
    if (x) {
        v.storage.(w) = v.storage.(w) | flag;
    } else {
        v.storage.(w) = v.storage.(w) & ~flag;
    }
}

fn init_to_vec(&t v, uint i) -> uint {
    if (get(v, i)) {
        ret 1u;
    } else {
        ret 0u;
    }
}

fn to_vec(&t v) -> vec[uint] {
    auto sub = bind init_to_vec(v, _);
    ret _vec.init_fn[uint](sub, v.nbits);
}

// FIXME: can we just use structural equality on to_vec?
fn eq_vec(&t v0, &vec[uint] v1) -> bool {
    check (v0.nbits == _vec.len[uint](v1));
    auto len = v0.nbits;
    auto i = 0u;
    while (i < len) {
        auto w0 = get(v0, i);
        auto w1 = v1.(i);
        if ((!w0 && w1 != 0u) || (w0 && w1 == 0u)) {
            ret false;
        }
        i = i + 1u;
    }
    ret true;
}

fn test() {
    auto act;
    auto exp;

    // -----------------------------------------------------------------------
    // Tests of 0-element bit-vectors.

    act = create(0u, false);
    exp = _vec.init_elt[uint](0u, 0u);
    // FIXME: why can't I write vec[uint]()?
    check (eq_vec(act, exp));

    // -----------------------------------------------------------------------
    // Tests of 1-element bit-vectors.

    act = create(1u, false);
    check (eq_vec(act, vec(0u)));

    act = create(1u, true);
    check (eq_vec(act, vec(1u)));

    // -----------------------------------------------------------------------
    // Tests of 10-element bit-vectors.

    // all 0
    act = create(10u, false);
    check (eq_vec(act, vec(0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u)));

    // all 1
    act = create(10u, true);
    check (eq_vec(act, vec(1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u)));

    // mixed
    act = create(10u, false);
    set(act, 0u, true);
    set(act, 1u, true);
    set(act, 2u, true);
    set(act, 3u, true);
    set(act, 4u, true);
    check (eq_vec(act, vec(1u, 1u, 1u, 1u, 1u, 0u, 0u, 0u, 0u, 0u)));

    // mixed
    act = create(10u, false);
    set(act, 5u, true);
    set(act, 6u, true);
    set(act, 7u, true);
    set(act, 8u, true);
    set(act, 9u, true);
    check (eq_vec(act, vec(0u, 0u, 0u, 0u, 0u, 1u, 1u, 1u, 1u, 1u)));

    // mixed
    act = create(10u, false);
    set(act, 0u, true);
    set(act, 3u, true);
    set(act, 6u, true);
    set(act, 9u, true);
    check (eq_vec(act, vec(1u, 0u, 0u, 1u, 0u, 0u, 1u, 0u, 0u, 1u)));

    // -----------------------------------------------------------------------
    // Tests of 31-element bit-vectors.

    // all 0
    act = create(31u, false);
    check (eq_vec(act, vec(0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u,
                           0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u,
                           0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u,
                           0u, 0u, 0u, 0u, 0u, 0u, 0u)));

    // all 1
    act = create(31u, true);
    check (eq_vec(act, vec(1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u,
                           1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u,
                           1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u,
                           1u, 1u, 1u, 1u, 1u, 1u, 1u)));

    // mixed
    act = create(31u, false);
    set(act, 0u, true);
    set(act, 1u, true);
    set(act, 2u, true);
    set(act, 3u, true);
    set(act, 4u, true);
    set(act, 5u, true);
    set(act, 6u, true);
    set(act, 7u, true);
    check (eq_vec(act, vec(1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u,
                           0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u,
                           0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u,
                           0u, 0u, 0u, 0u, 0u, 0u, 0u)));

    // mixed
    act = create(31u, false);
    set(act, 16u, true);
    set(act, 17u, true);
    set(act, 18u, true);
    set(act, 19u, true);
    set(act, 20u, true);
    set(act, 21u, true);
    set(act, 22u, true);
    set(act, 23u, true);
    check (eq_vec(act, vec(0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u,
                           0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u,
                           1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u,
                           0u, 0u, 0u, 0u, 0u, 0u, 0u)));

    // mixed
    act = create(31u, false);
    set(act, 24u, true);
    set(act, 25u, true);
    set(act, 26u, true);
    set(act, 27u, true);
    set(act, 28u, true);
    set(act, 29u, true);
    set(act, 30u, true);
    check (eq_vec(act, vec(0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u,
                           0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u,
                           0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u,
                           1u, 1u, 1u, 1u, 1u, 1u, 1u)));

    // mixed
    act = create(31u, false);
    set(act, 3u, true);
    set(act, 17u, true);
    set(act, 30u, true);
    check (eq_vec(act, vec(0u, 0u, 0u, 1u, 0u, 0u, 0u, 0u,
                           0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u,
                           0u, 1u, 0u, 0u, 0u, 0u, 0u, 0u,
                           0u, 0u, 0u, 0u, 0u, 0u, 1u)));

    // -----------------------------------------------------------------------
    // Tests of 32-element bit-vectors.

    // all 0
    act = create(32u, false);
    check (eq_vec(act, vec(0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u,
                           0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u,
                           0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u,
                           0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u)));

    // all 1
    act = create(32u, true);
    check (eq_vec(act, vec(1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u,
                           1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u,
                           1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u,
                           1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u)));

    // mixed
    act = create(32u, false);
    set(act, 0u, true);
    set(act, 1u, true);
    set(act, 2u, true);
    set(act, 3u, true);
    set(act, 4u, true);
    set(act, 5u, true);
    set(act, 6u, true);
    set(act, 7u, true);
    check (eq_vec(act, vec(1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u,
                           0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u,
                           0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u,
                           0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u)));

    // mixed
    act = create(32u, false);
    set(act, 16u, true);
    set(act, 17u, true);
    set(act, 18u, true);
    set(act, 19u, true);
    set(act, 20u, true);
    set(act, 21u, true);
    set(act, 22u, true);
    set(act, 23u, true);
    check (eq_vec(act, vec(0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u,
                           0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u,
                           1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u,
                           0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u)));

    // mixed
    act = create(32u, false);
    set(act, 24u, true);
    set(act, 25u, true);
    set(act, 26u, true);
    set(act, 27u, true);
    set(act, 28u, true);
    set(act, 29u, true);
    set(act, 30u, true);
    set(act, 31u, true);
    check (eq_vec(act, vec(0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u,
                           0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u,
                           0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u,
                           1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u)));

    // mixed
    act = create(32u, false);
    set(act, 3u, true);
    set(act, 17u, true);
    set(act, 30u, true);
    set(act, 31u, true);
    check (eq_vec(act, vec(0u, 0u, 0u, 1u, 0u, 0u, 0u, 0u,
                           0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u,
                           0u, 1u, 0u, 0u, 0u, 0u, 0u, 0u,
                           0u, 0u, 0u, 0u, 0u, 0u, 1u, 1u)));

    // -----------------------------------------------------------------------
    // Tests of 33-element bit-vectors.

    // all 0
    act = create(33u, false);
    check (eq_vec(act, vec(0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u,
                           0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u,
                           0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u,
                           0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u,
                           0u)));

    // all 1
    act = create(33u, true);
    check (eq_vec(act, vec(1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u,
                           1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u,
                           1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u,
                           1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u,
                           1u)));

    // mixed
    act = create(33u, false);
    set(act, 0u, true);
    set(act, 1u, true);
    set(act, 2u, true);
    set(act, 3u, true);
    set(act, 4u, true);
    set(act, 5u, true);
    set(act, 6u, true);
    set(act, 7u, true);
    check (eq_vec(act, vec(1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u,
                           0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u,
                           0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u,
                           0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u,
                           0u)));

    // mixed
    act = create(33u, false);
    set(act, 16u, true);
    set(act, 17u, true);
    set(act, 18u, true);
    set(act, 19u, true);
    set(act, 20u, true);
    set(act, 21u, true);
    set(act, 22u, true);
    set(act, 23u, true);
    check (eq_vec(act, vec(0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u,
                           0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u,
                           1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u,
                           0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u,
                           0u)));

    // mixed
    act = create(33u, false);
    set(act, 24u, true);
    set(act, 25u, true);
    set(act, 26u, true);
    set(act, 27u, true);
    set(act, 28u, true);
    set(act, 29u, true);
    set(act, 30u, true);
    set(act, 31u, true);
    check (eq_vec(act, vec(0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u,
                           0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u,
                           0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u,
                           1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u,
                           0u)));

    // mixed
    act = create(33u, false);
    set(act, 3u, true);
    set(act, 17u, true);
    set(act, 30u, true);
    set(act, 31u, true);
    set(act, 32u, true);
    check (eq_vec(act, vec(0u, 0u, 0u, 1u, 0u, 0u, 0u, 0u,
                           0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u,
                           0u, 1u, 0u, 0u, 0u, 0u, 0u, 0u,
                           0u, 0u, 0u, 0u, 0u, 0u, 1u, 1u,
                           1u)));
}

//
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
//
<commit_msg>eliminated bitv.test, which now lives in test/run-pass<commit_after>import std._uint;
import std._int;
import std._vec;

// FIXME: With recursive object types, we could implement binary methods like
//        union, intersection, and difference. At that point, we could write
//        an optimizing version of this module that produces a different obj
//        for the case where nbits <= 32.

// FIXME: Almost all the functions in this module should be state fns, but the
//        effect system isn't currently working correctly.

state type t = rec(vec[mutable uint] storage, uint nbits);

// FIXME: this should be a constant once they work
fn uint_bits() -> uint {
    ret 32u + ((1u << 32u) >> 27u);
}

fn create(uint nbits, bool init) -> t {
    auto elt;
    if (init) {
        elt = ~0u;
    } else {
        elt = 0u;
    }

    auto storage = _vec.init_elt[mutable uint](elt, nbits / uint_bits() + 1u);
    ret rec(storage = storage, nbits = nbits);
}

fn process(&fn(uint, uint) -> uint op, &t v0, &t v1) -> bool {
    auto len = _vec.len[mutable uint](v1.storage);

    check (_vec.len[mutable uint](v0.storage) == len);
    check (v0.nbits == v1.nbits);

    auto changed = false;

    for each (uint i in _uint.range(0u, len)) {
        auto w0 = v0.storage.(i);
        auto w1 = v1.storage.(i);

        auto w = op(w0, w1);
        if (w0 != w) {
            changed = true;
            v0.storage.(i) = w;
        }
    }

    ret changed;
}

fn lor(uint w0, uint w1) -> uint {
    ret w0 | w1;
}

fn union(&t v0, &t v1) -> bool {
    auto sub = lor;
    ret process(sub, v0, v1);
}

fn land(uint w0, uint w1) -> uint {
    ret w0 & w1;
}

fn intersect(&t v0, &t v1) -> bool {
    auto sub = land;
    ret process(sub, v0, v1);
}

fn right(uint w0, uint w1) -> uint {
    ret w1;
}

fn copy(&t v0, t v1) -> bool {
    auto sub = right;
    ret process(sub, v0, v1);
}

fn get(&t v, uint i) -> bool {
    check (i < v.nbits);

    auto bits = uint_bits();

    auto w = i / bits;
    auto b = i % bits;
    auto x = 1u & (v.storage.(w) >> b);
    ret x == 1u;
}

fn equal(&t v0, &t v1) -> bool {
    // FIXME: when we can break or return from inside an iterator loop,
    //        we can eliminate this painful while-loop
    auto len = _vec.len[mutable uint](v1.storage);
    auto i = 0u;
    while (i < len) {
        if (v0.storage.(i) != v1.storage.(i)) {
            ret false;
        }
        i = i + 1u;
    }
    ret true;
}

fn clear(&t v) {
    for each (uint i in _uint.range(0u, _vec.len[mutable uint](v.storage))) {
        v.storage.(i) = 0u;
    }
}

fn invert(&t v) {
    for each (uint i in _uint.range(0u, _vec.len[mutable uint](v.storage))) {
        v.storage.(i) = ~v.storage.(i);
    }
}

/* v0 = v0 - v1 */
fn difference(&t v0, &t v1) -> bool {
    invert(v1);
    auto b = intersect(v0, v1);
    invert(v1);
    ret b;
}

fn set(&t v, uint i, bool x) {
    check (i < v.nbits);

    auto bits = uint_bits();

    auto w = i / bits;
    auto b = i % bits;
    auto w0 = v.storage.(w);
    auto flag = 1u << b;
    if (x) {
        v.storage.(w) = v.storage.(w) | flag;
    } else {
        v.storage.(w) = v.storage.(w) & ~flag;
    }
}

fn init_to_vec(&t v, uint i) -> uint {
    if (get(v, i)) {
        ret 1u;
    } else {
        ret 0u;
    }
}

fn to_vec(&t v) -> vec[uint] {
    auto sub = bind init_to_vec(v, _);
    ret _vec.init_fn[uint](sub, v.nbits);
}

// FIXME: can we just use structural equality on to_vec?
fn eq_vec(&t v0, &vec[uint] v1) -> bool {
    check (v0.nbits == _vec.len[uint](v1));
    auto len = v0.nbits;
    auto i = 0u;
    while (i < len) {
        auto w0 = get(v0, i);
        auto w1 = v1.(i);
        if ((!w0 && w1 != 0u) || (w0 && w1 == 0u)) {
            ret false;
        }
        i = i + 1u;
    }
    ret true;
}

//
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
//
<|endoftext|>


<commit_before>extern crate notify;
extern crate yaml_rust;
extern crate glob;

use std::process::Command as ShellCommand;
use std::sync::mpsc::channel;
use self::glob::Pattern;

use self::notify::{RecommendedWatcher, Watcher};
use self::yaml_rust::{Yaml, YamlLoader};

use cli::Command;

pub const FILENAME: &'static str = ".watch.yaml";

/// # `WatchCommand`
///
/// Starts watcher to listen the change events configured
/// in watch.yaml
///
pub struct WatchCommand {
    watches: Watches,
}

impl WatchCommand {
    pub fn new(watches: Watches) -> Self {
        WatchCommand {
            watches: watches
        }
    }
}

impl Command for WatchCommand {

    fn execute(&self) -> Result<(), &str>{

        let (tx, rx) = channel();
        let mut watcher: RecommendedWatcher = match Watcher::new(tx) {
            Ok(w) => w,
            Err(err) => panic!("Error while trying watch. Cause: {:?}",err)
        };

        match watcher.watch(".") {
            Ok(()) => {},
            Err(_) => panic!("Unable to watch current directory"),
        }

        println!("Watching.");
        while let Ok(event) = rx.recv() {
            let path: &str = if let Some(ref path_buf) = event.path {
                path_buf.to_str().unwrap()
            } else {
                ""
            };

            if let Some(mut cmd) = self.watches.watch(&path) {
                match cmd.status() {
                    Ok(_) => println!("executed"),
                    Err(err) => println!("Error {:?}", err),
                }
            }
        };
        Ok(())
    }
}

/// # Watches
///
/// Represent all items in the yaml config loaded.
///
pub struct Watches {
    items: Vec<Yaml>,
}
impl Watches {
    pub fn from_args(args: Vec<String>) -> Self {
        let template =
        format!("
        - name: from command
          when:
            change: '{path}'
            run: {command}
        ", path="**", command=args[0]);

        Watches {
            items: YamlLoader::load_from_str(&template).unwrap(),
        }
    }

    pub fn from(plain_text: &str) -> Self {
        Watches {
            items: YamlLoader::load_from_str(&plain_text).unwrap(),
        }
    }

    /// Returns the first watch found for the given path
    /// it may return None if there is no item that match.
    ///
    pub fn watch(&self, path: &str) -> Option<ShellCommand>{
        match self.items[0] {
            Yaml::Array(ref items) => {
                for i in items.iter()
                              .filter(|i| !matches(i["when"]["ignore"].clone(), path)){
                    let command = i["when"]["run"].as_str().unwrap();

                    if matches(i["when"]["change"].clone(), path) {
                        println!("Running: {}", i["name"].as_str().unwrap());
                        let mut args: Vec<&str>= command.split(' ').collect();
                        let cmd = args.remove(0);

                        let mut shell = ShellCommand::new(cmd);
                        shell.args(&args);

                        return Some(shell)
                    }
                };
            },
            _ => panic!("Yaml format unkown.")
        };
        None
    }
}

fn matches(item: Yaml, path: &str) -> bool {
    match item {
        Yaml::Array(ref items) => {
            items.iter().any(|i| matches(i.clone(), path))
        },
        Yaml::String(ref item) => { pattern_for(item).matches(path) },
        _ => false
    }
}

fn pattern_for(pattern: &str) -> Pattern {
    Pattern::new(&format!("**/{}", pattern)).unwrap()
}

#[cfg(test)]
mod tests {
    extern crate notify;
    extern crate yaml_rust;
    extern crate glob;

    use super::*;
    use std::process::Command as ShellCommand;
    use self::yaml_rust::{YamlLoader};

    #[test]
    fn it_loads_from_yaml_file() {
        let file_content = "
    - name: my tests
      when:
        change: tests/*
        run: cargo tests
    ";
        let content = YamlLoader::load_from_str(&file_content).unwrap();
        let watches = Watches::from(file_content);
        assert_eq!(content[0], watches.items[0]);
        assert_eq!(content[0]["when"], watches.items[0]["when"]);
        assert_eq!(content[0]["when"]["change"],
                   watches.items[0]["when"]["change"])
    }

    #[test]
    fn it_loads_from_args() {
        let args = vec![String::from("cargo build")];
        let watches = Watches::from_args(args);

        println!("{:?}", watches.items[0]);
        assert!(watches.watch("src/main.rs").is_some());
        assert!(watches.watch("test/main.rs").is_some());
        assert!(watches.watch(".").is_some());

        let result = watches.watch(".").unwrap();
        let mut expected = ShellCommand::new("cargo");
        expected.arg("build");
        assert_eq!(format!("{:?}", expected),  format!("{:?}", result));
    }

    #[test]
    fn it_watches_test_path() {
        let file_content = "
    - name: my tests
      when:
        change: 'tests/**'
        run: 'cargo tests'
    ";
        let watches = Watches::from(file_content);
        assert!(watches.watch("/Users/crosa/others/funzzy/tests/test.rs").is_some());
        assert!(watches.watch("tests/tests.rs").is_some());
        assert!(watches.watch("tests/ruby.rb").is_some());
        assert!(watches.watch("tests/folder/other.rs").is_some())
    }

    #[test]
    fn it_doesnot_watches_test_path() {
        let file_content = "
    - name: my source
      when:
        change: 'src/**'
        run: 'cargo build'
    ";
        let watches = Watches::from(file_content);

        assert!(watches.watch("/Users/crosa/others/funzzy/events.yaml").is_none());
        assert!(watches.watch("tests/").is_none());
        assert!(watches.watch("tests/test.rs").is_none());
        assert!(watches.watch("tests/folder/other.rs").is_none());
    }

    #[test]
    fn it_creates_shell_command() {
        let file_content = "
    - name: my source
      when:
        change: 'src/**'
        run: 'cargo build'
    ";
        let watches = Watches::from(file_content);
        let result = watches.watch("src/test.rs").unwrap();
        let mut expected = ShellCommand::new("cargo");
        expected.arg("build");
        assert_eq!(format!("{:?}", expected),  format!("{:?}", result))
    }

    #[test]
    fn it_works_with_multiples_itens() {
        let file_content = "
    - name: my source
      when:
        change: 'src/**'
        run: 'cargo build'

    - name: other
      when:
        change: 'test/**'
        run: 'cargo test'
    ";
        let watches = Watches::from(file_content);

        let result = watches.watch("test/test.rs").unwrap();
        let mut expected = ShellCommand::new("cargo");
        expected.arg("test");
        assert_eq!(format!("{:?}", expected),  format!("{:?}", result));

        let result_src = watches.watch("src/test.rs").unwrap();
        let mut expected_src = ShellCommand::new("cargo");
        expected_src.arg("build");
        assert_eq!(format!("{:?}", expected_src),  format!("{:?}", result_src))
    }

    #[test]
    fn it_ignores_pattern() {
        let file_content = "
    - name: my source
      when:
        change: 'src/**'
        run: 'cargo build'
        ignore: 'src/test/**'
    ";
        let watches = Watches::from(file_content);
        assert!(watches.watch("src/other.rb").is_some());
        assert!(watches.watch("src/test.txt").is_some());
        assert!(watches.watch("src/test/other.tmp").is_none())
    }

    #[test]
    fn it_ignores_a_list_of_patterns() {
        let file_content = "
    - name: my source
      when:
        change: 'src/**'
        run: 'cargo build'
        ignore: ['src/test/**', 'src/tmp/**']
    ";
        let watches = Watches::from(file_content);
        assert!(watches.watch("src/other.rb").is_some());
        assert!(watches.watch("src/test.txt").is_some());
        assert!(watches.watch("src/tmp/test.txt").is_none());
        assert!(watches.watch("src/test/other.tmp").is_none())
    }
}
<commit_msg>feat: clear shell for each file change close #11<commit_after>extern crate notify;
extern crate yaml_rust;
extern crate glob;

use std::process::Command as ShellCommand;
use std::sync::mpsc::channel;
use self::glob::Pattern;

use self::notify::{RecommendedWatcher, Watcher};
use self::yaml_rust::{Yaml, YamlLoader};

use cli::Command;

pub const FILENAME: &'static str = ".watch.yaml";

/// # `WatchCommand`
///
/// Starts watcher to listen the change events configured
/// in watch.yaml
///
pub struct WatchCommand {
    watches: Watches,
}

impl WatchCommand {
    pub fn new(watches: Watches) -> Self {
        WatchCommand {
            watches: watches
        }
    }
}

impl Command for WatchCommand {

    fn execute(&self) -> Result<(), &str>{

        let (tx, rx) = channel();
        let mut watcher: RecommendedWatcher = match Watcher::new(tx) {
            Ok(w) => w,
            Err(err) => panic!("Error while trying watch. Cause: {:?}",err)
        };

        match watcher.watch(".") {
            Ok(()) => {},
            Err(_) => panic!("Unable to watch current directory"),
        }

        println!("Watching.");
        while let Ok(event) = rx.recv() {
            let path: &str = if let Some(ref path_buf) = event.path {
                path_buf.to_str().unwrap()
            } else {
                ""
            };

            if let Some(mut cmd) = self.watches.watch(&path) {
                clear_shell();
                match cmd.status() {
                    Ok(_) => println!("executed"),
                    Err(err) => println!("Error {:?}", err),
                }
            }
        };
        Ok(())
    }
}

/// # Watches
///
/// Represent all items in the yaml config loaded.
///
pub struct Watches {
    items: Vec<Yaml>,
}
impl Watches {
    pub fn from_args(args: Vec<String>) -> Self {
        let template =
        format!("
        - name: from command
          when:
            change: '{path}'
            run: {command}
        ", path="**", command=args[0]);

        Watches {
            items: YamlLoader::load_from_str(&template).unwrap(),
        }
    }

    pub fn from(plain_text: &str) -> Self {
        Watches {
            items: YamlLoader::load_from_str(&plain_text).unwrap(),
        }
    }

    /// Returns the first watch found for the given path
    /// it may return None if there is no item that match.
    ///
    pub fn watch(&self, path: &str) -> Option<ShellCommand>{
        match self.items[0] {
            Yaml::Array(ref items) => {
                for i in items.iter()
                              .filter(|i| !matches(i["when"]["ignore"].clone(), path)){
                    let command = i["when"]["run"].as_str().unwrap();

                    if matches(i["when"]["change"].clone(), path) {
                        println!("Running: {}", i["name"].as_str().unwrap());
                        let mut args: Vec<&str>= command.split(' ').collect();
                        let cmd = args.remove(0);

                        let mut shell = ShellCommand::new(cmd);
                        shell.args(&args);

                        return Some(shell)
                    }
                };
            },
            _ => panic!("Yaml format unkown.")
        };
        None
    }
}

fn clear_shell() {
    let _ = ShellCommand::new("clear").status();
}

fn matches(item: Yaml, path: &str) -> bool {
    match item {
        Yaml::Array(ref items) => {
            items.iter().any(|i| matches(i.clone(), path))
        },
        Yaml::String(ref item) => { pattern_for(item).matches(path) },
        _ => false
    }
}

fn pattern_for(pattern: &str) -> Pattern {
    Pattern::new(&format!("**/{}", pattern)).unwrap()
}

#[cfg(test)]
mod tests {
    extern crate notify;
    extern crate yaml_rust;
    extern crate glob;

    use super::*;
    use std::process::Command as ShellCommand;
    use self::yaml_rust::{YamlLoader};

    #[test]
    fn it_loads_from_yaml_file() {
        let file_content = "
    - name: my tests
      when:
        change: tests/*
        run: cargo tests
    ";
        let content = YamlLoader::load_from_str(&file_content).unwrap();
        let watches = Watches::from(file_content);
        assert_eq!(content[0], watches.items[0]);
        assert_eq!(content[0]["when"], watches.items[0]["when"]);
        assert_eq!(content[0]["when"]["change"],
                   watches.items[0]["when"]["change"])
    }

    #[test]
    fn it_loads_from_args() {
        let args = vec![String::from("cargo build")];
        let watches = Watches::from_args(args);

        println!("{:?}", watches.items[0]);
        assert!(watches.watch("src/main.rs").is_some());
        assert!(watches.watch("test/main.rs").is_some());
        assert!(watches.watch(".").is_some());

        let result = watches.watch(".").unwrap();
        let mut expected = ShellCommand::new("cargo");
        expected.arg("build");
        assert_eq!(format!("{:?}", expected),  format!("{:?}", result));
    }

    #[test]
    fn it_watches_test_path() {
        let file_content = "
    - name: my tests
      when:
        change: 'tests/**'
        run: 'cargo tests'
    ";
        let watches = Watches::from(file_content);
        assert!(watches.watch("/Users/crosa/others/funzzy/tests/test.rs").is_some());
        assert!(watches.watch("tests/tests.rs").is_some());
        assert!(watches.watch("tests/ruby.rb").is_some());
        assert!(watches.watch("tests/folder/other.rs").is_some())
    }

    #[test]
    fn it_doesnot_watches_test_path() {
        let file_content = "
    - name: my source
      when:
        change: 'src/**'
        run: 'cargo build'
    ";
        let watches = Watches::from(file_content);

        assert!(watches.watch("/Users/crosa/others/funzzy/events.yaml").is_none());
        assert!(watches.watch("tests/").is_none());
        assert!(watches.watch("tests/test.rs").is_none());
        assert!(watches.watch("tests/folder/other.rs").is_none());
    }

    #[test]
    fn it_creates_shell_command() {
        let file_content = "
    - name: my source
      when:
        change: 'src/**'
        run: 'cargo build'
    ";
        let watches = Watches::from(file_content);
        let result = watches.watch("src/test.rs").unwrap();
        let mut expected = ShellCommand::new("cargo");
        expected.arg("build");
        assert_eq!(format!("{:?}", expected),  format!("{:?}", result))
    }

    #[test]
    fn it_works_with_multiples_itens() {
        let file_content = "
    - name: my source
      when:
        change: 'src/**'
        run: 'cargo build'

    - name: other
      when:
        change: 'test/**'
        run: 'cargo test'
    ";
        let watches = Watches::from(file_content);

        let result = watches.watch("test/test.rs").unwrap();
        let mut expected = ShellCommand::new("cargo");
        expected.arg("test");
        assert_eq!(format!("{:?}", expected),  format!("{:?}", result));

        let result_src = watches.watch("src/test.rs").unwrap();
        let mut expected_src = ShellCommand::new("cargo");
        expected_src.arg("build");
        assert_eq!(format!("{:?}", expected_src),  format!("{:?}", result_src))
    }

    #[test]
    fn it_ignores_pattern() {
        let file_content = "
    - name: my source
      when:
        change: 'src/**'
        run: 'cargo build'
        ignore: 'src/test/**'
    ";
        let watches = Watches::from(file_content);
        assert!(watches.watch("src/other.rb").is_some());
        assert!(watches.watch("src/test.txt").is_some());
        assert!(watches.watch("src/test/other.tmp").is_none())
    }

    #[test]
    fn it_ignores_a_list_of_patterns() {
        let file_content = "
    - name: my source
      when:
        change: 'src/**'
        run: 'cargo build'
        ignore: ['src/test/**', 'src/tmp/**']
    ";
        let watches = Watches::from(file_content);
        assert!(watches.watch("src/other.rb").is_some());
        assert!(watches.watch("src/test.txt").is_some());
        assert!(watches.watch("src/tmp/test.txt").is_none());
        assert!(watches.watch("src/test/other.tmp").is_none())
    }
}
<|endoftext|>


<commit_before>extern crate mm_client;

use mm_client::Client;
use mm_client::MMCError;

use client::client::APIClient;
use client::error::{ClientError, ClientResult};
use config::APIConfig;

pub struct MMClient {
    client: Client,
}

impl APIClient for MMClient {
    fn new(config: Option<&APIConfig>) -> ClientResult<MMClient> {
        config
            .ok_or(ClientError::ConfigError)
            .and_then(|conf| {
                Client::new(conf.key.as_str(), conf.secret.as_str()).or(
                    Err(
                        ClientError::InitializationError,
                    ),
                )
            })
            .and_then(|client| Ok(MMClient { client: client }))
    }

    fn url(&self, url: &str) -> ClientResult<String> {
        let mut lookup_url = String::new();
        lookup_url.push_str(url);
        lookup_url.push(if url.contains('?') { '&' } else { '?' });
        lookup_url.push_str("platform-slug");
        lookup_url.push_str("partnerplayer");

        self.client
            .url(lookup_url.as_str())
            .or_else(|err| {
                match err {
                    MMCError::ResourceNotFound => {}
                    MMCError::NotAuthorized => {}
                    _ => {
                        error!("Failed to query {} due to {}", url, err);
                    }
                };

                Err(err)
            })
            .map_err(ClientError::API)
    }

    fn show(&self, id: &str) -> ClientResult<String> {
        self.client
            .show(id, Some(vec![("platform-slug", "partnerplayer")]))
            .or_else(|err| {
                match err {
                    MMCError::ResourceNotFound => {}
                    MMCError::NotAuthorized => {}
                    _ => {
                        error!("Failed to query show {} due to {}", id, err);
                    }
                };

                Err(err)
            })
            .map_err(ClientError::API)
    }

    fn all_shows(&self) -> ClientResult<String> {
        self.client
            .shows(vec![
                ("page-size", "50"),
                ("platform-slug", "partnerplayer"),
            ])
            .or_else(|err| {
                error!("Failed to query all shows due to {}", err);
                Err(err)
            })
            .map_err(ClientError::API)
    }

    fn changes(&self, since: &str) -> ClientResult<String> {
        self.client
            .changelog(vec![("since", since)])
            .or_else(|err| {
                error!("Failed to query changelog from {} due to {}", since, err);
                Err(err)
            })
            .map_err(ClientError::API)
    }
}
<commit_msg>DEVOPS-86 Fix bad url construction<commit_after>extern crate mm_client;

use mm_client::Client;
use mm_client::MMCError;

use client::client::APIClient;
use client::error::{ClientError, ClientResult};
use config::APIConfig;

pub struct MMClient {
    client: Client,
}

impl APIClient for MMClient {
    fn new(config: Option<&APIConfig>) -> ClientResult<MMClient> {
        config
            .ok_or(ClientError::ConfigError)
            .and_then(|conf| {
                Client::new(conf.key.as_str(), conf.secret.as_str()).or(
                    Err(
                        ClientError::InitializationError,
                    ),
                )
            })
            .and_then(|client| Ok(MMClient { client: client }))
    }

    fn url(&self, url: &str) -> ClientResult<String> {
        let mut lookup_url = String::new();
        lookup_url.push_str(url);
        lookup_url.push(if url.contains('?') { '&' } else { '?' });
        lookup_url.push_str("platform-slug");
        lookup_url.push_str("=");
        lookup_url.push_str("partnerplayer");

        self.client
            .url(lookup_url.as_str())
            .or_else(|err| {
                match err {
                    MMCError::ResourceNotFound => {}
                    MMCError::NotAuthorized => {}
                    _ => {
                        error!("Failed to query {} due to {}", url, err);
                    }
                };

                Err(err)
            })
            .map_err(ClientError::API)
    }

    fn show(&self, id: &str) -> ClientResult<String> {
        self.client
            .show(id, Some(vec![("platform-slug", "partnerplayer")]))
            .or_else(|err| {
                match err {
                    MMCError::ResourceNotFound => {}
                    MMCError::NotAuthorized => {}
                    _ => {
                        error!("Failed to query show {} due to {}", id, err);
                    }
                };

                Err(err)
            })
            .map_err(ClientError::API)
    }

    fn all_shows(&self) -> ClientResult<String> {
        self.client
            .shows(vec![
                ("page-size", "50"),
                ("platform-slug", "partnerplayer"),
            ])
            .or_else(|err| {
                error!("Failed to query all shows due to {}", err);
                Err(err)
            })
            .map_err(ClientError::API)
    }

    fn changes(&self, since: &str) -> ClientResult<String> {
        self.client
            .changelog(vec![("since", since)])
            .or_else(|err| {
                error!("Failed to query changelog from {} due to {}", since, err);
                Err(err)
            })
            .map_err(ClientError::API)
    }
}
<|endoftext|>


<commit_before>use proc_macro2::{Ident, Span};
use std::cmp::Ordering;
use std::fmt::{self, Display};
use std::hash::{Hash, Hasher};

#[cfg(feature = "parsing")]
use crate::lookahead;

/// A Rust lifetime: `'a`.
///
/// Lifetime names must conform to the following rules:
///
/// - Must start with an apostrophe.
/// - Must not consist of just an apostrophe: `'`.
/// - Character after the apostrophe must be `_` or a Unicode code point with
///   the XID_Start property.
/// - All following characters must be Unicode code points with the XID_Continue
///   property.
///
/// *This type is available only if Syn is built with the `"derive"` or `"full"`
/// feature.*
pub struct Lifetime {
    pub apostrophe: Span,
    pub ident: Ident,
}

impl Lifetime {
    /// # Panics
    ///
    /// Panics if the lifetime does not conform to the bulleted rules above.
    ///
    /// # Invocation
    ///
    /// ```
    /// # use proc_macro2::Span;
    /// # use syn::Lifetime;
    /// #
    /// # fn f() -> Lifetime {
    /// Lifetime::new("'a", Span::call_site())
    /// # }
    /// ```
    pub fn new(symbol: &str, span: Span) -> Self {
        if !symbol.starts_with('\'') {
            panic!(
                "lifetime name must start with apostrophe as in \"'a\", got {:?}",
                symbol
            );
        }

        if symbol == "'" {
            panic!("lifetime name must not be empty");
        }

        if !crate::ident::xid_ok(&symbol[1..]) {
            panic!("{:?} is not a valid lifetime name", symbol);
        }

        Lifetime {
            apostrophe: span,
            ident: Ident::new(&symbol[1..], span),
        }
    }
}

impl Display for Lifetime {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        "'".fmt(formatter)?;
        self.ident.fmt(formatter)
    }
}

impl Clone for Lifetime {
    fn clone(&self) -> Self {
        Lifetime {
            apostrophe: self.apostrophe,
            ident: self.ident.clone(),
        }
    }
}

impl PartialEq for Lifetime {
    fn eq(&self, other: &Lifetime) -> bool {
        self.ident.eq(&other.ident)
    }
}

impl Eq for Lifetime {}

impl PartialOrd for Lifetime {
    fn partial_cmp(&self, other: &Lifetime) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for Lifetime {
    fn cmp(&self, other: &Lifetime) -> Ordering {
        self.ident.cmp(&other.ident)
    }
}

impl Hash for Lifetime {
    fn hash<H: Hasher>(&self, h: &mut H) {
        self.ident.hash(h)
    }
}

#[cfg(feature = "parsing")]
#[doc(hidden)]
#[allow(non_snake_case)]
pub fn Lifetime(marker: lookahead::TokenMarker) -> Lifetime {
    match marker {}
}

#[cfg(feature = "parsing")]
pub mod parsing {
    use super::*;
    use crate::parse::{Parse, ParseStream, Result};

    impl Parse for Lifetime {
        fn parse(input: ParseStream) -> Result<Self> {
            input.step(|cursor| {
                cursor
                    .lifetime()
                    .ok_or_else(|| cursor.error("expected lifetime"))
            })
        }
    }
}

#[cfg(feature = "printing")]
mod printing {
    use super::*;
    use proc_macro2::{Punct, Spacing, TokenStream};
    use quote::{ToTokens, TokenStreamExt};

    impl ToTokens for Lifetime {
        fn to_tokens(&self, tokens: &mut TokenStream) {
            let mut apostrophe = Punct::new('\'', Spacing::Joint);
            apostrophe.set_span(self.apostrophe);
            tokens.append(apostrophe);
            self.ident.to_tokens(tokens);
        }
    }
}
<commit_msg>Fix incorrect doc comment about Lifetime being conditional<commit_after>use proc_macro2::{Ident, Span};
use std::cmp::Ordering;
use std::fmt::{self, Display};
use std::hash::{Hash, Hasher};

#[cfg(feature = "parsing")]
use crate::lookahead;

/// A Rust lifetime: `'a`.
///
/// Lifetime names must conform to the following rules:
///
/// - Must start with an apostrophe.
/// - Must not consist of just an apostrophe: `'`.
/// - Character after the apostrophe must be `_` or a Unicode code point with
///   the XID_Start property.
/// - All following characters must be Unicode code points with the XID_Continue
///   property.
pub struct Lifetime {
    pub apostrophe: Span,
    pub ident: Ident,
}

impl Lifetime {
    /// # Panics
    ///
    /// Panics if the lifetime does not conform to the bulleted rules above.
    ///
    /// # Invocation
    ///
    /// ```
    /// # use proc_macro2::Span;
    /// # use syn::Lifetime;
    /// #
    /// # fn f() -> Lifetime {
    /// Lifetime::new("'a", Span::call_site())
    /// # }
    /// ```
    pub fn new(symbol: &str, span: Span) -> Self {
        if !symbol.starts_with('\'') {
            panic!(
                "lifetime name must start with apostrophe as in \"'a\", got {:?}",
                symbol
            );
        }

        if symbol == "'" {
            panic!("lifetime name must not be empty");
        }

        if !crate::ident::xid_ok(&symbol[1..]) {
            panic!("{:?} is not a valid lifetime name", symbol);
        }

        Lifetime {
            apostrophe: span,
            ident: Ident::new(&symbol[1..], span),
        }
    }
}

impl Display for Lifetime {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        "'".fmt(formatter)?;
        self.ident.fmt(formatter)
    }
}

impl Clone for Lifetime {
    fn clone(&self) -> Self {
        Lifetime {
            apostrophe: self.apostrophe,
            ident: self.ident.clone(),
        }
    }
}

impl PartialEq for Lifetime {
    fn eq(&self, other: &Lifetime) -> bool {
        self.ident.eq(&other.ident)
    }
}

impl Eq for Lifetime {}

impl PartialOrd for Lifetime {
    fn partial_cmp(&self, other: &Lifetime) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for Lifetime {
    fn cmp(&self, other: &Lifetime) -> Ordering {
        self.ident.cmp(&other.ident)
    }
}

impl Hash for Lifetime {
    fn hash<H: Hasher>(&self, h: &mut H) {
        self.ident.hash(h)
    }
}

#[cfg(feature = "parsing")]
#[doc(hidden)]
#[allow(non_snake_case)]
pub fn Lifetime(marker: lookahead::TokenMarker) -> Lifetime {
    match marker {}
}

#[cfg(feature = "parsing")]
pub mod parsing {
    use super::*;
    use crate::parse::{Parse, ParseStream, Result};

    impl Parse for Lifetime {
        fn parse(input: ParseStream) -> Result<Self> {
            input.step(|cursor| {
                cursor
                    .lifetime()
                    .ok_or_else(|| cursor.error("expected lifetime"))
            })
        }
    }
}

#[cfg(feature = "printing")]
mod printing {
    use super::*;
    use proc_macro2::{Punct, Spacing, TokenStream};
    use quote::{ToTokens, TokenStreamExt};

    impl ToTokens for Lifetime {
        fn to_tokens(&self, tokens: &mut TokenStream) {
            let mut apostrophe = Punct::new('\'', Spacing::Joint);
            apostrophe.set_span(self.apostrophe);
            tokens.append(apostrophe);
            self.ident.to_tokens(tokens);
        }
    }
}
<|endoftext|>