<commit_before>//! goto :: Flexible Working Directory Shortcuts
//!
//! Copyright (c) 2016 by William R. Fraser

extern crate docopt;
extern crate rustc_serialize;
extern crate toml;

use std::collections::btree_map::*;
use std::env;
use std::fs::File;
use std::io::{self, Read, Write};
use std::path::{Path, PathBuf};
use docopt::Docopt;

// 79 columns:
// ----------------------------------------------------------------------------
const USAGE: &'static str = r#"
Usage:
    goto [<name>]
    goto (--help | --version)

Configuration is stored in ~/.goto.toml, with the following format:
    [global]
    name = "/some/path"             # 'goto name' takes you here
    othername = "~/some/other/path" # $HOME expansion will happen

    ["/somewhere/specific"]         # Only in effect when in this location:
    "*" = "default/under/specific"  # With no arguments, this is used.
    "name" = "somewhere/else"       # Overshadows the one in [global]

Relative paths under a context header are resolved relative to the path in that
header. In the above example, when your current directory is under
/somewhere/specific, running 'goto name' takes you to
/somewhere/specific/somewhere/else.

goto is meant to be used as the argument to your shell's 'eval' builtin, like:
    function goto() {
        eval $(/usr/local/bin/goto $*)  # or wherever the 'goto' binary is
    }
"#;

#[derive(Debug, RustcDecodable)]
struct Args {
    arg_name: Option<String>,
}

fn config_path() -> io::Result<PathBuf> {
    match env::home_dir() {
        Some(home) => Ok(home.join(".goto.toml")),
        None => Err(io::Error::new(io::ErrorKind::Other, "unable to determine home directory")),
    }
}

fn read_config() -> io::Result<toml::Table> {
    let mut config_text = String::new();
    let mut file = try!(File::open(try!(config_path())));
    try!(file.read_to_string(&mut config_text));
    let mut parser = toml::Parser::new(&config_text);
    let config = match parser.parse() {
        Some(v) => v,
        None => {
            let mut msg = String::from("failed to parse TOML:");
            for err in &parser.errors {
                let linecol_lo = parser.to_linecol(err.lo);
                let linecol_hi = parser.to_linecol(err.hi);
                msg.push_str(&format!("\n\t{} at {}:{} to {}:{}",
                        err.desc,
                        linecol_lo.0 + 1,
                        linecol_lo.1 + 1,
                        linecol_hi.0 + 1,
                        linecol_hi.1 + 1));
            }
            return Err(io::Error::new(io::ErrorKind::Other, msg));
        }
    };

    Ok(config)
}

type PathMapping = BTreeMap<String, PathBuf>;

#[derive(Debug)]
struct Configuration {
    global: PathMapping,
    contexts: BTreeMap<PathBuf, PathMapping>,
}

fn parse_toml_as_path(t: toml::Value, cwd: Option<&Path>) -> Result<PathBuf, String> {
    if let toml::Value::String(s) = t {
        let path: PathBuf = if s.starts_with("~/") {
            env::home_dir().unwrap().join(&Path::new(&s[2..]))
        } else if s.starts_with("/") {
            PathBuf::from(s)
        } else if cwd.is_some() {
            cwd.unwrap().join(Path::new(&s))
        } else {
            return Err(format!("expected a path starting with \"/\" or \"~/\""));
        };
        Ok(path)
    } else {
        Err(format!("type error: expected a string, not {}", t.type_str()))
    }
}

fn process_config(config_toml: toml::Table) -> Result<Configuration, String> {
    let mut config = Configuration {
        global: PathMapping::new(),
        contexts: BTreeMap::new(),
    };

    for (k, v) in config_toml.into_iter() {
        if let toml::Value::Table(t) = v {
            if k == "global" {
                for (name, path) in t.into_iter() {
                    match parse_toml_as_path(path, None) {
                        Ok(path) => { config.global.insert(name, path); },
                        Err(msg) => { return Err(format!("error at global.{}: {}", name, msg)); },
                    }
                }
            } else {
                let context_path = match parse_toml_as_path(toml::Value::String(k), None) {
                    Ok(path) => path,
                    Err(msg) => { return Err(format!("error: {}", msg)); }
                };

                let mut context_map = PathMapping::new();

                for (name, path) in t.into_iter() {
                    let mapped_path: PathBuf = match parse_toml_as_path(path, Some(&context_path)) {
                        Ok(path) => path,
                        Err(msg) => {
                            return Err(format!("error at {:?}.{}: {}", context_path, name, msg));
                        }
                    };

                    context_map.insert(name, mapped_path);
                }

                config.contexts.insert(context_path, context_map);
            }
        } else {
            return Err(format!("type error at {}: expected a table, not {}", k, v.type_str()));
        }
    }

    Ok(config)
}

fn main() {
    let args: Args = Docopt::new(USAGE)
                            .and_then(|d| {
                                d.version(Some(format!("goto {}", env!("CARGO_PKG_VERSION"))))
                                 .decode()
                            })
                            .unwrap_or_else(|e| {
                                writeln!(io::stderr(), "{}", e).unwrap();
                                ::std::process::exit(if e.fatal() { 1 } else { 0 });
                            });

    let name = args.arg_name.unwrap_or("*".to_owned());

    let cwd = PathBuf::from(env::current_dir().unwrap_or_else(|e| {
        panic!("unable to get current working directory: {}", e);
    }));

    let config_toml = read_config().map_err(|e| {
        panic!("failed to read configuration ~/.goto.toml: {}", e);
    }).unwrap();

    let config = process_config(config_toml).map_err(|msg| {
        panic!("invalid configuration in ~/.goto.toml: {}", msg);
    }).unwrap();

    let mut matched = false;
    let mut context_paths_by_len: Vec<&PathBuf> = config.contexts.keys().collect();
    context_paths_by_len.sort_by_key(|p| p.as_os_str().len());
    for context_path in context_paths_by_len.iter().rev() {
        if cwd.starts_with(context_path) {
            let map = config.contexts.get(*context_path).unwrap();
            if let Some(path) = map.get(&name) {
                println!("pushd {:?}", path);
                matched = true;
                break;
            }
        }
    }

    if !matched {
        if let Some(path) = config.global.get(&name) {
            println!("pushd {:?}", path);
            matched = true;
        }
    }

    if !matched {
        writeln!(io::stderr(), "not sure where to go").unwrap();
    }
}
<commit_msg>also allow top-level entries not under [global] header<commit_after>//! goto :: Flexible Working Directory Shortcuts
//!
//! Copyright (c) 2016 by William R. Fraser

extern crate docopt;
extern crate rustc_serialize;
extern crate toml;

use std::collections::btree_map::*;
use std::env;
use std::fs::File;
use std::io::{self, Read, Write};
use std::path::{Path, PathBuf};
use docopt::Docopt;

// 79 columns:
// ----------------------------------------------------------------------------
const USAGE: &'static str = r#"
Usage:
    goto [<name>]
    goto (--help | --version)

Configuration is stored in ~/.goto.toml, with the following format:
    [global]
    name = "/some/path"             # 'goto name' takes you here
    othername = "~/some/other/path" # $HOME expansion will happen

    ["/somewhere/specific"]         # Only in effect when in this location:
    "*" = "default/under/specific"  # With no arguments, this is used.
    "name" = "somewhere/else"       # Overshadows the one in [global]

Relative paths under a context header are resolved relative to the path in that
header. In the above example, when your current directory is under
/somewhere/specific, running 'goto name' takes you to
/somewhere/specific/somewhere/else.

goto is meant to be used as the argument to your shell's 'eval' builtin, like:
    function goto() {
        eval $(/usr/local/bin/goto $*)  # or wherever the 'goto' binary is
    }
"#;

#[derive(Debug, RustcDecodable)]
struct Args {
    arg_name: Option<String>,
}

fn config_path() -> io::Result<PathBuf> {
    match env::home_dir() {
        Some(home) => Ok(home.join(".goto.toml")),
        None => Err(io::Error::new(io::ErrorKind::Other, "unable to determine home directory")),
    }
}

fn read_config() -> io::Result<toml::Table> {
    let mut config_text = String::new();
    let mut file = try!(File::open(try!(config_path())));
    try!(file.read_to_string(&mut config_text));
    let mut parser = toml::Parser::new(&config_text);
    let config = match parser.parse() {
        Some(v) => v,
        None => {
            let mut msg = String::from("failed to parse TOML:");
            for err in &parser.errors {
                let linecol_lo = parser.to_linecol(err.lo);
                let linecol_hi = parser.to_linecol(err.hi);
                msg.push_str(&format!("\n\t{} at {}:{} to {}:{}",
                        err.desc,
                        linecol_lo.0 + 1,
                        linecol_lo.1 + 1,
                        linecol_hi.0 + 1,
                        linecol_hi.1 + 1));
            }
            return Err(io::Error::new(io::ErrorKind::Other, msg));
        }
    };

    Ok(config)
}

type PathMapping = BTreeMap<String, PathBuf>;

#[derive(Debug)]
struct Configuration {
    global: PathMapping,
    contexts: BTreeMap<PathBuf, PathMapping>,
}

fn parse_toml_as_path(t: &toml::Value, cwd: Option<&Path>) -> Result<PathBuf, String> {
    if let &toml::Value::String(ref s) = t {
        let path: PathBuf = if s.starts_with("~/") {
            env::home_dir().unwrap().join(&Path::new(&s[2..]))
        } else if s.starts_with("/") {
            PathBuf::from(s)
        } else if cwd.is_some() {
            cwd.unwrap().join(Path::new(&s))
        } else {
            return Err(format!("expected a path starting with \"/\" or \"~/\""));
        };
        Ok(path)
    } else {
        Err(format!("type error: expected a string, not {}", t.type_str()))
    }
}

fn process_config(config_toml: toml::Table) -> Result<Configuration, String> {
    let mut config = Configuration {
        global: PathMapping::new(),
        contexts: BTreeMap::new(),
    };

    for (k, v) in config_toml.into_iter() {
        if let toml::Value::Table(t) = v {
            if k == "global" {
                for (name, path) in t.into_iter() {
                    match parse_toml_as_path(&path, None) {
                        Ok(path) => { config.global.insert(name, path); },
                        Err(msg) => { return Err(format!("error at global.{}: {}", name, msg)); },
                    }
                }
            } else {
                let context_path = match parse_toml_as_path(&toml::Value::String(k), None) {
                    Ok(path) => path,
                    Err(msg) => { return Err(format!("error: {}", msg)); }
                };

                let mut context_map = PathMapping::new();

                for (name, path) in t.into_iter() {
                    let mapped_path: PathBuf = match parse_toml_as_path(&path, Some(&context_path)) {
                        Ok(path) => path,
                        Err(msg) => {
                            return Err(format!("error at {:?}.{}: {}", context_path, name, msg));
                        }
                    };

                    context_map.insert(name, mapped_path);
                }

                config.contexts.insert(context_path, context_map);
            }
        } else {
            // Attempt to parse any top-level entry (i.e. not under any header) as a path, and if
            // it works, consider it to be a global entry.
            match parse_toml_as_path(&v, None) {
                Ok(path) => { config.global.insert(k, path); },
                Err(msg) => {
                    return Err(format!(
                        "error at {}: expected a table or a path string, not {} ({})",
                         k, v.type_str(), msg));
                },
            }
        }
    }

    Ok(config)
}

fn main() {
    let args: Args = Docopt::new(USAGE)
                            .and_then(|d| {
                                d.version(Some(format!("goto {}", env!("CARGO_PKG_VERSION"))))
                                 .decode()
                            })
                            .unwrap_or_else(|e| {
                                writeln!(io::stderr(), "{}", e).unwrap();
                                ::std::process::exit(if e.fatal() { 1 } else { 0 });
                            });

    let name = args.arg_name.unwrap_or("*".to_owned());

    let cwd = PathBuf::from(env::current_dir().unwrap_or_else(|e| {
        panic!("unable to get current working directory: {}", e);
    }));

    let config_toml = read_config().map_err(|e| {
        panic!("failed to read configuration ~/.goto.toml: {}", e);
    }).unwrap();

    let config = process_config(config_toml).map_err(|msg| {
        panic!("invalid configuration in ~/.goto.toml: {}", msg);
    }).unwrap();

    let mut matched = false;
    let mut context_paths_by_len: Vec<&PathBuf> = config.contexts.keys().collect();
    context_paths_by_len.sort_by_key(|p| p.as_os_str().len());
    for context_path in context_paths_by_len.iter().rev() {
        if cwd.starts_with(context_path) {
            let map = config.contexts.get(*context_path).unwrap();
            if let Some(path) = map.get(&name) {
                println!("pushd {:?}", path);
                matched = true;
                break;
            }
        }
    }

    if !matched {
        if let Some(path) = config.global.get(&name) {
            println!("pushd {:?}", path);
            matched = true;
        }
    }

    if !matched {
        writeln!(io::stderr(), "not sure where to go").unwrap();
    }
}
<|endoftext|>


<commit_before>mod chipate;

#[macro_use]
extern crate log;
extern crate env_logger;

fn main() {
    env_logger::init().unwrap();

    let mut chip = chipate::Chipate::new();
    chip.init();
    chip.load_program("pong");

    chip.setup_testing_memory();
    chip.emulate_cycle();
    chip.decode_opcode();

    loop {
        chip.emulate_cycle();
        if false {
            chip.draw_screen();
        }

        chip.set_keys();
    }
}
<commit_msg>Updated main program to use real rom file named PONG<commit_after>mod chipate;

#[macro_use]
extern crate log;
extern crate env_logger;

fn main() {
    env_logger::init().unwrap();

    let mut chip = chipate::Chipate::new();
    chip.init();
    chip.load_program("PONG");

    chip.setup_testing_memory();
    chip.emulate_cycle();

    loop {
        chip.emulate_cycle();
        if false {
            chip.draw_screen();
        }

        chip.set_keys();
    }
}
<|endoftext|>


<commit_before>extern crate rustc_serialize;
use rustc_serialize::json;

use std::fmt;
use std::env;

use std::fs::File;
use std::io::Read;

use std::thread;
use std::process::Command;

struct WorkConf {
    project: String,
    operate: String,
    head:    String,
}

impl fmt::Debug for WorkConf {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "WorkConf:{}\"project\": {}, \"operate\": {}, \"head\": {}{}",
            "{", self.project, self.operate, self.head, "}")
    }
}

// Automatically generate `RustcDecodable` and `RustcEncodable` trait
// implementations
#[derive(RustcDecodable, RustcEncodable)]
struct DeployConf {
    user: String,
    path: String,
    hosts_conf: Vec<String>,
}

fn usage(argv_0: &String) {
    println!("-----USAGE----");
    println!("{} project deploy             deploy project with latest <head>.", argv_0);
    println!("{} project rollback <head>    rollback with <head>.", argv_0);
    std::process::exit(0);
}

static DEBUG: bool = true;

fn main() {
    let args: Vec<_> = env::args().collect();
    let argc = args.len();

    if argc < 3 {
        usage(&args[0]);
    }

    // 处理命令行参数
    let project = args[1].to_string();
    let operate = args[2].to_string();
    if ("deploy".to_string() != operate && "rollback".to_string() != operate) || ("rollback".to_string() == operate && argc < 4) {
        usage(&args[0]);
    }

    let head = if argc > 3 { args[3].to_string() } else { "".to_string() };

    let work_conf = WorkConf{
        project: project,
        operate: operate,
        head:    head,
    };
    if DEBUG { println!("{:?}", work_conf); }

    let config_filename = format!("./conf/{}.json", work_conf.project);
    if DEBUG { println!("config_filename: {}", config_filename); }

    let mut data = String::new();
    let mut f = File::open(config_filename)
        .ok()
        .expect("Can NOT open file.");
    f.read_to_string(&mut data)
        .ok()
        .expect("Can NOT read cofing file.");
    if DEBUG { println!("deploy_conf: {}", data)}
    let deploy_conf: DeployConf = json::decode(&data)
        .ok()
        .expect("Can NOT parse JSON config data.");
    if DEBUG { println!("JSON: {}", json::encode(&deploy_conf).unwrap()); }

    let mut hosts_conf  = Vec::new();
    let hosts_len = deploy_conf.hosts_conf.len();
    for index in 0 .. hosts_len {
        hosts_conf.push(deploy_conf.hosts_conf[index].clone());
    }
    let handles: Vec<_> = hosts_conf.into_iter().map(|host| {
        let user = deploy_conf.user.clone();
        let path = deploy_conf.path.clone();
        let operate = work_conf.operate.clone();
        let head    = work_conf.head.clone();
        thread::spawn(move || {
            let cmd = if "deploy".to_string() == operate {
                format!("ssh {}@{} \"cd {} && git pull && git log -1 | awk '{{if (\\$1 ~/commit/) {{print \\$2}}}}' 2>&1\"",
                user, host, path)
            } else {
                format!("ssh {}@{} \"cd {} && git reset --hard {} && git log -1 | awk '{{if (\\$1 ~/commit/) {{print \\$2}}}}' 2>&1\"",
                user, host, path, head)
            };
            if DEBUG { println!("CMD: {}", cmd); }
            let output = Command::new("sh")
                .arg("-c")
                .arg(cmd)
                .output()
                .unwrap_or_else(|e| { panic!("failed to execute process: {}", e) });

            if DEBUG { println!("status: {}", output.status); }
            println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
            println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
        })
    }).collect();

    for h in handles {
        h.join().unwrap();
    }
}
<commit_msg>1. 关掉debug; 2. 美化输出.<commit_after>extern crate rustc_serialize;
use rustc_serialize::json;

use std::fmt;
use std::env;

use std::fs::File;
use std::io::Read;

use std::thread;
use std::process::Command;

struct WorkConf {
    project: String,
    operate: String,
    head:    String,
}

impl fmt::Debug for WorkConf {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "WorkConf:{}\"project\": {}, \"operate\": {}, \"head\": {}{}",
            "{", self.project, self.operate, self.head, "}")
    }
}

// Automatically generate `RustcDecodable` and `RustcEncodable` trait
// implementations
#[derive(RustcDecodable, RustcEncodable)]
struct DeployConf {
    user: String,
    path: String,
    hosts_conf: Vec<String>,
}

fn usage(argv_0: &String) {
    println!("-----USAGE----");
    println!("{} project deploy             deploy project with latest <head>.", argv_0);
    println!("{} project rollback <head>    rollback with <head>.", argv_0);
    std::process::exit(0);
}

static DEBUG: bool = false;

fn main() {
    let args: Vec<_> = env::args().collect();
    let argc = args.len();

    if argc < 3 {
        usage(&args[0]);
    }

    // 处理命令行参数
    let project = args[1].to_string();
    let operate = args[2].to_string();
    if ("deploy".to_string() != operate && "rollback".to_string() != operate) || ("rollback".to_string() == operate && argc < 4) {
        usage(&args[0]);
    }

    let head = if argc > 3 { args[3].to_string() } else { "".to_string() };

    let work_conf = WorkConf{
        project: project,
        operate: operate,
        head:    head,
    };
    if DEBUG { println!("{:?}", work_conf); }

    let config_filename = format!("./conf/{}.json", work_conf.project);
    if DEBUG { println!("config_filename: {}", config_filename); }

    let mut data = String::new();
    let mut f = File::open(config_filename)
        .ok()
        .expect("Can NOT open file.");
    f.read_to_string(&mut data)
        .ok()
        .expect("Can NOT read cofing file.");
    if DEBUG { println!("deploy_conf: {}", data)}
    let deploy_conf: DeployConf = json::decode(&data)
        .ok()
        .expect("Can NOT parse JSON config data.");
    if DEBUG { println!("JSON: {}", json::encode(&deploy_conf).unwrap()); }

    let mut hosts_conf  = Vec::new();
    let hosts_len = deploy_conf.hosts_conf.len();
    for index in 0 .. hosts_len {
        hosts_conf.push(deploy_conf.hosts_conf[index].clone());
    }

    println!("          ===== {} ======\n", work_conf.project);

    let handles: Vec<_> = hosts_conf.into_iter().map(|host| {
        let user = deploy_conf.user.clone();
        let path = deploy_conf.path.clone();
        let operate = work_conf.operate.clone();
        let head    = work_conf.head.clone();
        thread::spawn(move || {
            let cmd = if "deploy".to_string() == operate {
                format!("ssh {}@{} \"cd {} && git pull && git log -1 | awk '{{if (\\$1 ~/commit/) {{print \\$2}}}}' 2>&1\"",
                user, host, path)
            } else {
                format!("ssh {}@{} \"cd {} && git reset --hard {} && git log -1 | awk '{{if (\\$1 ~/commit/) {{print \\$2}}}}' 2>&1\"",
                user, host, path, head)
            };
            if DEBUG { println!("CMD: {}", cmd); }
            let output = Command::new("sh")
                .arg("-c")
                .arg(cmd)
                .output()
                .unwrap_or_else(|e| { panic!("failed to execute process: {}", e) });

            println!("--- Start {} for {}@{} ---", operate, user, host);
            if DEBUG { println!("status: {}", output.status); }
            println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
            println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
            println!("--- End {} for {}@{} ---\n", operate, user, host);
        })
    }).collect();

    for h in handles {
        h.join().unwrap();
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate lazy_static;
extern crate regex;
extern crate hyper;
extern crate hubcaps;
extern crate afterparty;
extern crate serde_json;
extern crate serde;
extern crate rustc_serialize;
extern crate toml;
#[macro_use]
extern crate log;
extern crate env_logger;

use std::collections::{HashMap, HashSet};
use std::fs::File;
use std::env;
use std::io::Read;

use hyper::{Client, Server, Url};
use hyper::header::{Headers, Authorization, Bearer, UserAgent};
use hubcaps::{Credentials, Github};
use afterparty::*;
use serde_json::Value;
use regex::Regex;
use toml::decode_str;

#[derive(RustcEncodable, RustcDecodable)]
struct RepoData {
    pub qa_user: String,
    pub qa_flags: Vec<String>,
}

#[derive(RustcEncodable, RustcDecodable)]
struct ConfigData {
    pub api_root: String,
    pub repos: HashMap<String, RepoData>,
}

fn read_config_file(file_name: &str) -> ConfigData {
    let mut config_file = File::open(file_name).expect("Couldn't open config file");
    let mut str = String::new();
    config_file.read_to_string(&mut str).expect("Couldn't read config file");
    let toml: Option<ConfigData> = decode_str(&str);
    match toml {
        Some(cfg) => cfg,
        _ => panic!("Couldn't decode config file"),
    }
}

#[test]
fn it_reads_toml() {
    let config_data = read_config_file("test-data/test.toml");
    assert_eq!(config_data.api_root, "github.ibm.com/api/v3");
    let steve = config_data.repos.get("lkgrele/steve").unwrap();
    assert_eq!(steve.qa_user, "leif");
    assert_eq!(steve.qa_flags, ["qa", "test"]);
    let raul = config_data.repos.get("raul/robot").unwrap();
    assert_eq!(raul.qa_user, "raul");
    assert_eq!(raul.qa_flags, ["qa"]);
}

fn main() {
    env_logger::init().unwrap();

    let mut hub = Hub::new();
    hub.handle("pull_request", |delivery: &Delivery| {
        match delivery.payload {
            Event::PullRequest { ref pull_request, ref repository, .. } => {
                if pull_request.merged {
                    handle_pr(&pull_request.commits_url, &repository.full_name)
                }
            }
            _ => info!("Recived a request that wasn't a pull-request webhook"),
        }
    });

    match Server::http("0.0.0.0:0") {
        Err(err) => error!("ERROR: failed to start server, the error was: {}", err),
        Ok(server) => {
            match server.handle(hub) {
                Err(err) => error!("ERROR starting handler, the error was: {}", err),
                Ok(server) => info!("Successfully started server"),
            }
        }
    }
}

fn handle_pr(commits_url: &str, repository: &str) {
    let config_data = read_config_file(".steve");
    let repo_config = config_data.repos.get(&repository.to_owned());
    // let auth_token = env::var_os("STEVE_GITHUB_TOKEN")
    //     .expect("Missing github token!")
    //     .map(|tok| tok.into_string())
    //     .unwrap();
    match env::var_os("STEVE_GITHUB_TOKEN") {
        None => {
            error!("Missing github token, STEVE_GITHUB_TOKEN is not defined!");
            panic!()
        }
        Some(tok) => {
            match tok.into_string() {
                Err(_) => {
                    error!("Github token contains illegal characters!");
                    panic!()
                }
                Ok(auth_token) => {
                    match repo_config {
                        Some(config) => {
                            update_issues(&commits_url,
                                          &auth_token,
                                          &config,
                                          &config_data.api_root,
                                          &repository)
                        }
                        None => info!("No config found for repo {}, skipping", repository),
                    }
                }
            }
        }
    }
}

fn update_issues(commits_url: &str,
                 auth_token: &str,
                 config: &RepoData,
                 api_root: &str,
                 repository: &str) {
    let client = Client::new();
    let mut headers = Headers::new();
    headers.set(Authorization(Bearer { token: auth_token.to_owned() }));
    headers.set(UserAgent("steve".to_owned()));
    let commits = client.get(commits_url).headers(headers).send().unwrap();
    assert_eq!(commits.status, hyper::Ok); //replace with actual error handling

    match serde_json::from_reader(commits) {
        Ok(json) => {
            match parse_commit_data(&json) {
                Ok(messages) => {
                    let mut issues = HashSet::new();
                    for message in messages {
                        get_issues(&mut issues, &message)
                    }

                    for issue in issues {
                        update_issue(&client, auth_token, issue, config, &repository, api_root);
                    }
                }
                Err(err) => println!("{:?}", err), //should log error
            }
        }
        Err(err) => println!("{:?}", err), //should log error
    }
}

#[test]
fn it_parses_commit_data() {
    let file = File::open("test-data/test-messages.json").expect("Couldn't open message file");
    let json = serde_json::from_reader(file);
    assert!(json.is_ok());
    let json = json.unwrap();
    let messages = parse_commit_data(&json);
    assert!(messages.is_ok());
    let messages = messages.unwrap();
    assert_eq!(messages[0], "message1");
    assert_eq!(messages[1], "swordfish");
}

fn parse_commit_data<'a>(json: &'a Value) -> Result<Vec<&'a str>, &str> {
    let arr = try!(json.as_array().ok_or("expected array as top level object"));
    arr.iter()
        .map(|obj| {
            obj.pointer("/commit/message")
                .ok_or("missing message field")
                .and_then(|m| m.as_string().ok_or("message field has wrong type"))
        })
        .collect()
}

fn update_issue(client: &Client,
                auth_token: &str,
                issue_num: u64,
                config: &RepoData,
                repository: &str,
                api_root: &str) {
    let github = Github::host(api_root,
                              "steve",
                              client,
                              Credentials::Token(auth_token.to_owned()));
    let (owner, repo_name) = match repository.split("/").collect::<Vec<_>>() {
        strs => (strs[0], strs[1]),
    };
    let _ = github.repo(repo_name, owner)
        .issue(issue_num)
        .labels()
        .add(config.qa_flags.iter().map(String::as_str).collect());
    let mut headers = Headers::new();
    headers.set(Authorization(Bearer { token: auth_token.to_owned() }));
    headers.set(UserAgent("steve".to_owned()));
    let update_json = format!("{{\"assignee\":\"{}\"}}", config.qa_user);
    let url = Url::parse(&format!("{}/repos/{}/issues/{}", api_root, repository, issue_num))
        .expect("bad url");
    let res = client.patch(url)
        .body(&update_json)
        .headers(headers.to_owned())
        .send()
        .unwrap();
    assert_eq!(res.status, hyper::Ok);
}

#[test]
fn it_finds_issue_numbers() {
    let message = r"This is a commit message
        needs qa: #72 should get caught
        needs QA #333 should also get caught
        but #44 won't, and needs QA: #33 #45
        only gets #33. Duplicates are discarded:
        needs qa #72";
    let mut expected: HashSet<u64> = HashSet::new();
    expected.insert(72);
    expected.insert(333);
    expected.insert(33);
    let mut numbers: HashSet<u64> = HashSet::new();
    get_issues(&mut numbers, message);
    assert_eq!(numbers, expected);
}

fn get_issues(numbers: &mut HashSet<u64>, message: &str) {
    lazy_static! {
        static ref RE: Regex = Regex::new(r"(?i)needs\sqa:?\s#(\d+)").unwrap();
    }
    for num in RE.captures_iter(message).filter_map(|cap| cap.at(1)) {
        numbers.insert(num.parse::<u64>().unwrap());
    }
}
<commit_msg>Replace println! with error! in update_issues()<commit_after>#[macro_use]
extern crate lazy_static;
extern crate regex;
extern crate hyper;
extern crate hubcaps;
extern crate afterparty;
extern crate serde_json;
extern crate serde;
extern crate rustc_serialize;
extern crate toml;
#[macro_use]
extern crate log;
extern crate env_logger;

use std::collections::{HashMap, HashSet};
use std::fs::File;
use std::env;
use std::io::Read;

use hyper::{Client, Server, Url};
use hyper::header::{Headers, Authorization, Bearer, UserAgent};
use hubcaps::{Credentials, Github};
use afterparty::*;
use serde_json::Value;
use regex::Regex;
use toml::decode_str;

#[derive(RustcEncodable, RustcDecodable)]
struct RepoData {
    pub qa_user: String,
    pub qa_flags: Vec<String>,
}

#[derive(RustcEncodable, RustcDecodable)]
struct ConfigData {
    pub api_root: String,
    pub repos: HashMap<String, RepoData>,
}

fn read_config_file(file_name: &str) -> ConfigData {
    let mut config_file = File::open(file_name).expect("Couldn't open config file");
    let mut str = String::new();
    config_file.read_to_string(&mut str).expect("Couldn't read config file");
    let toml: Option<ConfigData> = decode_str(&str);
    match toml {
        Some(cfg) => cfg,
        _ => panic!("Couldn't decode config file"),
    }
}

#[test]
fn it_reads_toml() {
    let config_data = read_config_file("test-data/test.toml");
    assert_eq!(config_data.api_root, "github.ibm.com/api/v3");
    let steve = config_data.repos.get("lkgrele/steve").unwrap();
    assert_eq!(steve.qa_user, "leif");
    assert_eq!(steve.qa_flags, ["qa", "test"]);
    let raul = config_data.repos.get("raul/robot").unwrap();
    assert_eq!(raul.qa_user, "raul");
    assert_eq!(raul.qa_flags, ["qa"]);
}

fn main() {
    env_logger::init().unwrap();

    let mut hub = Hub::new();
    hub.handle("pull_request", |delivery: &Delivery| {
        match delivery.payload {
            Event::PullRequest { ref pull_request, ref repository, .. } => {
                if pull_request.merged {
                    handle_pr(&pull_request.commits_url, &repository.full_name)
                }
            }
            _ => info!("Recived a request that wasn't a pull-request webhook"),
        }
    });

    match Server::http("0.0.0.0:0") {
        Err(err) => error!("ERROR: failed to start server, the error was: {}", err),
        Ok(server) => {
            match server.handle(hub) {
                Err(err) => error!("ERROR starting handler, the error was: {}", err),
                Ok(server) => info!("Successfully started server"),
            }
        }
    }
}

fn handle_pr(commits_url: &str, repository: &str) {
    let config_data = read_config_file(".steve");
    let repo_config = config_data.repos.get(&repository.to_owned());
    // let auth_token = env::var_os("STEVE_GITHUB_TOKEN")
    //     .expect("Missing github token!")
    //     .map(|tok| tok.into_string())
    //     .unwrap();
    match env::var_os("STEVE_GITHUB_TOKEN") {
        None => {
            error!("Missing github token, STEVE_GITHUB_TOKEN is not defined!");
            panic!()
        }
        Some(tok) => {
            match tok.into_string() {
                Err(_) => {
                    error!("Github token contains illegal characters!");
                    panic!()
                }
                Ok(auth_token) => {
                    match repo_config {
                        Some(config) => {
                            update_issues(&commits_url,
                                          &auth_token,
                                          &config,
                                          &config_data.api_root,
                                          &repository)
                        }
                        None => info!("No config found for repo {}, skipping", repository),
                    }
                }
            }
        }
    }
}

fn update_issues(commits_url: &str,
                 auth_token: &str,
                 config: &RepoData,
                 api_root: &str,
                 repository: &str) {
    let client = Client::new();
    let mut headers = Headers::new();
    headers.set(Authorization(Bearer { token: auth_token.to_owned() }));
    headers.set(UserAgent("steve".to_owned()));
    let commits = client.get(commits_url).headers(headers).send().unwrap();
    assert_eq!(commits.status, hyper::Ok); //replace with actual error handling

    match serde_json::from_reader(commits) {
        Ok(json) => {
            match parse_commit_data(&json) {
                Ok(messages) => {
                    let mut issues = HashSet::new();
                    for message in messages {
                        get_issues(&mut issues, &message)
                    }

                    for issue in issues {
                        update_issue(&client, auth_token, issue, config, &repository, api_root);
                    }
                }
                Err(err) => error!("{:?}", err),
            }
        }
        Err(err) => error!("{:?}", err),
    }
}

#[test]
fn it_parses_commit_data() {
    let file = File::open("test-data/test-messages.json").expect("Couldn't open message file");
    let json = serde_json::from_reader(file);
    assert!(json.is_ok());
    let json = json.unwrap();
    let messages = parse_commit_data(&json);
    assert!(messages.is_ok());
    let messages = messages.unwrap();
    assert_eq!(messages[0], "message1");
    assert_eq!(messages[1], "swordfish");
}

fn parse_commit_data<'a>(json: &'a Value) -> Result<Vec<&'a str>, &str> {
    let arr = try!(json.as_array().ok_or("expected array as top level object"));
    arr.iter()
        .map(|obj| {
            obj.pointer("/commit/message")
                .ok_or("missing message field")
                .and_then(|m| m.as_string().ok_or("message field has wrong type"))
        })
        .collect()
}

fn update_issue(client: &Client,
                auth_token: &str,
                issue_num: u64,
                config: &RepoData,
                repository: &str,
                api_root: &str) {
    let github = Github::host(api_root,
                              "steve",
                              client,
                              Credentials::Token(auth_token.to_owned()));
    let (owner, repo_name) = match repository.split("/").collect::<Vec<_>>() {
        strs => (strs[0], strs[1]),
    };
    let _ = github.repo(repo_name, owner)
        .issue(issue_num)
        .labels()
        .add(config.qa_flags.iter().map(String::as_str).collect());
    let mut headers = Headers::new();
    headers.set(Authorization(Bearer { token: auth_token.to_owned() }));
    headers.set(UserAgent("steve".to_owned()));
    let update_json = format!("{{\"assignee\":\"{}\"}}", config.qa_user);
    let url = Url::parse(&format!("{}/repos/{}/issues/{}", api_root, repository, issue_num))
        .expect("bad url");
    let res = client.patch(url)
        .body(&update_json)
        .headers(headers.to_owned())
        .send()
        .unwrap();
    assert_eq!(res.status, hyper::Ok);
}

#[test]
fn it_finds_issue_numbers() {
    let message = r"This is a commit message
        needs qa: #72 should get caught
        needs QA #333 should also get caught
        but #44 won't, and needs QA: #33 #45
        only gets #33. Duplicates are discarded:
        needs qa #72";
    let mut expected: HashSet<u64> = HashSet::new();
    expected.insert(72);
    expected.insert(333);
    expected.insert(33);
    let mut numbers: HashSet<u64> = HashSet::new();
    get_issues(&mut numbers, message);
    assert_eq!(numbers, expected);
}

fn get_issues(numbers: &mut HashSet<u64>, message: &str) {
    lazy_static! {
        static ref RE: Regex = Regex::new(r"(?i)needs\sqa:?\s#(\d+)").unwrap();
    }
    for num in RE.captures_iter(message).filter_map(|cap| cap.at(1)) {
        numbers.insert(num.parse::<u64>().unwrap());
    }
}
<|endoftext|>


<commit_before>extern crate docopt;
extern crate rustc_serialize;
extern crate semver;
extern crate toml;

use std::collections::BTreeMap;
use std::env;
use std::error::Error;
use std::fmt;
use std::fs::{self, OpenOptions, File};
use std::io::{Read, Write, Seek, SeekFrom};
use std::path::{Path, PathBuf};
use std::process;

type Dependency = (String, toml::Value);

static USAGE: &'static str = "
Usage:
    cargo add [options] <dep>...
    cargo add [options] <dep> (--version | --path | --git) <source>
    cargo add -h | --help

Options:
    --manifest-path PATH    Path to the manifest to add a dependency to.
    -h --help               Show this help page.

Add a dependency to the crate's Cargo.toml file. If no source is specified, the
source will be set to a wild-card version dependency from the source's default
crate registry.

If a version is specified, it will be validated as a valid semantic version
requirement. No other kind of source will be validated, and the registry will
not be polled to guarantee that a crate meeting that version requirement
actually exists.
";

#[derive(Debug, RustcDecodable)]
//Docopts input args.
struct Args {
    arg_dep: Vec<String>,
    arg_source: String,
    flag_manifest_path: Option<String>,
    flag_version: bool,
    flag_git: bool,
    flag_path: bool,
}

#[derive(Debug)]
// Catch-all error for misconfigured crates.
pub struct ManifestError;

impl Error for ManifestError {
    fn description(&self) -> &str {
        "Your Cargo.toml is either missing or incorrectly structured."
    }
}

impl fmt::Display for ManifestError {
    fn fmt(&self, format: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        format.write_str(self.description())
    }
}

// Parse command-line input into key/value data that can be added to the TOML.
fn parse_dependency(dep: &String, args: &Args) -> Result<Dependency, Box<Error>> {
    if args.flag_version { parse_semver(&args.arg_source) }
    else if args.flag_git { parse_git(&args.arg_source) }
    else if args.flag_path { parse_path(&args.arg_source) }
    else { Ok(toml::Value::String(String::from("*"))) }
    .map(|data| (dep.clone(), data))
}

// Parse (and validate) a version requirement to the correct TOML data.
fn parse_semver(version: &String) -> Result<toml::Value, Box<Error>> {
    try!(semver::VersionReq::parse(version));
    Ok(toml::Value::String(version.clone()))
}

// Parse a git source to the correct TOML data.
fn parse_git(repo: &String) -> Result<toml::Value, Box<Error>> {
    let mut dep = BTreeMap::new();
    dep.insert(String::from("git"), toml::Value::String(repo.clone()));
    Ok(toml::Value::Table(dep))
}

// Parse a path to the correct TOML data.
fn parse_path(path: &String) -> Result<toml::Value, Box<Error>> {
    let mut dep = BTreeMap::new();
    dep.insert(String::from("path"), toml::Value::String(path.clone()));
    Ok(toml::Value::Table(dep))
}

// If a manifest is specified, return that one, otherise perform a manifest search starting from
// the current directory.
fn find_manifest(specified: Option<&String>) -> Result<PathBuf, Box<Error>> {
    specified.map(PathBuf::from).ok_or(())
    .or_else(|_| env::current_dir().map_err(From::from)
                 .and_then(|ref dir| manifest_search(dir).map_err(From::from)))
}

// Search for Cargo.toml in this directory and recursively up the tree until one is found.
#[allow(unconditional_recursion)] //Incorrect lint; recursion is conditional.
fn manifest_search(dir: &Path) -> Result<PathBuf, ManifestError> {
    let manifest = dir.join("Cargo.toml");
    fs::metadata(&manifest).map(|_| manifest)
    .or(dir.parent().ok_or(ManifestError).and_then(manifest_search))
}

// Read all the contents of a file & parse as a TOML table.
fn read_as_toml(file: &mut File) -> Result<BTreeMap<String, toml::Value>, Box<Error>> {
    let mut data = String::new();
    try!(file.read_to_string(&mut data));
    let mut parser = toml::Parser::new(&data);
    parser.parse().ok_or(parser.errors.pop()).map_err(Option::unwrap).map_err(From::from)
}

// Overwrite a file with TOML data.
fn write_from_toml(file: &mut File, mut toml: BTreeMap<String, toml::Value>)
        -> Result<(), Box<Error>> {
    try!(file.seek(SeekFrom::Start(0)));
    let (proj_header, proj_data) =
        try!(toml.remove("package").map(|data| ("package", data))
             .or_else(|| toml.remove("project").map(|data| ("project", data)))
             .ok_or(ManifestError));
    write!(file, "[{}]\n{}{}", proj_header, proj_data,
           toml::Value::Table(toml)).map_err(From::from)
}

// Add a dependency to a Cargo.toml.
fn insert_dependency(manifest: &mut BTreeMap<String, toml::Value>, (name, data): Dependency)
        -> Result<(), ManifestError> {
    match manifest.entry(String::from("dependencies"))
    .or_insert(toml::Value::Table(BTreeMap::new())) {
        &mut toml::Value::Table(ref mut deps) => {
            deps.insert(name, data);
            Ok(())
        }
        _ => Err(ManifestError)
    }
}

fn main() {
    //1. Generate an Args struct from the docopts string.
    docopt::Docopt::new(USAGE).and_then(|d| d.decode::<Args>()).map_err(From::from)
    //2. Generate a list of dependencies & a manifest file handle from the Args.
    //[Args -> (File, Vec<Dependency>)]
    .and_then(|args| {
        args.arg_dep.iter()
        .map(|dep| parse_dependency(dep, &args))
        .collect::<Result<Vec<_>, _>>()
        .and_then(|deps| {
            find_manifest(args.flag_manifest_path.as_ref())
            .and_then(|path| OpenOptions::new().read(true).write(true)
                                               .open(path).map_err(From::from))
            .map(|manifest| (manifest, deps))
        })
    })
    //3. Add the dependencies to the manifest. [(File, Vec<Dependency>) -> ()]
    .and_then(|(mut manifest, deps)| {
        read_as_toml(&mut manifest)
        .and_then(|mut toml_data| deps.into_iter()
                                 .map(|dep| insert_dependency(&mut toml_data, dep))
                                 .collect::<Result<Vec<_>, _>>()
                                 .map_err(From::from)
                                 .map(|_| toml_data))
        .and_then(|toml_data| write_from_toml(&mut manifest, toml_data))
    })
    //4. Print error message and return error code on failure.
    .or_else(|err| -> Result<(), Box<Error>> {
        println!("Could not add dependency.\n\nERROR: {}", err);
        process::exit(1);
    }).ok();
}
<commit_msg>Handle docopt errors more correctly.<commit_after>extern crate docopt;
extern crate rustc_serialize;
extern crate semver;
extern crate toml;

use std::collections::BTreeMap;
use std::env;
use std::error::Error;
use std::fmt;
use std::fs::{self, OpenOptions, File};
use std::io::{Read, Write, Seek, SeekFrom};
use std::path::{Path, PathBuf};
use std::process;

type Dependency = (String, toml::Value);

static USAGE: &'static str = "
Usage:
    cargo add [options] <dep>...
    cargo add [options] <dep> (--version | --path | --git) <source>
    cargo add -h | --help

Options:
    --manifest-path PATH    Path to the manifest to add a dependency to.
    -h --help               Show this help page.

Add a dependency to the crate's Cargo.toml file. If no source is specified, the
source will be set to a wild-card version dependency from the source's default
crate registry.

If a version is specified, it will be validated as a valid semantic version
requirement. No other kind of source will be validated, and the registry will
not be polled to guarantee that a crate meeting that version requirement
actually exists.
";

#[derive(Debug, RustcDecodable)]
//Docopts input args.
struct Args {
    arg_dep: Vec<String>,
    arg_source: String,
    flag_manifest_path: Option<String>,
    flag_version: bool,
    flag_git: bool,
    flag_path: bool,
}

#[derive(Debug)]
// Catch-all error for misconfigured crates.
pub struct ManifestError;

impl Error for ManifestError {
    fn description(&self) -> &str {
        "Your Cargo.toml is either missing or incorrectly structured."
    }
}

impl fmt::Display for ManifestError {
    fn fmt(&self, format: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        format.write_str(self.description())
    }
}

// Parse command-line input into key/value data that can be added to the TOML.
fn parse_dependency(dep: &String, args: &Args) -> Result<Dependency, Box<Error>> {
    if args.flag_version { parse_semver(&args.arg_source) }
    else if args.flag_git { parse_git(&args.arg_source) }
    else if args.flag_path { parse_path(&args.arg_source) }
    else { Ok(toml::Value::String(String::from("*"))) }
    .map(|data| (dep.clone(), data))
}

// Parse (and validate) a version requirement to the correct TOML data.
fn parse_semver(version: &String) -> Result<toml::Value, Box<Error>> {
    try!(semver::VersionReq::parse(version));
    Ok(toml::Value::String(version.clone()))
}

// Parse a git source to the correct TOML data.
fn parse_git(repo: &String) -> Result<toml::Value, Box<Error>> {
    let mut dep = BTreeMap::new();
    dep.insert(String::from("git"), toml::Value::String(repo.clone()));
    Ok(toml::Value::Table(dep))
}

// Parse a path to the correct TOML data.
fn parse_path(path: &String) -> Result<toml::Value, Box<Error>> {
    let mut dep = BTreeMap::new();
    dep.insert(String::from("path"), toml::Value::String(path.clone()));
    Ok(toml::Value::Table(dep))
}

// If a manifest is specified, return that one, otherise perform a manifest search starting from
// the current directory.
fn find_manifest(specified: Option<&String>) -> Result<PathBuf, Box<Error>> {
    specified.map(PathBuf::from).ok_or(())
    .or_else(|_| env::current_dir().map_err(From::from)
                 .and_then(|ref dir| manifest_search(dir).map_err(From::from)))
}

// Search for Cargo.toml in this directory and recursively up the tree until one is found.
#[allow(unconditional_recursion)] //Incorrect lint; recursion is conditional.
fn manifest_search(dir: &Path) -> Result<PathBuf, ManifestError> {
    let manifest = dir.join("Cargo.toml");
    fs::metadata(&manifest).map(|_| manifest)
    .or(dir.parent().ok_or(ManifestError).and_then(manifest_search))
}

// Read all the contents of a file & parse as a TOML table.
fn read_as_toml(file: &mut File) -> Result<BTreeMap<String, toml::Value>, Box<Error>> {
    let mut data = String::new();
    try!(file.read_to_string(&mut data));
    let mut parser = toml::Parser::new(&data);
    parser.parse().ok_or(parser.errors.pop()).map_err(Option::unwrap).map_err(From::from)
}

// Overwrite a file with TOML data.
fn write_from_toml(file: &mut File, mut toml: BTreeMap<String, toml::Value>)
        -> Result<(), Box<Error>> {
    try!(file.seek(SeekFrom::Start(0)));
    let (proj_header, proj_data) =
        try!(toml.remove("package").map(|data| ("package", data))
             .or_else(|| toml.remove("project").map(|data| ("project", data)))
             .ok_or(ManifestError));
    write!(file, "[{}]\n{}{}", proj_header, proj_data,
           toml::Value::Table(toml)).map_err(From::from)
}

// Add a dependency to a Cargo.toml.
fn insert_dependency(manifest: &mut BTreeMap<String, toml::Value>, (name, data): Dependency)
        -> Result<(), ManifestError> {
    match manifest.entry(String::from("dependencies"))
    .or_insert(toml::Value::Table(BTreeMap::new())) {
        &mut toml::Value::Table(ref mut deps) => {
            deps.insert(name, data);
            Ok(())
        }
        _ => Err(ManifestError)
    }
}

fn main() {
    //1. Generate an Args struct from the docopts string.
    docopt::Docopt::new(USAGE).and_then(|d| d.decode::<Args>()).or_else(|err| err.exit())
    //2. Generate a list of dependencies & a manifest file handle from the Args.
    //[Args -> (File, Vec<Dependency>)]
    .and_then(|args| {
        args.arg_dep.iter()
        .map(|dep| parse_dependency(dep, &args))
        .collect::<Result<Vec<_>, _>>()
        .and_then(|deps| {
            find_manifest(args.flag_manifest_path.as_ref())
            .and_then(|path| OpenOptions::new().read(true).write(true)
                                               .open(path).map_err(From::from))
            .map(|manifest| (manifest, deps))
        })
    })
    //3. Add the dependencies to the manifest. [(File, Vec<Dependency>) -> ()]
    .and_then(|(mut manifest, deps)| {
        read_as_toml(&mut manifest)
        .and_then(|mut toml_data| deps.into_iter()
                                 .map(|dep| insert_dependency(&mut toml_data, dep))
                                 .collect::<Result<Vec<_>, _>>()
                                 .map_err(From::from)
                                 .map(|_| toml_data))
        .and_then(|toml_data| write_from_toml(&mut manifest, toml_data))
    })
    //4. Print error message and return error code on failure.
    .or_else(|err| -> Result<(), Box<Error>> {
        println!("Could not add dependency.\n\nERROR: {:?}", err);
        process::exit(1);
    }).ok();
}
<|endoftext|>


<commit_before>extern crate regex;
extern crate irc;
extern crate xdg_basedir;
mod github;

use regex::Regex;
use irc::client::prelude::*;
use xdg_basedir::*;

fn parse_post(post: &str) -> Option<String> {
    let postregex = Regex::new(r"(\S+)/(\S+)#(\d+)").unwrap();
    let cap = match postregex.captures(post) {
        Some(x) => x,
        None => { return None; }
    };
    let user = cap.at(1).unwrap();
    let repo = cap.at(2).unwrap();
    let number = cap.at(3).unwrap();
    return github::get_display_text(user, repo, number).ok();
}

fn main() {
    let mut configpath = get_config_home().unwrap();
    configpath.push("idsbot/config.json");

    let config = Config::load(configpath).unwrap();
    let nickname = config.nickname.clone().expect("Must provide nickname.");

    let server = IrcServer::from_config(config).unwrap();
    server.identify().unwrap();

    for message in server.iter() {
        let message = message.unwrap();
        if &message.command[..] == "PRIVMSG" {
            let from = message.get_source_nickname().unwrap().to_owned();
            let to = message.args[0].to_owned();
            let content = message.suffix.unwrap();

            if to == nickname && from == "ids1024" {
                let mut words = content.split_whitespace();
                let command = words.next().unwrap_or("");
                let parameter = words.next().unwrap_or("");
                match command {
                    "join" => {
                        server.send_join(parameter).unwrap();
                    },
                    "part" => {
                        server.send(Message::new(None, "PART", Some(vec![parameter]), None)).unwrap();
                    },
                    "quit" => {
                        server.send_quit("").unwrap();
                    }
                    _ => {},
                }
            }

            match parse_post(&content) {
                Some(x) => {
                    for line in x.lines() {
                        server.send_privmsg(&to, &line).unwrap();
                    }
                },
                None => {}
            };
        }
    }

}
<commit_msg>Print all messages from server<commit_after>extern crate regex;
extern crate irc;
extern crate xdg_basedir;
mod github;

use regex::Regex;
use irc::client::prelude::*;
use xdg_basedir::*;

fn parse_post(post: &str) -> Option<String> {
    let postregex = Regex::new(r"(\S+)/(\S+)#(\d+)").unwrap();
    let cap = match postregex.captures(post) {
        Some(x) => x,
        None => { return None; }
    };
    let user = cap.at(1).unwrap();
    let repo = cap.at(2).unwrap();
    let number = cap.at(3).unwrap();
    return github::get_display_text(user, repo, number).ok();
}

fn main() {
    let mut configpath = get_config_home().unwrap();
    configpath.push("idsbot/config.json");

    let config = Config::load(configpath).unwrap();
    let nickname = config.nickname.clone().expect("Must provide nickname.");

    let server = IrcServer::from_config(config).unwrap();
    server.identify().unwrap();

    for message in server.iter() {
        let message = message.unwrap();
        print!("{}", message.into_string());
        if &message.command[..] == "PRIVMSG" {
            let from = message.get_source_nickname().unwrap().to_owned();
            let to = message.args[0].to_owned();
            let content = message.suffix.unwrap();

            if to == nickname && from == "ids1024" {
                let mut words = content.split_whitespace();
                let command = words.next().unwrap_or("");
                let parameter = words.next().unwrap_or("");
                match command {
                    "join" => {
                        server.send_join(parameter).unwrap();
                    },
                    "part" => {
                        server.send(Message::new(None, "PART", Some(vec![parameter]), None)).unwrap();
                    },
                    "quit" => {
                        server.send_quit("").unwrap();
                    }
                    _ => {},
                }
            }

            match parse_post(&content) {
                Some(x) => {
                    for line in x.lines() {
                        server.send_privmsg(&to, &line).unwrap();
                    }
                },
                None => {}
            };
        }
    }

}
<|endoftext|>


<commit_before>extern crate rand;
extern crate walkdir;

use walkdir::WalkDir;
use rand::{Rng, thread_rng};
use std::env;
use std::fs::{self, OpenOptions};
use std::io::prelude::*;
use std::io::{Error, SeekFrom};
use std::cmp;
use std::path::Path;

fn main() {
    let mut recursive: bool = false;
    let mut file: String = String::from("");

    for argument in env::args().skip(1) {
        if argument.to_lowercase() == "--help" || argument.to_lowercase() == "-help" {
            help();
            return;
        } else if argument.to_lowercase() == "--v" || argument.to_lowercase() == "-v" ||
           argument.to_lowercase() == "-version" ||
           argument.to_lowercase() == "--version" {
            version();
            return;
        } else if argument.to_lowercase() == "-r" {
            recursive = true;
        } else {
            file = argument;
            break;
        }
    }
    if file == "" {
        help();
        return;
    }
    file = get_path(&file);

    if recursive {
        for entry in WalkDir::new(file) {
            let entry = entry.unwrap();
            trumpify_file(entry.path());
        }
    } else {
        trumpify_file(file)
    }
}

fn get_path2(file: &str) -> Result<String, Error> {
    let path = try!(fs::canonicalize(file));
    Ok(path.display().to_string())
}

fn get_path(file: &str) -> String {
    get_path2(file).unwrap_or(file.to_owned())
}

fn version() {
    println!("\x1b[32mVersion:");
    println!("\x1b[0m    trump-0.1.0");
    println!("\x1b[32mWebsite:");
    println!("\x1b[0m    https://github.com/x13machine/trump/");
    println!("\x1b[32mCreator:");
    println!("\x1b[0m    <Googolplexking/x13machine> https://googolplex.ninja/");
}
fn help() {
    println!("\x1b[32mHelp:");
    println!("\x1b[0m    trump --help");
    println!("\x1b[32mVersion:");
    println!("\x1b[0m    trump -v");
    println!("\x1b[32mFiles:");
    println!("\x1b[0m    trump <File>");
    println!("\x1b[32mDirectories:");
    println!("\x1b[0m    trump -r <Directory>");
}

fn trumpify_file<P: AsRef<Path>>(path: P) {
    let path = path.as_ref();

    let name = match path.file_name() {
        Some(name) => name.to_string_lossy(),
        None => return,
    };
    let capitalized_name = format!("{}{}",
                                   &name[0..1].to_uppercase(),
                                   &name[1..name.len()].to_lowercase());
    // Ansi Color Codes!!!
    println!("\x1b[32mMaking Great Again:\x1b[0m {:?}", path);

    let text = format!("Make {} Great Again!", capitalized_name);

    let mut file = match OpenOptions::new().read(true).write(true).open(path) {
        Ok(file) => file,
        Err(_) => return,
    };

    let mut data = Vec::new();

    if let Err(_) = file.read_to_end(&mut data) {
        return;
    }

    if data.is_empty() {
        return;
    }

    trumpify_bytes(&mut data, text.as_bytes());

    let _ = file.seek(SeekFrom::Start(0));
    let _ = file.write_all(&data);
}

fn trumpify_bytes(data: &mut [u8], replace_with: &[u8]) {
    let replace_len = replace_with.len();
    let loops = cmp::max(data.len() / replace_len / 2, 1);
    let mut rng = thread_rng();

    for _ in 0..loops {
        let offset = rng.gen_range(0, data.len());
        data[offset..offset + replace_len].clone_from_slice(replace_with);
    }
}
<commit_msg>Make sure we never index out of bounds<commit_after>extern crate rand;
extern crate walkdir;

use walkdir::WalkDir;
use rand::{Rng, thread_rng};
use std::env;
use std::fs::{self, OpenOptions};
use std::io::prelude::*;
use std::io::{Error, SeekFrom};
use std::cmp;
use std::path::Path;

fn main() {
    let mut recursive: bool = false;
    let mut file: String = String::from("");

    for argument in env::args().skip(1) {
        if argument.to_lowercase() == "--help" || argument.to_lowercase() == "-help" {
            help();
            return;
        } else if argument.to_lowercase() == "--v" || argument.to_lowercase() == "-v" ||
           argument.to_lowercase() == "-version" ||
           argument.to_lowercase() == "--version" {
            version();
            return;
        } else if argument.to_lowercase() == "-r" {
            recursive = true;
        } else {
            file = argument;
            break;
        }
    }
    if file == "" {
        help();
        return;
    }
    file = get_path(&file);

    if recursive {
        for entry in WalkDir::new(file) {
            let entry = entry.unwrap();
            trumpify_file(entry.path());
        }
    } else {
        trumpify_file(file)
    }
}

fn get_path2(file: &str) -> Result<String, Error> {
    let path = try!(fs::canonicalize(file));
    Ok(path.display().to_string())
}

fn get_path(file: &str) -> String {
    get_path2(file).unwrap_or(file.to_owned())
}

fn version() {
    println!("\x1b[32mVersion:");
    println!("\x1b[0m    trump-0.1.0");
    println!("\x1b[32mWebsite:");
    println!("\x1b[0m    https://github.com/x13machine/trump/");
    println!("\x1b[32mCreator:");
    println!("\x1b[0m    <Googolplexking/x13machine> https://googolplex.ninja/");
}
fn help() {
    println!("\x1b[32mHelp:");
    println!("\x1b[0m    trump --help");
    println!("\x1b[32mVersion:");
    println!("\x1b[0m    trump -v");
    println!("\x1b[32mFiles:");
    println!("\x1b[0m    trump <File>");
    println!("\x1b[32mDirectories:");
    println!("\x1b[0m    trump -r <Directory>");
}

fn trumpify_file<P: AsRef<Path>>(path: P) {
    let path = path.as_ref();

    let name = match path.file_name() {
        Some(name) => name.to_string_lossy(),
        None => return,
    };
    let capitalized_name = format!("{}{}",
                                   &name[0..1].to_uppercase(),
                                   &name[1..name.len()].to_lowercase());
    // Ansi Color Codes!!!
    println!("\x1b[32mMaking Great Again:\x1b[0m {:?}", path);

    let text = format!("Make {} Great Again!", capitalized_name);

    let mut file = match OpenOptions::new().read(true).write(true).open(path) {
        Ok(file) => file,
        Err(_) => return,
    };

    let mut data = Vec::new();

    if let Err(_) = file.read_to_end(&mut data) {
        return;
    }

    if data.is_empty() {
        return;
    }

    trumpify_bytes(&mut data, text.as_bytes());

    let _ = file.seek(SeekFrom::Start(0));
    let _ = file.write_all(&data);
}

fn trumpify_bytes(data: &mut [u8], replace_with: &[u8]) {
    let replace_len = replace_with.len();
    let loops = cmp::max(data.len() / replace_len / 2, 1);
    let mut rng = thread_rng();

    for _ in 0..loops {
        let offset = rng.gen_range(0, data.len());
        let to = cmp::min(offset + replace_len, data.len());
        let mut slice = &mut data[offset..to];
        let len = slice.len();
        slice.clone_from_slice(&replace_with[..len]);
    }
}
<|endoftext|>


<commit_before>use std::env;
use std::collections::HashMap;
#[allow(dead_code)]
struct Operator {
    precedence: i32,
    func: Box<Fn(f32, f32) -> f32>
}

fn get_operators () -> HashMap<char, Operator> {
    let multiply = Operator {
        precedence: 3,
        func: Box::new(move |a: f32, b:f32| a * b)
    };

    let divide = Operator {
        precedence: 3,
        func: Box::new(move |a: f32, b:f32| a / b)
    };

    let add = Operator {
        precedence: 4,
        func: Box::new(move |a: f32, b:f32| a + b)
    };

    let subtract = Operator {
        precedence: 4,
        func: Box::new(move |a: f32, b:f32| a - b)
    };

    let mut operators = HashMap::new();

    operators.insert('*', multiply);
    operators.insert('/', divide);
    operators.insert('+', add);
    operators.insert('-', subtract);

    operators
}

fn main () {
    let args: Vec<_> = env::args().collect();
    let tokens: Vec<&str> = args[1].split(" ").collect();
    let mut operator_stack: Vec<char> = vec![];
    let mut output: Vec<String> = vec![];
    let operators = get_operators();

    for t in tokens {
        let maybeInt = t.parse::<i32>();

        match maybeInt {
            Ok(a) => output.push(t.to_string()),
            Err(a) => {
                if operator_stack.len() > 0 {
                    let mut operator_char = t.to_string().pop().unwrap();
                    let o1 = operators.get(&operator_char).unwrap();
                    operator_char = operator_stack[operator_stack.len()];
                    let o2 = operators.get(&operator_char).unwrap();

                    if o1.precedence <= o2.precedence {
                        let o2_char = operator_stack.pop().unwrap().to_string();
                        output.push(o2_char);
                    }

                }
            }
        }
    }
}
<commit_msg>Refactor slightly<commit_after>use std::env;
use std::collections::HashMap;
#[allow(dead_code)]
struct Operator {
    precedence: i32,
    func: Box<Fn(f32, f32) -> f32>
}

fn get_operators () -> HashMap<char, Operator> {
    let multiply = Operator {
        precedence: 3,
        func: Box::new(move |a: f32, b:f32| a * b)
    };

    let divide = Operator {
        precedence: 3,
        func: Box::new(move |a: f32, b:f32| a / b)
    };

    let add = Operator {
        precedence: 4,
        func: Box::new(move |a: f32, b:f32| a + b)
    };

    let subtract = Operator {
        precedence: 4,
        func: Box::new(move |a: f32, b:f32| a - b)
    };

    let mut operators = HashMap::new();

    operators.insert('*', multiply);
    operators.insert('/', divide);
    operators.insert('+', add);
    operators.insert('-', subtract);

    operators
}

fn main () {
    let args: Vec<_> = env::args().collect();
    let tokens: Vec<&str> = args[1].split(" ").collect();
    let mut operator_stack: Vec<char> = vec![];
    let mut output: Vec<String> = vec![];
    let operators = get_operators();

    for token in tokens {
        let maybe_int = token.parse::<i32>();

        match maybe_int {
            Ok(_) => output.push(token.to_string()),
            Err(_) => {
                let mut o1_char = token.to_string().pop().unwrap();
                let o1 = operators.get(&o1_char).unwrap();

                if !operator_stack.is_empty() {
                    o1_char = operator_stack[0];
                    let o2 = operators.get(&o1_char).unwrap();

                    if o1.precedence <= o2.precedence {
                        let o2_char = operator_stack.pop().unwrap().to_string();
                        output.push(o2_char);
                    } else {
                        operator_stack.push(o1_char);
                    }
                } else {
                    operator_stack.push(o1_char);
                }
            }
        }
    }

    println!("{:?}", output);
}
<|endoftext|>


<commit_before>#![feature(custom_derive)]

extern crate hyper;
extern crate rustc_serialize;
extern crate scoped_threadpool;

use std::fs;
use std::path::Path;
use std::fs::File;
use std::io::{Read, Write};
use std::collections::{HashSet, HashMap};

use hyper::client::{Client, Response, RequestBuilder};
use hyper::header::{Accept, Connection, qitem};
use hyper::mime::{Mime, TopLevel, SubLevel};

use rustc_serialize::json;

use scoped_threadpool::Pool;

struct MyClient {
    client : Client,
    app_id : String,
    app_key : String,
    cache_dir : String,
}

#[derive(Clone, Debug, RustcEncodeable, RustcDecodable)]
struct Line {
    id : String,
    name : String,
    modeName : String,
    routeSections : Vec<RouteSection>
}

#[derive(Clone, Debug, RustcEncodeable, RustcDecodable)]
struct RouteSection {
    name : String,
    direction : String,
    originator : String,
    destination : String,
    timetable : Option<TimeTable>,
}

#[derive(Clone, Debug, RustcEncodeable, RustcDecodable)]
struct Interval {
    stopId : String,
    timeToArrival: f64,
}

#[derive(Clone, Debug, RustcEncodeable, RustcDecodable)]
struct StationInterval {
    id : i64,
    intervals : Vec<Interval>
}

#[derive(Clone, Debug, RustcEncodeable, RustcDecodable)]
struct KnownJourney {
    intervalId : i64,
    hour : String,
    minute : String,
}

#[derive(Clone, Debug, RustcEncodeable, RustcDecodable)]
struct Schedule {
    name : String,
    knownJourneys : Vec<KnownJourney>,
}

#[derive(Clone, Debug, RustcEncodeable, RustcDecodable)]
struct TimeTable {
    stationIntervals : Vec<StationInterval>,
    schedules : Vec<Schedule>,
}

#[derive(Debug, RustcEncodeable, RustcDecodable)]
struct RoutesTimeTables {
    routes : Vec<TimeTable>,
}

#[derive(Debug, RustcEncodeable, RustcDecodable)]
struct TimeTableResponse {
    timetable : RoutesTimeTables,
}

impl MyClient {
    fn new() -> MyClient {
        let cachePath : &Path = Path::new("./cache");
        fs::create_dir(cachePath);
        return MyClient{
            client : Client::new(),
            app_id : String::new(),
            app_key : String::new(),
            cache_dir : String::from("./cache"),
        }
    }

    fn get(&mut self, endpoint : &str) -> String {
        match self.cache_get(endpoint) {
            Some(body) => body,
            None => self.remote_get(endpoint)
        }
    }

    fn remote_get(&mut self, endpoint : &str) -> String {
        let req_uri = format!("https://api.tfl.gov.uk{}?app_id={}&app_key={}", endpoint, self.app_id, self.app_key);
        let mut body = String::new();
        let mut resp = self.client.get(&req_uri)
            .header(Accept(vec![
                qitem(Mime(TopLevel::Application,
                    SubLevel::Ext("json".to_owned()), vec![])),
            ]))
            .send().unwrap();
        resp.read_to_string(&mut body).unwrap();
        self.cache_put(endpoint, body)
    }

    fn cache_fname(&mut self, endpoint : &str) -> String {
        let fname = String::from(endpoint);
        let fname0 = fname.replace("/", "_");
        self.cache_dir.clone() + "/" + &fname0
    }

    fn cache_put(&mut self, endpoint : &str, body : String) -> String {
        let mut f = File::create(self.cache_fname(endpoint)).unwrap();
        f.write_all(body.as_bytes());
        body
    }

    fn cache_get(&mut self, endpoint : &str) -> Option<String> {
        let mut body = String::new();
        match File::open(self.cache_fname(endpoint)) {
            Ok(ref mut f) => {
                f.read_to_string(&mut body);
                Some(body)
            },
            Err(_) => None,
        }
    }
}

fn get_lines(client : &mut MyClient) -> Vec<Line> {
    let body = client.get("/line/route");
    json::decode(&body).unwrap()
}

fn get_timetable(client : &mut MyClient, line_id : &str, originator: &str, destination : &str) -> Option<TimeTable> {
    let req_uri = format!("/line/{}/timetable/{}/to/{}", line_id, originator, destination);
    let body = client.get(&req_uri);
    match json::decode::<TimeTableResponse>(&body) {
        Ok(ttresp) => Some(ttresp.timetable.routes[0].clone()),
        Err(_) => None,
    }
}

fn route_section_id(line : &Line, section : &RouteSection) -> String {
    return line.id.clone() + " " + &section.originator + " to " + &section.destination;
}

fn main() {
    // Fetch data
    let mut client = MyClient::new();
    let mut lines = get_lines(&mut client);
    let mut pool = Pool::new(10);

    pool.scoped(|scope| {
        for line in &mut lines {
            scope.execute(move || {
                let mut client = MyClient::new();
                for route_section in &mut line.routeSections {
                    println!("Getting Timetable for Line: {}, Route Section: {} ...", line.name, route_section.name);
                    route_section.timetable = get_timetable(&mut client, &line.id, &route_section.originator, &route_section.destination);
                }
            });
        }
    });

    // Generate a report
    let mut line_ids : HashSet<String> = HashSet::new();
    let mut route_section_ids: HashSet<String> = HashSet::new();
    for line in &lines {
        println!("{}, Duplicate: {}", line.id, line_ids.contains(&line.id));
        for route_section in &line.routeSections {
            let id = route_section_id(&line, &route_section);
            println!("\t{}, Duplicate: {}", id, route_section_ids.contains(&id));
            route_section_ids.insert(id.clone());
        }
        line_ids.insert(line.id.clone());
    }

    // Generate CSV files from fetched data

}
<commit_msg>Began the work of writting out the csv files<commit_after>#![feature(custom_derive)]

extern crate hyper;
extern crate rustc_serialize;
extern crate scoped_threadpool;

use std::fs;
use std::sync::Arc;
use std::path::Path;
use std::fs::File;
use std::io::{Read, Write};
use std::collections::{HashSet, HashMap};

use hyper::client::{Client, Response, RequestBuilder};
use hyper::header::{Accept, Connection, qitem};
use hyper::mime::{Mime, TopLevel, SubLevel};

use rustc_serialize::json;

use scoped_threadpool::Pool;

#[derive(Clone)]
struct MyClient {
    client : Arc<Client>,
    app_id : String,
    app_key : String,
    cache_dir : String,
}

#[derive(Clone, Debug, RustcDecodable)]
struct Line {
    id : String,
    name : String,
    modeName : String,
    routeSections : Vec<RouteSection>
}

#[derive(Clone, Debug, RustcDecodable)]
struct RouteSection {
    name : String,
    direction : String,
    originator : String,
    destination : String,
    timetable : Option<TimeTable>,
}

#[derive(Clone, Debug, RustcDecodable)]
struct Interval {
    stopId : String,
    timeToArrival: f64,
}

#[derive(Clone, Debug, RustcDecodable)]
struct StationInterval {
    id : i64,
    intervals : Vec<Interval>
}

#[derive(Clone, Debug, RustcDecodable)]
struct KnownJourney {
    intervalId : i64,
    hour : String,
    minute : String,
}

#[derive(Clone, Debug, RustcDecodable)]
struct Schedule {
    name : String,
    knownJourneys : Vec<KnownJourney>,
}

#[derive(Clone, Debug, RustcDecodable)]
struct TimeTable {
    stationIntervals : Vec<StationInterval>,
    schedules : Vec<Schedule>,
}

#[derive(Clone, Debug, RustcDecodable)]
struct RoutesTimeTables {
    routes : Vec<TimeTable>,
}

#[derive(Debug, RustcEncodeable, RustcDecodable)]
struct TimeTableResponse {
    timetable : RoutesTimeTables,
}

impl MyClient {
    fn new() -> MyClient {
        let cachePath : &Path = Path::new("./cache");
        fs::create_dir(cachePath);
        return MyClient{
            client : Arc::new(Client::new()),
            app_id : String::new(),
            app_key : String::new(),
            cache_dir : String::from("./cache"),
        }
    }

    fn get(&self, endpoint : &str) -> String {
        match self.cache_get(endpoint) {
            Some(body) => body,
            None => self.remote_get(endpoint)
        }
    }

    fn remote_get(&self, endpoint : &str) -> String {
        let req_uri = format!("https://api.tfl.gov.uk{}?app_id={}&app_key={}", endpoint, self.app_id, self.app_key);
        let mut body = String::new();
        let mut resp = self.client.get(&req_uri)
            .header(Accept(vec![
                qitem(Mime(TopLevel::Application,
                    SubLevel::Ext("json".to_owned()), vec![])),
            ]))
            .send().unwrap();
        resp.read_to_string(&mut body).unwrap();
        self.cache_put(endpoint, body)
    }

    fn cache_fname(&self, endpoint : &str) -> String {
        let fname = String::from(endpoint);
        let fname0 = fname.replace("/", "_");
        self.cache_dir.clone() + "/" + &fname0
    }

    fn cache_put(&self, endpoint : &str, body : String) -> String {
        let mut f = File::create(self.cache_fname(endpoint)).unwrap();
        f.write_all(body.as_bytes());
        body
    }

    fn cache_get(&self, endpoint : &str) -> Option<String> {
        let mut body = String::new();
        match File::open(self.cache_fname(endpoint)) {
            Ok(ref mut f) => {
                f.read_to_string(&mut body);
                Some(body)
            },
            Err(_) => None,
        }
    }
}

fn get_lines(client : &MyClient) -> Vec<Line> {
    let body = client.get("/line/route");
    json::decode(&body).unwrap()
}

fn get_timetable(client : &MyClient, line_id : &str, originator: &str, destination : &str) -> Option<TimeTable> {
    let req_uri = format!("/line/{}/timetable/{}/to/{}", line_id, originator, destination);
    let body = client.get(&req_uri);
    match json::decode::<TimeTableResponse>(&body) {
        Ok(ttresp) =>  Some(ttresp.timetable.routes[0].clone()),
        Err(err) => {
            println!("Error decoding timetable {}", err);
            None
        },
    }
}

fn route_section_id(line : &Line, section : &RouteSection) -> String {
    return line.id.clone() + " " + &section.originator + " to " + &section.destination;
}

fn write_gtfs(line : &Vec<Line>) {
        let gtfsPath : &Path = Path::new("./gtfs");
        fs::create_dir(gtfsPath);
}

fn main() {
    // Fetch data
    let client = Arc::new(MyClient::new());
    let mut lines = get_lines(&client);
    let mut pool = Pool::new(10);

    pool.scoped(|scope| {
        for line in &mut lines {
            let client = client.clone();
            scope.execute(move || {
                for route_section in &mut line.routeSections {
                    println!("Getting Timetable for Line: {}, Route Section: {} ...", line.name, route_section.name);
                    route_section.timetable = get_timetable(&client, &line.id, &route_section.originator, &route_section.destination);
                }
            });
        }
    });

    // Generate a report
    let mut line_count = 0;
    let mut line_ids : HashSet<String> = HashSet::new();
    let mut route_section_count = 0;
    let mut route_section_ids: HashSet<String> = HashSet::new();
    let mut schedule_names: HashSet<String> = HashSet::new();
    for line in &lines {
        println!("{}, Duplicate: {}", line.id, line_ids.contains(&line.id));
        for route_section in &line.routeSections {
            let has_timetable = match route_section.timetable {
                Some(ref timetable) => {
                    for schedule in &timetable.schedules {
                        schedule_names.insert(schedule.name.clone());
                    }
                    true
                },
                None => false,
            };
            let id = route_section_id(&line, &route_section);
            println!("\t{}, Has Timetable: {}, Duplicate: {}", id, has_timetable, route_section_ids.contains(&id));
            route_section_ids.insert(id.clone());
            route_section_count += 1;
        }
        line_count += 1;
        line_ids.insert(line.id.clone());
    }
    println!("Duplicate Lines: {}, Duplicate Route Sections: {}", line_count-line_ids.len(), route_section_count-route_section_ids.len());

    println!("Schedule Names:");
    for schedule_name in &schedule_names {
        println!("\t{}", schedule_name);
    }

    // Generate CSV files from fetched data
    write_gtfs(&lines);
}
<|endoftext|>